}
headers 属性的用法和功能与 params 属性相似. 在上面的代码中当请求 / testHeaders.do 的时候只有当请求头包含 Accept 信息, 且请求的 host 为 localhost 的时候才能正确的访问到 testHeaders 方法.
3, @RequestMapping 标记的处理器方法支持的方法参数和返回类型
支持的方法参数类型:
(1)HttpServlet 对象, 主要包括 HttpServletRequest ,HttpServletResponse 和 HttpSession 对象. 这些参数 Spring 在调用处理器方法的时候会自动给它们赋值, 所以当在处理器方法中需要使用到这些对象的时候, 可以直接在方法上给定一个方法参数的申明, 然后在方法体里面直接用就可以了. 但是有一点需要注意的是在使用 HttpSession 对象的时候, 如果此时 HttpSession 对象还没有建立起来的话就会有问题.
(2 )Spring 自己的 webRequest 对象. 使用该对象可以访问到存放在 HttpServletRequest 和 HttpSession 中的属性值.
(3 )InputStream ,OutputStream ,Reader 和 Writer . InputStream 和 Reader 是针对 HttpServletRequest 而言的, 可以从里面取数据; OutputStream 和 Writer 是针对 HttpServletResponse 而言的, 可以往里面写数据.
(4 )使用 @PathVariable ,@RequestParam ,@CookieValue 和 @RequestHeader 标记的参数.
(5 )使用 @ModelAttribute 标记的参数.
(6 )java.util.Map ,Spring 封装的 Model 和 ModelMap . 这些都可以用来封装模型数据, 用来给视图做展示.
(7 )实体类. 可以用来接收上传的参数.
(8 )Spring 封装的 MultipartFile . 用来接收上传文件的.
(9 )Spring 封装的 Errors 和 BindingResult 对象. 这两个对象参数必须紧接在需要验证的实体对象参数之后, 它里面包含了实体对象的验证结果.
支持的返回类型
(1 )一个包含模型和视图的 ModelAndView 对象.
(2 )一个模型对象, 这主要包括 Spring 封装好的 Model 和 ModelMap , 以及 java.util.Map , 当没有视图返回的时候视图名称将由 RequestToViewNameTranslator 来决定.
(3 )一个 View 对象. 这个时候如果在渲染视图的过程中模型的话就可以给处理器方法定义一个模型参数, 然后在方法体里面往模型中添加值.
(4 )一个 String 字符串. 这往往代表的是一个视图名称. 这个时候如果需要在渲染视图的过程中需要模型的话就可以给处理器方法一个模型参数, 然后在方法体里面往模型中添加值就可以了.
(5 )返回值是 void . 这种情况一般是我们直接把返回结果写到 HttpServletResponse 中了, 如果没有写的话, 那么 Spring 将会利用 RequestToViewNameTranslator 来返回一个对应的视图名称. 如果视图中需要模型的话, 处理方法与返回字符串的情况相同.
(6 )如果处理器方法被注解 @ResponseBody 标记的话, 那么处理器方法的任何返回类型都会通过 HttpMessageConverters 转换之后写到 HttpServletResponse 中, 而不会像上面的那些情况一样当做视图或者模型来处理.
(7 )除以上几种情况之外的其他任何返回类型都会被当做模型中的一个属性来处理, 而返回的视图还是由 RequestToViewNameTranslator 来决定, 添加到模型中的属性名称可以在该方法上用 @ModelAttribute("attributeName") 来定义, 否则将使用返回类型的类名称的首字母小写形式来表示. 使用 @ModelAttribute 标记的方法会在 @RequestMapping 标记的方法执行之前执行.
4, 使用 @ModelAttribute 和 @SessionAttributes 传递和保存数据
SpringMVC 支持使用 @ModelAttribute 和 @SessionAttributes 在不同的模型 (model) 和控制器之间共享数据. @ModelAttribute 主要有两种使用方式, 一种是标注在方法上, 一种是标注在 Controller 方法参数上.
当 @ModelAttribute 标记在方法上的时候, 该方法将在处理器方法执行之前执行, 然后把返回的对象存放在 session 或模型属性中, 属性名称可以使用 @ModelAttribute("attributeName") 在标记方法的时候指定, 若未指定, 则使用返回类型的类名称 (首字母小写) 作为属性名称. 关于 @ModelAttribute 标记在方法上时对应的属性是存放在 session 中还是存放在模型中, 我们来做一个实验, 看下面一段代码.
- @Controller@RequestMapping ( "/myTest" )public class MyController { @ModelAttribute ( "hello" ) public String getModel() {
- System. out .println( "-------------Hello---------" ); return "world" ;
- } @ModelAttribute ( "intValue" ) public int getInteger() {
- System. out .println( "-------------intValue---------------" ); return 10;
- } @RequestMapping ( "sayHello" ) public void sayHello( @ModelAttribute ( "hello" ) String hello, @ModelAttribute ( "intValue" ) int num, @ModelAttribute ( "user2" ) User user, Writer writer, HttpSession session) throws IOException {
- writer.write( "Hello" + hello + ", Hello" + user.getUsername() + num);
- writer.write( "\r" );
- Enumeration enume = session.getAttributeNames(); while (enume.hasMoreElements())
- writer.write(enume.nextElement() + "\r" );
- } @ModelAttribute ( "user2" ) public User getUser(){
- System. out .println( "---------getUser-------------" ); return new User(3, "user2" );
- }
- }
当我们请求 /myTest/sayHello.do 的时候使用 @ModelAttribute 标记的方法会先执行, 然后把它们返回的对象存放到模型中. 最终访问到 sayHello 方法的时候, 使用 @ModelAttribute 标记的方法参数都能被正确的注入值. 执行结果如下所示:
Hello world,Hello user210
由执行结果我们可以看出来, 此时 session 中没有包含任何属性, 也就是说上面的那些对象都是存放在模型属性中, 而不是存放在 session 属性中. 那要如何才能存放在 session 属性中呢? 这个时候我们先引入一个新的概念 @SessionAttributes , 它的用法会在讲完 @ModelAttribute 之后介绍, 这里我们就先拿来用一下. 我们在 MyController 类上加上 @SessionAttributes 属性标记哪些是需要存放到 session 中的. 看下面的代码:
- @Controller@RequestMapping ( "/myTest" )@SessionAttributes (value={ "intValue" , "stringValue" }, types={User. class })public class MyController { @ModelAttribute ( "hello" ) public String getModel() {
- System. out .println( "-------------Hello---------" ); return "world" ;
- } @ModelAttribute ( "intValue" ) public int getInteger() {
- System. out .println( "-------------intValue---------------" ); return 10;
- }
- @RequestMapping ( "sayHello" ) public void sayHello(Map<String, Object> map, @ModelAttribute ( "hello" ) String hello, @ModelAttribute ( "intValue" ) int num, @ModelAttribute ( "user2" ) User user, Writer writer, HttpServletRequest request) throws IOException {
- map.put( "stringValue" , "String" );
- writer.write( "Hello" + hello + ", Hello" + user.getUsername() + num);
- writer.write( "\r" );
- HttpSession session = request.getSession();
- Enumeration enume = session.getAttributeNames(); while (enume.hasMoreElements())
- writer.write(enume.nextElement() + "\r" );
- System. out .println(session);
- } @ModelAttribute ( "user2" ) public User getUser() {
- System. out .println( "---------getUser-------------" ); return new User(3, "user2" );
- }
- }
在上面代码中我们指定了属性为 intValue 或 stringValue 或者类型为 User 的都会放到 Session 中, 利用上面的代码当我们访问 /myTest/sayHello.do 的时候, 结果如下:
Hello world,Hello user210
仍然没有打印出任何 session 属性, 这是怎么回事呢? 怎么定义了把模型中属性名为 intValue 的对象和类型为 User 的对象存到 session 中, 而实际上没有加进去呢? 难道我们错啦? 我们当然没有错, 只是在第一次访问 /myTest/sayHello.do 的时候 @SessionAttributes 定义了需要存放到 session 中的属性, 而且这个模型中也有对应的属性, 但是这个时候还没有加到 session 中, 所以 session 中不会有任何属性, 等处理器方法执行完成后 Spring 才会把模型中对应的属性添加到 session 中. 所以当请求第二次的时候就会出现如下结果:
- Hello world,Hello user210
- user2
- intValue
- stringValue
当 @ModelAttribute 标记在处理器方法参数上的时候, 表示该参数的值将从模型或者 Session 中取对应名称的属性值, 该名称可以通过 @ModelAttribute("attributeName") 来指定, 若未指定, 则使用参数类型的类名称 (首字母小写) 作为属性名称.
5,@PathVariable 和 @RequestParam 的区别
请求路径上有个 id 的变量值, 可以通过 @PathVariable 来获取 @RequestMapping(value = "/page/{id}", method = RequestMethod.GET)
@RequestParam 用来获得静态的 URL 请求入参 spring 注解时 action 里用到.
简介:
handler method 参数绑定常用的注解, 我们根据他们处理的 Request 的不同内容部分分为四类:(主要讲解常用类型)
A, 处理 requet uri 部分 (这里指 uri template 中 variable, 不含 queryString 部分) 的注解: @PathVariable;
B, 处理 request header 部分的注解: @RequestHeader, @CookieValue;
C, 处理 request body 部分的注解:@RequestParam, @RequestBody;
D, 处理 attribute 类型是注解: @SessionAttributes, @ModelAttribute;
(1),@PathVariable
当使用 @RequestMapping URI template 样式映射时, 即 someUrl/{paramId}, 这时的 paramId 可通过 @Pathvariable 注解绑定它传过来的值到方法的参数上.
示例代码:
上面代码把 URI template 中变量 ownerId 的值和 petId 的值, 绑定到方法的参数上. 若方法参数名称和需要绑定的 uri template 中变量名称不一致, 需要在 @PathVariable("name")指定 uri template 中的名称.
(2), @RequestHeader,@CookieValue
@RequestHeader 注解, 可以把 Request 请求 header 部分的值绑定到方法的参数上.
示例代码:
- @Controller @RequestMapping("/owners/{ownerId}")
- public class RelativePathUriTemplateController {
- @RequestMapping("/pets/{petId}")
- public void findPet(@PathVariable String ownerId, @PathVariable String petId, Model model) {
- // implementation omitted
- }
- }
上面代码把 URI template 中变量 ownerId 的值和 petId 的值, 绑定到方法的参数上. 若方法参数名称和需要绑定的 uri template 中变量名称不一致, 需要在 @PathVariable("name")指定 uri template 中的名称.
(2), @RequestHeader,@CookieValue
@RequestHeader 注解, 可以把 Request 请求 header 部分的值绑定到方法的参数上.
示例代码:
这是一个 Request 的 header 部分:
- Host localhost:8080 Accept text/html,application/xhtml+xml,application/xml;q=0.9 Accept-Language fr,en-gb;q=0.7,en;q=0.3 Accept-Encoding gzip,deflate
- Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 Keep-Alive 300 @RequestMapping("/displayHeaderInfo.do")
- public void displayHeaderInfo(@RequestHeader("Accept-Encoding") String encoding,
- @RequestHeader("Keep-Alive") long keepAlive) {
- }
上面的代码, 把 request header 部分的 Accept-Encoding 的值, 绑定到参数 encoding 上了, Keep-Alive header 的值绑定到参数 keepAlive 上.
@CookieValue 可以把 Request header 中关于 cookie 的值绑定到方法的参数上.
例如有如下 Cookie 值:
- JSESSIONID=415A4AC178C59DACE0B2C9CA727CDD84
- @RequestMapping("/displayHeaderInfo.do")
- public void displayHeaderInfo(@CookieValue("JSESSIONID") String cookie) {
- }
即把 JSESSIONID 的值绑定到参数 cookie 上.
- (3),@RequestParam, @RequestBody
- @RequestParam
A) 常用来处理简单类型的绑定, 通过 Request.getParameter() 获取的 String 可直接转换为简单类型的情况 ( String--> 简单类型的转换操作由 ConversionService 配置的转换器来完成); 因为使用 request.getParameter() 方式获取参数, 所以可以处理 get 方式中 queryString 的值, 也可以处理 post 方式中 body data 的值;
B)用来处理 Content-Type: 为 application/x-www-form-urlencoded 编码的内容, 提交方式 GET,POST;
C) 该注解有两个属性: value,required; value 用来指定要传入值的 id 名称, required 用来指示参数是否必须绑定;
示例代码:
- @Controller @RequestMapping("/pets")
- @SessionAttributes("pet")
- public class EditPetForm {
- @RequestMapping(method = RequestMethod.GET)
- public String setupForm(@RequestParam("petId") int petId, ModelMap model) {
- Pet pet = this.clinic.loadPet(petId);
- model.addAttribute("pet", pet);
- return "petForm";
- }
- }
- @RequestBody
该注解常用来处理 Content-Type: 不是 application/x-www-form-urlencoded 编码的内容, 例如 application/json, application/xml 等;
它是通过使用 HandlerAdapter 配置的 HttpMessageConverters 来解析 post data body, 然后绑定到相应的 bean 上的.
因为配置有 FormHttpMessageConverter, 所以也可以用来处理 application/x-www-form-urlencoded 的内容, 处理完的结果放在一个 MultiValueMap<String, String > 里, 这种情况在某些特殊需求下使用, 详情查看 FormHttpMessageConverter api;
示例代码:
- @RequestMapping(value = "/something", method = RequestMethod.PUT)
- public void handle(@RequestBody String body, Writer writer) throws IOException {
- writer.write(body);
- }
- (4),@SessionAttributes, @ModelAttribute
- @SessionAttributes:
该注解用来绑定 HttpSession 中的 attribute 对象的值, 便于在方法中的参数里使用.
该注解有 value,types 两个属性, 可以通过名字和类型指定要使用的 attribute 对象;
示例代码:
- @Controller @RequestMapping("/editPet.do")
- @SessionAttributes("pet")
- public class EditPetForm {
- // ... }
- @ModelAttribute
该注解有两个用法, 一个是用于方法上, 一个是用于参数上;
用于方法上时: 通常用来在处理 @RequestMapping 之前, 为请求绑定需要从后台查询的 model;
用于参数上时: 用来通过名称对应, 把相应名称的值绑定到注解的参数 bean 上; 要绑定的值来源于:
A) @SessionAttributes 启用的 attribute 对象上;
B) @ModelAttribute 用于方法上时指定的 model 对象;
C) 上述两种情况都没有时, new 一个需要绑定的 bean 对象, 然后把 request 中按名称对应的方式把值绑定到 bean 中.
用到方法上 @ModelAttribute 的示例代码:
- @ModelAttribute public Account addAccount(@RequestParam String number) {
- return accountManager.findAccount(number);
- }
这种方式实际的效果就是在调用 @RequestMapping 的方法之前, 为 request 对象的 model 里 put("account", Account).
用在参数上的 @ModelAttribute 示例代码:
- @RequestMapping(value="/owners/{ownerId}/pets/{petId}/edit", method = RequestMethod.POST)
- public String processSubmit(@ModelAttribute Pet pet) {
- }
首先查询 @SessionAttributes 有无绑定的 Pet 对象, 若没有则查询 @ModelAttribute 方法层面上是否绑定了 Pet 对象, 若没有则将 URI template 中的值按对应的名称绑定到 Pet 对象的各属性上.
6,<context:component-scan base-package = "" />浅析
component-scan 默认扫描的注解类型是 @Component, 不过, 在 @Component 语义基础上细化后的 @Repository, @Service 和 @Controller 也同样可以获得 component-scan 的青睐
有了 < context:component-scan>, 另一个 < context:annotation-config/>标签根本可以移除掉, 因为已经被包含进去了
另外 < context:annotation-config/>还提供了两个子标签
- 1. <context:include-filter> // 指定扫描的路径
- 2. <context:exclude-filter> // 排除扫描的路径
- <context:component-scan > 有一个 use-default-filters 属性, 属性默认为 true, 表示会扫描指定包下的全部的标有 @Component 的类, 并注册成 bean. 也就是 @Component 的子注解 @Service,@Reposity 等.
这种扫描的粒度有点太大, 如果你只想扫描指定包下面的 Controller 或其他内容则设置 use-default-filters 属性为 false, 表示不再按照 scan 指定的包扫描, 而是按照 < context:include-filter > 指定的包扫描, 示例:
- <context:component-scan base-package="com.tan" use-default-filters="false">
- <context:include-filter type="regex" expression="com.tan.*"/>// 注意后面要写.*</context:component-scan>
当没有设置 use-default-filters 属性或者属性为 true 时, 表示基于 base-packge 包下指定扫描的具体路径
- <context:component-scan base-package="com.tan">
- <context:include-filter type="regex" expression=".controller.*"/>
- <context:include-filter type="regex" expression=".service.*"/>
- <context:include-filter type="regex" expression=".dao.*"/>
- </context:component-scan>
效果相当于:
- <context:component-scan base-package="com.tan">
- <context:exclude-filter type="regex" expression=".model.*"/>
- </context:component-scan>
注意: 本人尝试时无论哪种情况 < context:include-filter > 和 < context:exclude-filter > 都不能同时存在.
来源: http://www.bubuko.com/infodetail-2770828.html