一、RequestMapping注解
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Mapping
@Reflective({ControllerMappingReflectiveProcessor.class})
public @interface RequestMapping {String name() default "";@AliasFor("path")String[] value() default {};@AliasFor("value")String[] path() default {};RequestMethod[] method() default {};String[] params() default {};String[] headers() default {};String[] consumes() default {};String[] produces() default {};
}
RequestMapping注解是SpringMVC框架中的一个控制器映射注解,用于把请求映射到相应的处理方法上。也就是说它可以把指定URL的请求绑定到一个特定的方法或类上。从而实现对请求的处理和响应。
其定义中的:@Target({ElementType.TYPE, ElementType.METHOD}) 表明这个注解是可以出现在类或者是方法上的。
RequestMapping映射唯一性
如果我们的项目中有两个不同的处理器方法上使用了相同的映射路径,这会怎么样呢?
示例如下:
@Controller
public class UserController {@RequestMapping("/detail")public String toDetail() {return "detail";}
}
@Controller
public class ProductController {@RequestMapping("/detail")public String toDetail() {return "detail";}
}
这样子是会出问题的,因为当前端请求的路径是/detail的时候到底哪个处理器方法处理并响应?
在程序部署到Tomcat当中的时候会报错如下:
java.lang.IllegalStateException: Ambiguous mapping. Cannot map 'userController' method com.xiaoxie.controller.UserController#toDetail() to { [/detail]}: There is already 'productController' bean method com.xiaoxie.controller.ProductController#toDetail() mapped.
从而我们得出一个结论:在同一个webapp中,RequestMapping必须具有唯一性。对上面的错误我们如何解决?
有两种解决方案:
- 方案一:把方法上RequestMapping的映射路径修改为不一样
- 方案二:在类上也添加上@RequestMapping注解指定映射路径,以类上的这个作为命名空间,用来区分两个不同的映射
解决方案一:
调整两个Controller类如下:
@Controller
public class UserController {@RequestMapping("/user/detail")public String toDetail() {return "user/detail";}
}
@Controller
public class ProductController {@RequestMapping("/product/detail")public String toDetail() {return "product/detail";}
}
这样子的话两个处理类中的toDetial()方法实际上对应的是不同的映射。不会产生冲突
根据上面的两个返回的逻辑视图的名称,根据SpringMVC配置文件要求分别在指定位置建立对应的detail.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head><meta charset="UTF-8"><title>商品详情页</title>
</head>
<body><h1>商品详情</h1>
</body>
</html>
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head><meta charset="UTF-8"><title>用户详情页</title>
</head>
<body><h1>用户详情</h1>
</body>
</html>
解决方案二:
同时在类和方法上都加上@RequestMappping注解来进行路径映射。比如我们在类上映射的路径是/a,在方法上映射的路径是/b,那么完整的映射路径就是"/a/b"
在方案一中,如果我们UserController类中有很多映射处理方法,那么每个方法上都加上"/user",会比较麻烦,这个时候可以考虑把/user提到类上去注解。
@Controller
@RequestMapping("/user")
public class UserController {@RequestMapping("/detail")public String toDetail() {return "user/detail";}
}
@Controller
@RequestMapping("/product")
public class ProductController {@RequestMapping("/detail")public String toDetail() {return "product/detail";}
}
value属性
value属性匹配控制器
value属性是@RequestMapping注解的核心属性,它指定的是请求路径,也就是说通过这个请求路径与对应的控制器方法绑定到一起。
@AliasFor("path")
String[] value() default {};
从上面可以看到为了可读性,value属性与path属性是代表的同一个东西,只是我们只写value属性值是可以省略value这个名称,当我们path属性时则必须把属性名path写上。
另外,属性可配置的值是一个字符串数组,从这里可以看出来在SpringMVC当中多个不同的请求可以映射到同一个控制器的同一方法上。
@Controller
public class TestValueController {@RequestMapping({"/testValue1","/testValue2"})public String testValue() {return "testValue";}
}
value属性值是用来匹配请求路径的,它也支持模糊匹配
我们把这种模糊匹配称为Ant风格,路径中的通配符包含如下一些:
?:表示任意一个字符
*:表示0到n个任意字符
**:表示0到n个任意字符,并且路径是可以出现路径分隔符的
注意:** 通配符在使用时其左右是不可以出现除/以外的其它字符的
示例:
@RequestMapping("/a?b/testValeAnt")
public String testValueAnt() {return "testValueAnt";
}
新增高图模板文件:testValueAnt.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head><meta charset="UTF-8"><title>TestValueAnt</title>
</head>
<body><h1>RequestMapping中的value属性支持模糊匹配</h1>
</body>
</html>
在index.html模板文件中加上如下的链接
<a th:href="@{/a0b/testValeAnt}">value属性支持模糊匹配</a>
启动后返回这个链接是正常的
注意:有些情况我们是无法匹配成功的
- /a?b/testValueAnt 看样子是可以匹配到但是要注意,在浏览器上?后面的部分会认为是请求参数,前面的部分才是访问的路径,此时拿/a去请求是匹配不到的
- /a/b/testValueAnt 这个地址浏览器会直接去请求/a/b/testValueAnt是匹配不到控制器方法的
一定要注意,** 这个两边一定要是/,这个时候可以匹配到多级地址,如果它两个不是/则会失去模糊匹配的效果直接认为请求中就是**
value属性中的路径占位
我们常规的路径请求格式是:uri?name1=value1&name2=value2
除了上面这种常规的请求方式,还有一种RESTful的风格请求格式:uri/value1/value2/value3
原来常规的请求格式我们是把?后的当作为请求参数,它是key=value对的形式,那么RESTful风格的请求如果获取到路径中的请求值?
要获取到请求路径中的值得要把路径使用占位符来表示
@RequestMapping("/login/{username}/{password}")
public String testRESTful(@PathVariable("username") String username,@PathVariable("password") String password) {System.out.println(username + ":" + password);return "testRESTful";
}
从上面可以看到:
- @RequestMapping中value属性占位使用{xxx}形式
- 控制器方法中的形参要使用@PathVariable("xxx")形式,来把形参与路径中的值做绑定
这个时候路径占位符上的{xxx},其中指定的名称要与@PathVariable("xxx")指定的名称保持一致它们才能与对应的形参做绑定
method属性
这个属性是用来指定请求方法的,如:POST请求,GET请求,它们的请求方法分别是POST和GET才能对应上,如果我们后端定义的@RequestMapping中定义的method属性值是POST,而前端使用GET请求,这个时候请求方法就无法匹配了,会出现405错误。
method属性的值也是一个数组,类型是RequestMethod的枚举,它定义的值有:
GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE;
@RequestMapping(value = "/login", method= RequestMethod.POST)
public String login() {return "success";
}
像上面这个控制器处理方法login()要求请求路径是/login,请求方法必须是post
衍生Mapping
我们把不同请求方法都写作一个对应的Mapping注解,可以简化我们去指定method的过程。这样就对应的衍生出了如下几个Mapping注解
衍生注解 | 说明 |
---|---|
@PostMapping | 默认请求方法是POST @PostMapping("/test") 等价于 @RequestMapping(value="/test", method=RequestMethod.POST) |
@GetMapping | 表示方法默认采用GET处理方式 |
@PutMapping | 表示方法默认采用PUT处理方式 |
@DeleteMapiing | 表示方法默认使用DELETE处理方式 |
@PatchMapping | 表示方法默认使用PATCH处理方式 |
实际上衍生出来的Mapping注解就是为了省略去写method而来的。
我们看到有多种不同的请求方式,每种方式又是怎么样的呢?我们简单的来说明一个关于web中的请求方式。
web请求方式
前端向后端请求的时候有九种方式常用见有前五种
请求方式 | 说明 |
---|---|
GET | 获取资源,只能读取数据,不影响数据状态 使用url路径传参或者在http请求头添加参数,服务器返回请求资源 |
POST | 向服务器提交资源,可能会改变数据的状态 通过表单的方式提交请求体,服务端接收到请求后进行数据处理 |
PUT | 更新资源,用于更新指定的资源上内容 通过请求体发送需要更新的全部内容,服务端接收到数据后进行资源替换或修改 |
DELETE | 删除资源 用来删除指定资源,把要删 |