`

Spring MVC 中的基于注解的 Controller

 
阅读更多
原文链接:http://blog.csdn.net/sgx425021234/article/details/13629835
终于来到了基于注解的 Spring MVC 了。之前我们所讲到的 handler,需要根据 url 并通过 HandlerMapping 来映射出相应的 handler 并调用相应的方法以响应请求。实际上,ControllerClassNameHandlerMapping, MultiActionController 和选择恰当的 methodNameResolver(如 InternalPathMethodNameResolver) 就已经可以在很大程度上帮助我们省去不少的 XML 配置,谁让 ControllerClassNameHandlerMapping 极度的拥抱了 Convention Over Configuration 呢。

        那为什么还要用基于注解的 Controller 呢?Spring MVC 在 Spring 2.5 发布中新添加了一种基于注解的 Controller 形式。借助于与 Spring 2.5 一同发布的容器内 <context:component-scan> 功能支持,基于注解的 Controller 几乎可以达到 XML 零配置,进而极大地提高我们的开发效率。

        和其它 Controller 一样,基于注解的 Controller 同样有相应的 HandlerMapping,那就是 DefaultAnnotationHandlerMapping。同样,也有相应的 HandlerAdapter,那就是 AnnotationMethodHandlerAdapter。甚至,我们都可以不把 Controller 注册到容器里,那么肯定需要一种机制来帮助我们完成这点,这就是 <context:component-scan>。开发基于注解的 Controller,我们需要做以下准备工作:

● <context:compnent-scan>
Xml代码 复制代码 
  1. <!-- 切记,这不是必需的!除非你把注解的 Controller 一个 个的注册到容器中。相信大家还是喜欢用 context:compnent-scan 吧。不要认为在 Spring MVC 中才提 到 context:component-scan,就认为它只能扫描 @Controller。component-scan 默认扫描的注解类型 是 @Component,不过,在 @Component 语义基础上细化后 的 @Repository, @Service 和 @Controller 也同样可以获得 component-scan 的青睐 -->  
  2. <context:component-scan base-package="org.zachary.spring3.anno.web" />  
  1. <!-- 切 记,这不是必需的!除非你把注解的 Controller 一个个的注册到容器中。相信大家还是喜欢用 context:compnent- scan 吧。不要认为在 Spring MVC 中才提到 context:component-scan,就认为它只能扫 描 @Controller。component-scan 默认扫描的注解类型是 @Component,不过,在 @Component 语义基础上 细化后的 @Repository, @Service 和 @Controller 也同样可以获得 component-scan 的青 睐 -->  
  2. <context:component-scan base-package="org.zachary.spring3.anno.web" />  

● HandlerMapping
Xml代码 复制代码 
  1. <bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">  
  2.   <description>  
  3.   这点是必需的还是非必需的呢?   
  4.   如 果定义了 DefaultAnnotationHandlerMapping,它就可以将请求来的 url 和被注解 了 @RequesMapping 的指进行匹配。当然,说这句话的前提是定义 DefaultAnnotationHandlerMapping 的优 先级比定义了其它的 HandlerMapping 的优先级要高(如果定义了其它的话)。   
  5.   如果 没有定义 DefaultAnnotationHandlerMapping,并不代表不能映射到相应的 handler 上。因为如果你定义了其它 的 HandlerMapping,请求过来的 url 和注解了的 @RequestMapping 里的值正好能匹配上,那么没 有 DefaultAnnotationHandlerMapping,@Controller 一样可以如鱼得水的被捕获到。   
  6.   当然,如果你要使用基于注解的 @Controller,最好还是老老实实地注册 DefaultAnnotationHandlerMapping。   
  7.   </description>  
  8. </bean>  
  1. <bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">  
  2.   <description>  
  3.   这点是必需的还是非必需的呢?  
  4.   如 果定义了 DefaultAnnotationHandlerMapping,它就可以将请求来的 url 和被注解 了 @RequesMapping 的指进行匹配。当然,说这句话的前提是定义 DefaultAnnotationHandlerMapping 的优 先级比定义了其它的 HandlerMapping 的优先级要高(如果定义了其它的话)。  
  5.   如 果没有定义 DefaultAnnotationHandlerMapping,并不代表不能映射到相应的 handler 上。因为如果你定义了其它 的 HandlerMapping,请求过来的 url 和注解了的 @RequestMapping 里的值正好能匹配上,那么没 有 DefaultAnnotationHandlerMapping,@Controller 一样可以如鱼得水的被捕获到。  
  6.   当然,如果你要使用基于注解的 @Controller,最好还是老老实实地注册 DefaultAnnotationHandlerMapping。  
  7.   </description>  
  8. </bean>  

● HandlerAdaptor
Xml代码 复制代码 
  1. <bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">  
  2.   <description>  
  3.   和上面的 HandlerMapping 一样,是必需的还是非必需的呢?   
  4.   Spring MVC 中,如果我们没有注册任何 HandlerAdaptor 到容器中,注意,我说的是任何。那么 DispatcherServlet 将启用后备的几个默认使用的 HandlerAdaptor 实现,包括:   
  5.   org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter   
  6.   org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter   
  7.   org.springframework.web.servlet.mvc.AnnotationMethodHandlerAdaptor   
  8.   
  9.    看 见没,如果我们没有注册任何的 HandlerAdaptor,框架会准备 AnnotationMethodHandlerAdaptor 的。可是由 于某些原因,我们需要为某些 HandlerAdaptoer 进行一些定制化,即在容器中注册了某个 HandlerAdaptor,那么很抱歉,框架 只会启用你注册的那个,而框架本身准备的不会被启用。所以,你一旦为某个 HandlerMapping 进行了定制化,请别忘了把其它 的 HandlerAdaptor 也注册进来,即便这些不需要定制化。否则的话,后果你是可以想象的。当然,除非你确保你真的只需要那一个你注册进容器 的 HandlerAdaptor,否则,我再啰嗦一遍,别忘了把其它的 HandlerAdaptor 也注册进来。   
  10.   </description>  
  11. </bean>  
  1. <bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">  
  2.   <description>  
  3.   和上面的 HandlerMapping 一样,是必需的还是非必需的呢?  
  4.   Spring MVC 中,如果我们没有注册任何 HandlerAdaptor 到容器中,注意,我说的是任何。那么 DispatcherServlet 将启用后备的几个默认使用的 HandlerAdaptor 实现,包括:  
  5.   org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter  
  6.   org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter  
  7.   org.springframework.web.servlet.mvc.AnnotationMethodHandlerAdaptor  
  8.   
  9.    看 见没,如果我们没有注册任何的 HandlerAdaptor,框架会准备 AnnotationMethodHandlerAdaptor 的。可是由 于某些原因,我们需要为某些 HandlerAdaptoer 进行一些定制化,即在容器中注册了某个 HandlerAdaptor,那么很抱歉,框架 只会启用你注册的那个,而框架本身准备的不会被启用。所以,你一旦为某个 HandlerMapping 进行了定制化,请别忘了把其它 的 HandlerAdaptor 也注册进来,即便这些不需要定制化。否则的话,后果你是可以想象的。当然,除非你确保你真的只需要那一个你注册进容器 的 HandlerAdaptor,否则,我再啰嗦一遍,别忘了把其它的 HandlerAdaptor 也注册进来。  
  10.   </description>  
  11. </bean>  


        好了,有了以上几点准备工作,我们就可以开始基于注解的 Controller 之旅了。下面我们来一个一个注解的来讲解。

● @Controller
Java代码 复制代码 
  1. /**  
  2.  * @Controller,类级别上的注解。我们定义的类可以只是一个 javabean,不需要实现任何接口。标注了  
  3.  * @Controller,借助 <context:component-scan>,框架能自动识别到这就是一个 Controller  
  4.  */  
  5. @Controller  
  6. public class MyController {   
  7.   // ......   
  8. }  
  1. /** 
  2.  * @Controller,类级别上的注解。我们定义的类可以只是一个 javabean,不需要实现任何接口。标注了 
  3.  * @Controller,借助 <context:component-scan>,框架能自动识别到这就是一个 Controller 
  4.  */  
  5. @Controller  
  6. public class MyController {  
  7.   // ......  
  8. }  

● @RequestMapping
Java代码 复制代码 
  1. /**  
  2.  * @RequestMapping 可以出现在类级别上,也可以出现在方法上。如果出现在类级别上,那请求的 url 为 类级别  
  3.  * 上的 @RequestMapping + 方法级别上的 @RequestMapping,否则直接取方法级上的 @RequestMapping。  
  4.  * 类级别的 @RequestMapping 不是必需的。  
  5.  */  
  6. @Controller  
  7. @RequestMapping("/my")   
  8. public class MyController {   
  9.      
  10.   /**  
  11.    * 由于类级别上定义了 @RequestMapping,那么想匹配到这个方法来处理请求,url 必须为 /my/somelist。  
  12.    * 如果没有定义类级别上的 @RequestMapping,url 为 /somelist 即可。同时,请求方法必须为 POST  
  13.    */  
  14.   @RequestMapping(value="/somelist", method=RequestMethod.POST);   
  15.   public String getSomeList() {...}   
  16.   
  17.     /**  
  18.      * 在方法级别使用 @RequestMapping 来限定请求处理的时候,可以指定两个属性。除了我们在上面刚使用过的  
  19.      * method 属性,还有一个 params 属性。使用 params 属性,可以达到与使用  
  20.      * ParameterMethodNameResolver 作为 MethodResolver的 MultiActionController 类似的功能。  
  21.      *  
  22.      * params 有两种表达形式,这里先说第一种:"parameterName=parameterValue"  
  23.      *  
  24.      * 请求方法为 GET 或 POST,且具有 hello 参数,且值为 world 的请求才能匹配到该方法,如:  
  25.      *   /my?hello=world  
  26.      */  
  27.     @RequestMapping(params="hello=world", method={RequestMethod.GET, RequestMethod.POST})   
  28.     public String helloworld() {...}   
  29.   
  30.     /**  
  31.      * 请求方法为 GET 或 POST,且具有 hello 参数,且值为 java 的请求才能匹配到该方法,如:  
  32.      *   /my?hello=java  
  33.      */  
  34.     @RequestMapping(params="hello=java", method={RequestMethod.GET, RequestMethod.POST})   
  35.     public String hellojava() {...}   
  36.   
  37.     /**  
  38.      * params 属性的另外一种表达形式为:"parameter"  
  39.      *  
  40.      * 请求方法为 GET,且具有请求参数 java 即匹配此方法,而不管 java 参数的值是什么,如:  
  41.      *   /my?java=anything  
  42.      */  
  43.     @RequestMapping(params="java", method={RequestMethod.GET})   
  44.     public String java() {...}   
  45.   
  46.     /**  
  47.      * 请求方法为 GET,且具有请求参数 cplusplus 即匹配此方法,而不管 cplusplus 参数的值是什么,如:  
  48.      *   /my?cplusplus=anything  
  49.      */  
  50.     @RequestMapping(params="cplusplus", method={RequestMethod.GET})   
  51.     public String cplusplus() {...}   
  52.   
  53.     /**  
  54.      * @RequestMapping 还有一个参数化 headers,它和 params 非常相似,也有两种表达式,只不过它是对  
  55.      * 请求头做限制罢了。大家可以通过 telnet 或 http-client 来发类似的请求以检验。以 telnet 为例:  
  56.      *   
  57.      * telnet localhost 8080  
  58.      * POST /contextPath/my HTTP/1.1  
  59.      * Host: localhost  
  60.      * hello: world # 这个就是自定义请求头,和标准的请求头的写法别无二致  
  61.      * 【回车】  
  62.      * 【回车】  
  63.      */  
  64.     @RequestMapping(headers="hello=world", method={RequestMethod.POST})   
  65.     public String cplusplus() {...}   
  66. }  
  1. /** 
  2.  * @RequestMapping 可以出现在类级别上,也可以出现在方法上。如果出现在类级别上,那请求的 url 为 类级别 
  3.  * 上的 @RequestMapping + 方法级别上的 @RequestMapping,否则直接取方法级上的 @RequestMapping。 
  4.  * 类级别的 @RequestMapping 不是必需的。 
  5.  */  
  6. @Controller  
  7. @RequestMapping("/my")  
  8. public class MyController {  
  9.     
  10.   /** 
  11.    * 由于类级别上定义了 @RequestMapping,那么想匹配到这个方法来处理请求,url 必须为 /my/somelist。 
  12.    * 如果没有定义类级别上的 @RequestMapping,url 为 /somelist 即可。同时,请求方法必须为 POST 
  13.    */  
  14.   @RequestMapping(value="/somelist", method=RequestMethod.POST);  
  15.   public String getSomeList() {...}  
  16.   
  17.     /** 
  18.      * 在方法级别使用 @RequestMapping 来限定请求处理的时候,可以指定两个属性。除了我们在上面刚使用过的 
  19.      * method 属性,还有一个 params 属性。使用 params 属性,可以达到与使用 
  20.      * ParameterMethodNameResolver 作为 MethodResolver的 MultiActionController 类似的功能。 
  21.      * 
  22.      * params 有两种表达形式,这里先说第一种:"parameterName=parameterValue" 
  23.      * 
  24.      * 请求方法为 GET 或 POST,且具有 hello 参数,且值为 world 的请求才能匹配到该方法,如: 
  25.      *   /my?hello=world 
  26.      */  
  27.     @RequestMapping(params="hello=world", method={RequestMethod.GET, RequestMethod.POST})  
  28.     public String helloworld() {...}  
  29.   
  30.     /** 
  31.      * 请求方法为 GET 或 POST,且具有 hello 参数,且值为 java 的请求才能匹配到该方法,如: 
  32.      *   /my?hello=java 
  33.      */  
  34.     @RequestMapping(params="hello=java", method={RequestMethod.GET, RequestMethod.POST})  
  35.     public String hellojava() {...}  
  36.   
  37.     /** 
  38.      * params 属性的另外一种表达形式为:"parameter" 
  39.      * 
  40.      * 请求方法为 GET,且具有请求参数 java 即匹配此方法,而不管 java 参数的值是什么,如: 
  41.      *   /my?java=anything 
  42.      */  
  43.     @RequestMapping(params="java", method={RequestMethod.GET})  
  44.     public String java() {...}  
  45.   
  46.     /** 
  47.      * 请求方法为 GET,且具有请求参数 cplusplus 即匹配此方法,而不管 cplusplus 参数的值是什么,如: 
  48.      *   /my?cplusplus=anything 
  49.      */  
  50.     @RequestMapping(params="cplusplus", method={RequestMethod.GET})  
  51.     public String cplusplus() {...}  
  52.   
  53.     /** 
  54.      * @RequestMapping 还有一个参数化 headers,它和 params 非常相似,也有两种表达式,只不过它是对 
  55.      * 请求头做限制罢了。大家可以通过 telnet 或 http-client 来发类似的请求以检验。以 telnet 为例: 
  56.      *  
  57.      * telnet localhost 8080 
  58.      * POST /contextPath/my HTTP/1.1 
  59.      * Host: localhost 
  60.      * hello: world # 这个就是自定义请求头,和标准的请求头的写法别无二致 
  61.      * 【回车】 
  62.      * 【回车】 
  63.      */  
  64.     @RequestMapping(headers="hello=world", method={RequestMethod.POST})  
  65.     public String cplusplus() {...}  
  66. }  

● @RequestParam(将请求参数绑定到方法参数)
Java代码 复制代码 
  1. @Controller  
  2. @RequestMapping("/my")   
  3. public class MyController {   
  4.   
  5.   /**  
  6.    * 注意,这里的方法有一个参数。若请求 url 为 /my/test,会匹配此方法。这里的方法的参数名为 userId,  
  7.    * 那么请求参数中一定有名为 userId 的参数,且值为整数。这也是默认的绑定行为,它是根据名称匹配原则进行  
  8.    * 的数据绑定。当请求中的参数名与方法名一致的时候,相应的参数值将被绑定到相应的方法参数上。  
  9.    *   
  10.    * 如果没有传递 userId 参数,框架会传入 null。可是这里我们定义的是 primitive type,异常伺候!若  
  11.    * 要解决此问题,需要将 primitive type 定义成相应的 wrapper type 即可,这里使用 Integer 就行了。  
  12.    *  
  13.    * 如果传递了 userId 参数,但值不是整数,你叫 test 怎么办呢?这种情况下,框架借助 PropertyEditor   
  14.    * 数据类型转换失败,ExceptionResolver 会接手处理,请求是不会进入 test 方法的。  
  15.    *  
  16.    * 这种方式下,默认的绑定行为需要我们严格遵守命名一致性原则。如果我们对此不满,想自定义绑定关系,可以求  
  17.    * 助于 @RequestParam。  
  18.    */  
  19.   @RequestMapping("/test")   
  20.   public String test(int userId) { ... }   
  21.   
  22.   /**  
  23.    * 当我们不想使用 userId 作为方法的参数名,即不想使用默认的数据绑定方式。如果我们要使用 id 作为方法  
  24.    * 的参数,为了保证名称为 userId 的请求参数可以绑定到新的名称为 id 的方法参数上,我们就可以使用   
  25.    * @RequestParam 对这一参数进行标注。@RequestParam 只可以标注于方法参数上。  
  26.    *  
  27.    * 如果请求参数中有 age,和方法的参数名称一致,故 age 参数不需要 @RequestParam 标注。如果没有传递  
  28.    * age,我们又不想定义成 Integer,很显然框架会注入 null 值,报错是必然的。这是由于 @RequestParam   
  29.    * 的 required 属性决定的,默认就是 true。如果我们定义成 false,  
  30.    * 即 @RequestParam(required=false) int age  
  31.    * 这个时候定义成 int 型的 age,即便请求参数没有 age 参数,也是没问题的。  
  32.    *  
  33.    * 同时,这里还能绑定 Date 类型,User 对象类型等等。如 date=2011-01-01&userName=Tom&userAge=18  
  34.    * 这里,User 类的属性需要为 userName 和 userAge,以免和 age,name 混淆。所以,Spring MVC 对对象  
  35.    * 的数据绑定就没有 Struts2 做的那么好了,Strtus2 可以这样:user.age=18&user.name=Tom  
  36.    */  
  37.   @RequestMapping("/test2")   
  38.   public String test2(@RequestParam("userId"int id, int age, Date date, User user) { ... }   
  39. }  
  1. @Controller  
  2. @RequestMapping("/my")  
  3. public class MyController {  
  4.   
  5.   /** 
  6.    * 注意,这里的方法有一个参数。若请求 url 为 /my/test,会匹配此方法。这里的方法的参数名为 userId, 
  7.    * 那么请求参数中一定有名为 userId 的参数,且值为整数。这也是默认的绑定行为,它是根据名称匹配原则进行 
  8.    * 的数据绑定。当请求中的参数名与方法名一致的时候,相应的参数值将被绑定到相应的方法参数上。 
  9.    *  
  10.    * 如果没有传递 userId 参数,框架会传入 null。可是这里我们定义的是 primitive type,异常伺候!若 
  11.    * 要解决此问题,需要将 primitive type 定义成相应的 wrapper type 即可,这里使用 Integer 就行了。 
  12.    * 
  13.    * 如果传递了 userId 参数,但值不是整数,你叫 test 怎么办呢?这种情况下,框架借助 PropertyEditor  
  14.    * 数据类型转换失败,ExceptionResolver 会接手处理,请求是不会进入 test 方法的。 
  15.    * 
  16.    * 这种方式下,默认的绑定行为需要我们严格遵守命名一致性原则。如果我们对此不满,想自定义绑定关系,可以求 
  17.    * 助于 @RequestParam。 
  18.    */  
  19.   @RequestMapping("/test")  
  20.   public String test(int userId) { ... }  
  21.   
  22.   /** 
  23.    * 当我们不想使用 userId 作为方法的参数名,即不想使用默认的数据绑定方式。如果我们要使用 id 作为方法 
  24.    * 的参数,为了保证名称为 userId 的请求参数可以绑定到新的名称为 id 的方法参数上,我们就可以使用  
  25.    * @RequestParam 对这一参数进行标注。@RequestParam 只可以标注于方法参数上。 
  26.    * 
  27.    * 如果请求参数中有 age,和方法的参数名称一致,故 age 参数不需要 @RequestParam 标注。如果没有传递 
  28.    * age,我们又不想定义成 Integer,很显然框架会注入 null 值,报错是必然的。这是由于 @RequestParam  
  29.    * 的 required 属性决定的,默认就是 true。如果我们定义成 false, 
  30.    * 即 @RequestParam(required=false) int age 
  31.    * 这个时候定义成 int 型的 age,即便请求参数没有 age 参数,也是没问题的。 
  32.    * 
  33.    * 同时,这里还能绑定 Date 类型,User 对象类型等等。如 date=2011-01-01&userName=Tom&userAge=18 
  34.    * 这里,User 类的属性需要为 userName 和 userAge,以免和 age,name 混淆。所以,Spring MVC 对对象 
  35.    * 的数据绑定就没有 Struts2 做的那么好了,Strtus2 可以这样:user.age=18&user.name=Tom 
  36.    */  
  37.   @RequestMapping("/test2")  
  38.   public String test2(@RequestParam("userId"int id, int age, Date date, User user) { ... }  
  39. }  

● @PathVariable(将 url template 里的参数绑定到方法参数)
Java代码 复制代码 
  1. @Controller  
  2. @RequestMapping("/my")   
  3. public class MyController {   
  4.   
  5.   /**  
  6.    * @PathVariable 是 url 模板,需要和 @RequestMapping 配合起来使用,这是 Spring 3.0 之后引入的。  
  7.    *  
  8.    * 在这个例子中,请求的 url 必须满足类似 /my/user/zhangsan/18 这样的格式才能匹配方法。url 模板里  
  9.    * 的参数名和方法参数名的绑定规则和 @RequestParam 类似,这里就不再赘述了。  
  10.    *  
  11.    * @PathVariable 和 @RequestParam 的区别在于:  
  12.    *   @PathVariable 的 url:/my//user/zhangsan/18  
  13.    *   @RequestParam 的 url:/my//user?nickname=zhangsan&age=18  
  14.    */  
  15.   @RequestMapping("/user/{nickname}/{age}");   
  16.   public String getUserInfo(@PathVariable("nickname") String name, @PathVariable int age) {...}   
  17. }  
  1. @Controller  
  2. @RequestMapping("/my")  
  3. public class MyController {  
  4.   
  5.   /** 
  6.    * @PathVariable 是 url 模板,需要和 @RequestMapping 配合起来使用,这是 Spring 3.0 之后引入的。 
  7.    * 
  8.    * 在这个例子中,请求的 url 必须满足类似 /my/user/zhangsan/18 这样的格式才能匹配方法。url 模板里 
  9.    * 的参数名和方法参数名的绑定规则和 @RequestParam 类似,这里就不再赘述了。 
  10.    * 
  11.    * @PathVariable 和 @RequestParam 的区别在于: 
  12.    *   @PathVariable 的 url:/my//user/zhangsan/18 
  13.    *   @RequestParam 的 url:/my//user?nickname=zhangsan&age=18 
  14.    */  
  15.   @RequestMapping("/user/{nickname}/{age}");  
  16.   public String getUserInfo(@PathVariable("nickname") String name, @PathVariable int age) {...}  
  17. }  

● @RequestBody(将请求正文绑定到方法参数)
Java代码 复制代码 
  1. /**  
  2.  * 来看一个 http 请求:  
  3.  * (请求行) POST /my HTTP/1.1  
  4.  * (请求头) Host: localhost  
  5.  * (请求头) Content-Type: text/plain  
  6.  * (请求头) Content-Length: 5  
  7.  *  
  8.  * (请求体) hello  
  9.  *  
  10.  * 这里的 hello,就是请求体,也称 request message。若有请求体,则必须提供请求体的类型和长度,这些信  
  11.  * 息是写在请求头里的,即 Content-Type 和 Content-Length  
  12.  */  
  13. @Controller  
  14. @RequestMapping("/my")   
  15. public class MyController {   
  16.   
  17.   /**  
  18.    * 我们定义的 body 的数据类型是 String,请求体嘛,肯定是 String。实际上,@RequestBody 是用于将请  
  19.    * 求体的内容绑定到方法参数上,数据类型不一定是 String。Spring MVC 是通过 HttpMessageConverter  
  20.    * 来完成这种转换的。AnnotationMethodHandlerAdapter 默认注册了一些 HttpMessageConverters:  
  21.    *   ByteArrayHttpMessageConverter - converts byte arrays  
  22.    *   StringHttpMessageConverter - converts strings  
  23.    *   FormHttpMessageConverter - converts form data to/from MultiValueMap<String,String>  
  24.    *   SourceHttpMessageConverter - convert to/from a javax.xml.transform.Source  
  25.    *   MappingJacksonHttpMessageConverter - converts json  
  26.    *   MarshallingHttpMessageConverter - convert to/from an object using the   
  27.    *                                     org.springframework.oxm package.  
  28.    *  
  29.    * 正如上所述,HttpMessageConverter 用于从请求正文绑定到对象和把对象序列化成 String 予客户端响应。  
  30.    * 即 HttpMessageConverter is responsible for converting from the HTTP request message to  
  31.    * an object and converting from an object to the HTTP response body  
  32.    *  
  33.    * 我们可以在 AnnotationMethodHandlerAdapter 定义任意多的 HttpMessageConverters。  
  34.    *  
  35.    * 既然 HttpMessageConverter 可以用于双向 convert,这里讨论的是 @RequestBody,那这部分我们只讲   
  36.    * converting from the HTTP request message to an object。  
  37.    *  
  38.    * 假设我们只向 AnnotationMethodHandlerAdapter 注入了 MappingJacksonHttpMessageConverter 和  
  39.    * MarshallingHttpMessageConverter。处理请求的方法有如下签名:  
  40.    *     public String test(@RequestBody User user) { ... }  
  41.    *  
  42.    * 不管请求正文的内容是什么,对于客户端和服务器而言,它们只是用文本来互相通信。把字符串转为 User 对  
  43.    * 象,该用哪个 HttpMessageConverter 来完成此项工作呢?  
  44.    *  
  45.    * 在定义 HttpMessageConverters 时,我们可以为其指定 supportedMediaTypes。对于将请求正文转为对象  
  46.    * 这个方向的操作,HttpMessageConverters 会从请求头得到 Content-Type 头信息,看其是否隶属于其定义  
  47.    * 的 supportedMediaTypes。若没有匹配上,则会使用下一个 HttpMessageConverter 做同样的判断。只要  
  48.    * 某个 HttpMessageConverter 支持请求头中的 Content-Type,那么就会应用此 HttpMessageConverter  
  49.    * 来将 String 转为 Object。当然,若请求正文并没有按照 Content-Type 所规定的格式来编写,必然要收到  
  50.    * 500 的响应。同时请注意,请求头中还必须提供 Content-Length,否则拿不到请求正文。  
  51.    *  
  52.    * 如果所有的 HttpMessageConverters 中定义的 supportedMediaTypes 均不能匹配上 Content-Type 请  
  53.    * 求头中的类型,那么就会收到 415 Unsupported Media Type 响应。  
  54.    */  
  55.   @RequestMapping("/user/body");   
  56.   public String getBody(@RequestBody String body) {   
  57.   
  58.     // 这里的 body 的内容就是 hello   
  59.     System.out.println(body);   
  60.     return null;   
  61.   }   
  62. }  
  1. /** 
  2.  * 来看一个 http 请求: 
  3.  * (请求行) POST /my HTTP/1.1 
  4.  * (请求头) Host: localhost 
  5.  * (请求头) Content-Type: text/plain 
  6.  * (请求头) Content-Length: 5 
  7.  * 
  8.  * (请求体) hello 
  9.  * 
  10.  * 这里的 hello,就是请求体,也称 request message。若有请求体,则必须提供请求体的类型和长度,这些信 
  11.  * 息是写在请求头里的,即 Content-Type 和 Content-Length 
  12.  */  
  13. @Controller  
  14. @RequestMapping("/my")  
  15. public class MyController {  
  16.   
  17.   /** 
  18.    * 我们定义的 body 的数据类型是 String,请求体嘛,肯定是 String。实际上,@RequestBody 是用于将请 
  19.    * 求体的内容绑定到方法参数上,数据类型不一定是 String。Spring MVC 是通过 HttpMessageConverter 
  20.    * 来完成这种转换的。AnnotationMethodHandlerAdapter 默认注册了一些 HttpMessageConverters: 
  21.    *   ByteArrayHttpMessageConverter - converts byte arrays 
  22.    *   StringHttpMessageConverter - converts strings 
  23.    *   FormHttpMessageConverter - converts form data to/from MultiValueMap<String,String> 
  24.    *   SourceHttpMessageConverter - convert to/from a javax.xml.transform.Source 
  25.    *   MappingJacksonHttpMessageConverter - converts json 
  26.    *   MarshallingHttpMessageConverter - convert to/from an object using the  
  27.    *                                     org.springframework.oxm package. 
  28.    * 
  29.    * 正如上所述,HttpMessageConverter 用于从请求正文绑定到对象和把对象序列化成 String 予客户端响应。 
  30.    * 即 HttpMessageConverter is responsible for converting from the HTTP request message to 
  31.    * an object and converting from an object to the HTTP response body 
  32.    * 
  33.    * 我们可以在 AnnotationMethodHandlerAdapter 定义任意多的 HttpMessageConverters。 
  34.    * 
  35.    * 既然 HttpMessageConverter 可以用于双向 convert,这里讨论的是 @RequestBody,那这部分我们只讲  
  36.    * converting from the HTTP request message to an object。 
  37.    * 
  38.    * 假设我们只向 AnnotationMethodHandlerAdapter 注入了 MappingJacksonHttpMessageConverter 和 
  39.    * MarshallingHttpMessageConverter。处理请求的方法有如下签名: 
  40.    *     public String test(@RequestBody User user) { ... } 
  41.    * 
  42.    * 不管请求正文的内容是什么,对于客户端和服务器而言,它们只是用文本来互相通信。把字符串转为 User 对 
  43.    * 象,该用哪个 HttpMessageConverter 来完成此项工作呢? 
  44.    * 
  45.    * 在定义 HttpMessageConverters 时,我们可以为其指定 supportedMediaTypes。对于将请求正文转为对象 
  46.    * 这个方向的操作,HttpMessageConverters 会从请求头得到 Content-Type 头信息,看其是否隶属于其定义 
  47.    * 的 supportedMediaTypes。若没有匹配上,则会使用下一个 HttpMessageConverter 做同样的判断。只要 
  48.    * 某个 HttpMessageConverter 支持请求头中的 Content-Type,那么就会应用此 HttpMessageConverter 
  49.    * 来将 String 转为 Object。当然,若请求正文并没有按照 Content-Type 所规定的格式来编写,必然要收到 
  50.    * 500 的响应。同时请注意,请求头中还必须提供 Content-Length,否则拿不到请求正文。 
  51.    * 
  52.    * 如果所有的 HttpMessageConverters 中定义的 supportedMediaTypes 均不能匹配上 Content-Type 请 
  53.    * 求头中的类型,那么就会收到 415 Unsupported Media Type 响应。 
  54.    */  
  55.   @RequestMapping("/user/body");  
  56.   public String getBody(@RequestBody String body) {  
  57.   
  58.     // 这里的 body 的内容就是 hello  
  59.     System.out.println(body);  
  60.     return null;  
  61.   }  
  62. }  

● @ResponseBody(将处理完请求后返回的对象绑定到响应正文)
Java代码 复制代码 
  1. /**  
  2.  * 上面的 @RequestBody 讲了 HttpMessageConverter 从请求正文到对象转换的方向,现在来讲讲另外一个方  
  3.  * 向,@ResponseBody,此时,HttpMessageConverter 用于将处理完请求后返回的对象序列化成字符串,即  
  4.  * converting from an object to the HTTP response body.  
  5.  */  
  6. @Controller  
  7. @RequestMapping("/my")   
  8. public class MyController {   
  9.   
  10.   /**  
  11.    * 该方法的返回类型是 User,并不符合含有 @RequestMapping 的注解所需的签名方式。但它仍然是合法的,因  
  12.    * 为在返回类型前有 @ResponseBody 注解,此注解将告知框架,将 User 对象作为影响正文返回?什么?对象  
  13.    * 作为响应正文!所以,HttpMessageConverter 在这里就起到作用了。这里讨论的是 @ResponseBody,所以  
  14.    * 这里我们只讲 converting from an object to the HTTP response body。  
  15.    *  
  16.    * User 对象要转成什么样的 String,或者说要转成什么格式的 String?这个时候需要从请求头中获得此信息  
  17.    * 了,这里,就是请求头的 Accept 头。Accept 头可以使用逗号分隔定义多个类型,用以告知服务器我只接受  
  18.    * 哪些类型的响应。AnnotationMethodHandlerAdapter 中同样注入了多个 HttpMessageConverter,每个   
  19.    * HttpMessageConverter 都可以定义各自的 supportedMediaTypes。这个时候该用哪个   
  20.    * HttpMessageConverter 来完成对象到文本的序列化操作呢?  
  21.    *  
  22.    * 遍历 Accept 头中的每种媒体类型,在定义的多个 HttpMessageConverters 中依次去匹配,若匹配上,就使  
  23.    * 用该 HttpMessageConverter 来完成序列化操作,并且响应头的 Content-Type 并不是请求头 Accept 头  
  24.    * 的诸多类型中第一个被匹配的类型,而是匹配到的 HttpMessageConverter 定义的 supportedMediaTypes  
  25.    * 中的第一个类型。  
  26.    *  
  27.    * 如果所有的 HttpMessageConverters 中定义的 supportedMediaTypes 均不能匹配上 Accept 请求头中  
  28.    * 的诸多的类型,那么就会收到 406 Not Acceptable 响应。  
  29.    */  
  30.   @RequestMapping("/user")   
  31.   public @ResponseBody User getUser() {   
  32.     return new User(18"Jack""计算机");   
  33.   }   
  34. }  
  1. /** 
  2.  * 上面的 @RequestBody 讲了 HttpMessageConverter 从请求正文到对象转换的方向,现在来讲讲另外一个方 
  3.  * 向,@ResponseBody,此时,HttpMessageConverter 用于将处理完请求后返回的对象序列化成字符串,即 
  4.  * converting from an object to the HTTP response body. 
  5.  */  
  6. @Controller  
  7. @RequestMapping("/my")  
  8. public class MyController {  
  9.   
  10.   /** 
  11.    * 该方法的返回类型是 User,并不符合含有 @RequestMapping 的注解所需的签名方式。但它仍然是合法的,因 
  12.    * 为在返回类型前有 @ResponseBody 注解,此注解将告知框架,将 User 对象作为影响正文返回?什么?对象 
  13.    * 作为响应正文!所以,HttpMessageConverter 在这里就起到作用了。这里讨论的是 @ResponseBody,所以 
  14.    * 这里我们只讲 converting from an object to the HTTP response body。 
  15.    * 
  16.    * User 对象要转成什么样的 String,或者说要转成什么格式的 String?这个时候需要从请求头中获得此信息 
  17.    * 了,这里,就是请求头的 Accept 头。Accept 头可以使用逗号分隔定义多个类型,用以告知服务器我只接受 
  18.    * 哪些类型的响应。AnnotationMethodHandlerAdapter 中同样注入了多个 HttpMessageConverter,每个  
  19.    * HttpMessageConverter 都可以定义各自的 supportedMediaTypes。这个时候该用哪个  
  20.    * HttpMessageConverter 来完成对象到文本的序列化操作呢? 
  21.    * 
  22.    * 遍历 Accept 头中的每种媒体类型,在定义的多个 HttpMessageConverters 中依次去匹配,若匹配上,就使 
  23.    * 用该 HttpMessageConverter 来完成序列化操作,并且响应头的 Content-Type 并不是请求头 Accept 头 
  24.    * 的诸多类型中第一个被匹配的类型,而是匹配到的 HttpMessageConverter 定义的 supportedMediaTypes 
  25.    * 中的第一个类型。 
  26.    * 
  27.    * 如果所有的 HttpMessageConverters 中定义的 supportedMediaTypes 均不能匹配上 Accept 请求头中 
  28.    * 的诸多的类型,那么就会收到 406 Not Acceptable 响应。 
  29.    */  
  30.   @RequestMapping("/user")  
  31.   public @ResponseBody User getUser() {  
  32.     return new User(18"Jack""计算机");  
  33.   }  
  34. }  

● @ModelAttribute
Java代码 复制代码 
  1. /**  
  2.  * @ModelAttribute 可以为视图渲染提供更多的模型数据,而不需要在处理请求的方法里添加 ModelMap 或  
  3.  * Model 类型的参数。  
  4.  *  
  5.  * @ModelAttribute 可以标注在方法(存数据)上,也可以标注在方法参数(取数据)上。  
  6.  */  
  7. @Controller  
  8. @RequestMapping("/my")   
  9. public class MyController {   
  10.   
  11.   /**  
  12.    * 在处理该请求时,方法的返回类型是 User,貌似不符合返回类型的规范。由于这里使用了 @ModelAttribute  
  13.    * 注解,表示将返回的对象以 "user" 为 key 放入模型数据里。这里的 key 值默认值是返回的数据类型首字母  
  14.    * 小写的结果。如果想自定义 key,可以写成 @ModelAttribute("myAttribute"),那么模型数据将会将   
  15.    * User 对象绑定到 key 为 "myAttribute" 上。  
  16.    *   
  17.    * jsp 里可以这样访问模型里的数据:  
  18.    *   age: ${user.age}  
  19.    *   name: ${user.name}  
  20.    *   job: ${user.job}  
  21.    *  
  22.    * 当然,这里只是提到了 @ModelAttribute 存数据的操作。  
  23.    */  
  24.   @RequestMapping("/user")   
  25.   @ModelAttribute  
  26.   public User getUser() {   
  27.     return new User(18"Jack""计算机");   
  28.   }   
  29.      
  30.   /**  
  31.    * 这里将 @ModelAttribute 标注在方法参数上,表示要从模型数据里取 key 为 "user" 的对象,绑定在方法  
  32.    * 参数上。如果这样做的话,其实你是得不到上面的那个请求放入的 User 对象,得到的是另外一个对象。其实  
  33.    * 也好理解,这是两个互相独立的请求,作用域不一样。要想达到我们的目的,即能够从模型数据里取数据,需要  
  34.    * 求助于 @SessionAttributes  
  35.    */  
  36.   @RequestMapping("/user2")   
  37.   public String showUser(@ModelAttribute User user) {   
  38.     System.out.println(user);   
  39.     return null;   
  40.   }   
  41. }  
  1. /** 
  2.  * @ModelAttribute 可以为视图渲染提供更多的模型数据,而不需要在处理请求的方法里添加 ModelMap 或 
  3.  * Model 类型的参数。 
  4.  * 
  5.  * @ModelAttribute 可以标注在方法(存数据)上,也可以标注在方法参数(取数据)上。 
  6.  */  
  7. @Controller  
  8. @RequestMapping("/my")  
  9. public class MyController {  
  10.   
  11.   /** 
  12.    * 在处理该请求时,方法的返回类型是 User,貌似不符合返回类型的规范。由于这里使用了 @ModelAttribute 
  13.    * 注解,表示将返回的对象以 "user" 为 key 放入模型数据里。这里的 key 值默认值是返回的数据类型首字母 
  14.    * 小写的结果。如果想自定义 key,可以写成 @ModelAttribute("myAttribute"),那么模型数据将会将  
  15.    * User 对象绑定到 key 为 "myAttribute" 上。 
  16.    *  
  17.    * jsp 里可以这样访问模型里的数据: 
  18.    *   age: ${user.age} 
  19.    *   name: ${user.name} 
  20.    *   job: ${user.job} 
  21.    * 
  22.    * 当然,这里只是提到了 @ModelAttribute 存数据的操作。 
  23.    */  
  24.   @RequestMapping("/user")  
  25.   @ModelAttribute  
  26.   public User getUser() {  
  27.     return new User(18"Jack""计算机");  
  28.   }  
  29.     
  30.   /** 
  31.    * 这里将 @ModelAttribute 标注在方法参数上,表示要从模型数据里取 key 为 "user" 的对象,绑定在方法 
  32.    * 参数上。如果这样做的话,其实你是得不到上面的那个请求放入的 User 对象,得到的是另外一个对象。其实 
  33.    * 也好理解,这是两个互相独立的请求,作用域不一样。要想达到我们的目的,即能够从模型数据里取数据,需要 
  34.    * 求助于 @SessionAttributes 
  35.    */  
  36.   @RequestMapping("/user2")  
  37.   public String showUser(@ModelAttribute User user) {  
  38.     System.out.println(user);  
  39.     return null;  
  40.   }  
  41. }  

● @SessionAttributes
Java代码 复制代码 
  1. /**  
  2.  * @SessionAttributes 和 @ModelAttribute 类似,只不过 @SessionAttributes 是将数据存放于 session   
  3.  * 中或从 session 中取数据。  
  4.  *  
  5.  * @SessionAttributes 只能应用在类型声明上。比如下面的类的声明中,只有属性名为 "the-attribute" 的数  
  6.  * 据才会纳入到 session 的管理。  
  7.  *  
  8.  * @SessionAttributes 允许以属性名名称或者类型两种方法,来表明将哪些数据通过 session 进行管理。这里  
  9.  * 我们使用的是指定属性名称的方式,但通过类型来指定也是可行的,如:  
  10.  *   @SessionAttributes(types=User.class)  
  11.  */  
  12. @Controller  
  13. @RequestMapping("/my")   
  14. @SessionAttributes("the-attribute")   
  15. public class MyController {   
  16.   
  17.   @RequestMapping("/getUser")   
  18.   public String getUser(int userId, Model model) {   
  19.     /**  
  20.      * 注意,这里将 User 对象添加到属性名为 "the-attribute" 上,所以 User 对象将纳入到 session 的  
  21.      * 管理。如果这里添加的对象的属性名不是 "the-attribute",那么它只会作用于当前请求,而不会纳入到   
  22.      * session 的管理中。  
  23.      */  
  24.     User user = userService.getUserById(userId);   
  25.     model.addAtrribute("the-attribute", user);   
  26.     return "userinfo";   
  27.   }   
  28.      
  29.   /**  
  30.    * 将模型里的 "the-attribute" 为 key 的对象绑定到 User 类上。由于在类级别上声明了只有 "the-  
  31.    * attribute" 的属性名才会纳入到 session 的管理,所以就解决了在 @ModelAttribute 注解中讲解中最  
  32.    * 后提到的问题。  
  33.    *  
  34.    * 另外,这个方法还有两个参数,BindingResult 和 SessionStatus。由于这里有绑定数据的动作,我们可以  
  35.    * 根据 BindingResult 对象获得数据绑定结果以决定后继流程该如何处理。SessionStatus 在这里用于处理  
  36.    * 完请求后,清空 session 里的数据。  
  37.    */  
  38.   @RequestMapping("/updateUser")   
  39.   public String updateUser(@ModelAttribute("the-attribute") User user,    
  40.             BindingResult result, SessionStatus status) {   
  41.   
  42.     if (result.hasErrors) {   
  43.       return "error";   
  44.     }   
  45.        
  46.     userService.updateUser(user);   
  47.     // 我们通过调用 status.setComplete() 方法,该 Controller 所有放在 session 级别的模型属性数据   
  48.     // 将从 session 中清空   
  49.     status.setComplete();   
  50.     return "redirect:getUser?userId=" + user.getId();   
  51.   }   
  52. }  
  1. /** 
  2.  * @SessionAttributes 和 @ModelAttribute 类似,只不过 @SessionAttributes 是将数据存放于 session  
  3.  * 中或从 session 中取数据。 
  4.  * 
  5.  * @SessionAttributes 只能应用在类型声明上。比如下面的类的声明中,只有属性名为 "the-attribute" 的数 
  6.  * 据才会纳入到 session 的管理。 
  7.  * 
  8.  * @SessionAttributes 允许以属性名名称或者类型两种方法,来表明将哪些数据通过 session 进行管理。这里 
  9.  * 我们使用的是指定属性名称的方式,但通过类型来指定也是可行的,如: 
  10.  *   @SessionAttributes(types=User.class) 
  11.  */  
  12. @Controller  
  13. @RequestMapping("/my")  
  14. @SessionAttributes("the-attribute")  
  15. public class MyController {  
  16.   
  17.   @RequestMapping("/getUser")  
  18.   public String getUser(int userId, Model model) {  
  19.     /** 
  20.      * 注意,这里将 User 对象添加到属性名为 "the-attribute" 上,所以 User 对象将纳入到 session 的 
  21.      * 管理。如果这里添加的对象的属性名不是 "the-attribute",那么它只会作用于当前请求,而不会纳入到  
  22.      * session 的管理中。 
  23.      */  
  24.     User user = userService.getUserById(userId);  
  25.     model.addAtrribute("the-attribute", user);  
  26.     return "userinfo";  
  27.   }  
  28.     
  29.   /** 
  30.    * 将模型里的 "the-attribute" 为 key 的对象绑定到 User 类上。由于在类级别上声明了只有 "the- 
  31.    * attribute" 的属性名才会纳入到 session 的管理,所以就解决了在 @ModelAttribute 注解中讲解中最 
  32.    * 后提到的问题。 
  33.    * 
  34.    * 另外,这个方法还有两个参数,BindingResult 和 SessionStatus。由于这里有绑定数据的动作,我们可以 
  35.    * 根据 BindingResult 对象获得数据绑定结果以决定后继流程该如何处理。SessionStatus 在这里用于处理 
  36.    * 完请求后,清空 session 里的数据。 
  37.    */  
  38.   @RequestMapping("/updateUser")  
  39.   public String updateUser(@ModelAttribute("the-attribute") User user,   
  40.             BindingResult result, SessionStatus status) {  
  41.   
  42.     if (result.hasErrors) {  
  43.       return "error";  
  44.     }  
  45.       
  46.     userService.updateUser(user);  
  47.     // 我们通过调用 status.setComplete() 方法,该 Controller 所有放在 session 级别的模型属性数据  
  48.     // 将从 session 中清空  
  49.     status.setComplete();  
  50.     return "redirect:getUser?userId=" + user.getId();  
  51.   }  
  52. }  


        Spring MVC 里的大部分的注解,这里基本上都讲到了。日后随着 Spring 的升级,我也会逐一补充新加的注解。其实,仅凭以上的注解,是可以构建一个足够强大的 RESTFul Webservices 的了。

        这里,补充讲下被标注了 @RequestMapping 注解的请求方法的签名。使用 @RequestMapping 标注的 web 请求处理方法的签名比较灵活,我们几乎可以声明并使用任何类型的方法参数。不过,以下几种类型的方法参数将拥有更多语义,它们均来自框架内部(或者说 AnnotationMethodHandlerAdapter)所管理的对象引用:
  • request/response/session
  • org.springframework.web.context.request.WebRequest。当前处理方法中获得可用的 WebRequest 实例。
  • java.util.Locale。通过相应的 LocalResolver 所返回的对应当前 web 请求的 Locale。
  • java.io.InputStream/java.io.Reader。相当于 request.getInputStream() 或 request.getReader() 所获得的对象引用。
  • java.io.OutputStream/java.io.Writer。相当于 response.getOutputStream() 或 response.getWriter() 所获得的对象引用。
  • java.util.Map/org.springframework.ui.ModelMap。你现在可用对模型数据为所欲为了。
  • org.springframework.validation.Errors/org.springframework.validation.BindingResult。用于对 Command 对象进行数据验证的 Errors 或者 BindingResult 对象。声明这两种类型的方法参数有一个限制,它们的声明必须紧跟着 Command 对象的定义。其它类型的方法参数是没有任何顺序限制的。
  • org.springframework.web.bind.supportt.SessionStatus。SessionStatus 主要用于管理请求处理之后 Session 的状态,比如清除 Session 中的指定的数据。

        基于注解的 Controller 的请求处理方法返回类型可以有如下 4 种形式(当然,前面提到的 @ResponseBody 和 @ModelAttribute 并没下面所描述的返回类型,具体参见上面对各自注解的讲解):
  • org.springframework.web.servlet.ModelAndView。这个不用多说,视图信息和模型信息都能通过它返回。
  • java.lang.String。该类型返回值代表逻辑视图名,模型数据需要以其它形式提供,比如为处理方法声明一个 ModelMap 类型的参数。注意,如果返回 null,并不代表向客户端输出空页面(定向思维惹的祸),这种情况下,框架会从请求路径中提取视图信息。如果返回 null 就是要表示方法内部已处理完请求,也不需要通知页面,就是想仅仅返回空白页面,唉,我还没有想出来咋整。。。反正 writer.write("") 这样写可以,还得声明一个 Writer 类型的方法参数。
  • org.springframework.ui.ModelMap。ModelMap 类型返回值只包含了模型数据信息而没有视图信息,框架将根据请求的路径来提取视图信息。
  • void。没有任何返回值,视图信息将从请求路径中提取,模型数据需要通过其它形式提供。

        String 类型的返回值为 null, 还有返回类型为 ModelMap 和 void,从请求路径中如何提取视图信息呢?框架将截取请求路径中的最后一个 / 后面的内容,并去掉后缀名,剩下来的内容就是视图名。如请求路径为 /spring3/user/welcome,那么视图名是 welcome,/spring3/user/welcome.action 的视图名也是 welcome。

        接下来来讲最后一个部分,请求参数到方法参数的绑定。这个在 @RequestParam 中已经讲过,不过,这里要讲的是绑定复杂的对象。在 @RequestParam 中,我们这样请求,date=2011-01-01 其实是绑定不到 Date 对象的。因为不同的 Locale 处理日期的字符串的表达方式不一样。总之,这部分涉及到字符串到对象的转换,这很像 PropertyEditor,对吧?Spring MVC 中,可以为某个 Controller 定制数据绑定,即在被标注了 @InitBinder 的方法里写绑定逻辑,方法名可以随意,如:
Java代码  
  1. /**  
  2.  * 初始化方法不能有返回值,而且至少应该有一个类型为 org.springframework.web.bind.WebDataBinder 的  
  3.  * 方法参数。同时,一个典型的基于注解的 Controller 的处理方法可以使用的方法参数中,除了 Command 对象  
  4.  * 以及相关的 Errors/BindingResult 对象作为方法的参数外,都可以作为初始化方法的参数。  
  5.  *  
  6.  * 这里,我们没有必要为日期再定制自定义绑定规则,Spring 已经为我们提供了 CustomDateEditor,这里只是演  
  7.  * 示如何提供自定义数据绑定规则。  
  8.  *  
  9.  * 这里的 WebDataBinder,是不是很像 PropertyEditorRegistry?  
  10.  */  
  11. @InitBinder  
  12. public void initBinder(WebDataBinder binder) {   
  13.   binder.registerCustomEditor(Date.classnew PropertyEditorSupport() {   
  14.      
  15.     final SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd");   
  16.        
  17.     @Override  
  18.     public void setAsText(String text) throws IllegalArgumentException {   
  19.       try {   
  20.         Date date = sf.parse(text);   
  21.         setValue(date);   
  22.       } catch (ParseException e) {   
  23.         Date data = sf.parse(text);   
  24.         throw new IllegalArgumentException(e);   
  25.       }   
  26.     }   
  27.   })   
  28. }  
  1. /** 
  2.  * 初始化方法不能有返回值,而且至少应该有一个类型为 org.springframework.web.bind.WebDataBinder 的 
  3.  * 方法参数。同时,一个典型的基于注解的 Controller 的处理方法可以使用的方法参数中,除了 Command 对象 
  4.  * 以及相关的 Errors/BindingResult 对象作为方法的参数外,都可以作为初始化方法的参数。 
  5.  * 
  6.  * 这里,我们没有必要为日期再定制自定义绑定规则,Spring 已经为我们提供了 CustomDateEditor,这里只是演 
  7.  * 示如何提供自定义数据绑定规则。 
  8.  * 
  9.  * 这里的 WebDataBinder,是不是很像 PropertyEditorRegistry? 
  10.  */  
  11. @InitBinder  
  12. public void initBinder(WebDataBinder binder) {  
  13.   binder.registerCustomEditor(Date.classnew PropertyEditorSupport() {  
  14.     
  15.     final SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd");  
  16.       
  17.     @Override  
  18.     public void setAsText(String text) throws IllegalArgumentException {  
  19.       try {  
  20.         Date date = sf.parse(text);  
  21.         setValue(date);  
  22.       } catch (ParseException e) {  
  23.         Date data = sf.parse(text);  
  24.         throw new IllegalArgumentException(e);  
  25.       }  
  26.     }  
  27.   })  
  28. }  

        在 Controller 里使用 @InitBinder 标注的初始化方法只能对一个 Controller 对应的 WebBinder 做定制。如果想在整个应用中共享绑定规则,可以为 AnnotationMethodHandlerAdapter 指定一个自定义的 org.springframework.web.bind.support.WebBindingInitializer 实例,这样可以避免在每个 Controller 中都重复定义几乎相同逻辑的 @InitBinder 的初始化方法。
Java代码  
  1. public class MyBindingInitializer implements WebBindingInitializer {   
  2.   
  3.   public void initBinder(WebBinder binder, WebRequest request) {   
  4.     binder.registerCustomEditor(SomeDataType.class, somePropertyEditor)   
  5.     // 如果需要,这里可以继续注册更多的 propertyEditor   
  6.     // ......   
  7.   }   
  8. }  
  1. public class MyBindingInitializer implements WebBindingInitializer {  
  2.   
  3.   public void initBinder(WebBinder binder, WebRequest request) {  
  4.     binder.registerCustomEditor(SomeDataType.class, somePropertyEditor)  
  5.     // 如果需要,这里可以继续注册更多的 propertyEditor  
  6.     // ......  
  7.   }  
  8. }  

Xml代码 复制代码 
  1. <bean class=""org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter>  
  2.   <property name="webBindingInitializer">  
  3.     <bean class="...MyBindingInitializer" />  
  4.   </property>  
  5. </bean>  
  1. <bean class=""org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter>  
  2.   <property name="webBindingInitializer">  
  3.     <bean class="...MyBindingInitializer" />  
  4.   </property>  
  5. </bean>  

        结束该篇文章前,我们来看几个容易混淆的用于简化开发的配置: <mvc:annotation-driven />, <context:annotation-config/>, <context:component-scan />。

        <mvc:annotation-driven /> 会做以下几件事:
  1. 向 spring 容器中注册 DefaultAnnotationHandlerMapping。
  2. 向 spring 容器中注册 AnnotationMethodHandlerAdapter。
  3. 配置一些 messageconverter。
  4. 解决了 @Controller 注解的使用前提配置,即 HandlerMapping 能够知道谁来处理请求。
        <context:annotation-config /> 会做以下几件事:
  1. 向 spring 容器中注册 AutowiredAnnotationBeanPostProcessor。
  2. 向 spring 容器中注册 CommonAnnotationBeanPostProcessor。
  3. 向 spring 容器中注册 PersistenceAnnotationBeanPostProcessor。
  4. 向 spring 容器中注册 RequiredAnnotationBeanPostProcessor。
  5. 使用 <context:annotationconfig />之前,必须在 <beans> 元素中声明 context 命名空间 <context:component-scan />。<context:component-scan /> 对包进行扫描,实现注解驱动 Bean 定义。即,将 @Controller 标识的类的 bean 注册到容器中。
        <context:component-scan/>, 不但启用了对类包进行扫描以实施注解驱动 Bean 定义的功能,同时还启用了注解驱动自动注入的功能(即还隐式地在内部注册了 AutowiredAnnotationBeanPostProcessor 和 CommonAnnotationBeanPostProcessor)。因此当使用 <context:component-scan /> 后,除非需要使用PersistenceAnnotationBeanPostProcessor 和 RequiredAnnotationBeanPostProcessor 两个 Processor 的功能(例如 JPA 等),否则就可以将 <context:annotation-config /> 移除了。
分享到:
评论

相关推荐

    Spring Mvc AOP通过注解方式拦截controller等实现日志管理

    在Spring中,我们通常使用基于注解的AOP,它简化了配置并使代码更易读。 二、注解驱动的AOP 1. 定义切面(Aspect):首先,我们需要创建一个切面类,这个类通常包含通知(Advice),也就是实际的日志记录方法。使用...

    Spring MVC 的注解使用实例

    在Spring MVC框架中,注解的使用极大地简化了配置,提高了开发效率。Spring MVC通过注解可以实现控制器、方法映射、模型数据绑定、视图解析等关键功能。本实例将深入探讨Spring MVC中常见的注解及其应用。 1. `@...

    使用Spring 2.5 基于注解驱动的 Spring MVC详解

    在 Spring 2.5 中,Spring MVC 框架引入了注解驱动功能,使得开发者可以使用注解来配置 Controller,代替传统的基于 XML 的配置方式。这种方式使得 Controller 的开发变得更加灵活和方便。 使用 @Controller 注解 ...

    使用 Spring 2_5 基于注解驱动的 Spring MVC

    在本主题中,我们将深入探讨Spring框架的2.5版本引入的一个重要特性——基于注解的Spring MVC配置。Spring MVC是Spring框架的一部分,专门用于构建Web应用程序,它提供了一个模型-视图-控制器(MVC)架构来组织和...

    学习Spring MVC,关于注解的Spring MVC,简单例子,关于控制器的Spring MVC,学习Spring,简单Spring MVC实例

    在本教程中,我们将深入探讨Spring MVC的核心概念,特别是关于注解的使用以及简单的控制器实现。 首先,Spring MVC的核心组件包括DispatcherServlet、Controller、ModelAndView、ViewResolver等。DispatcherServlet...

    最全的Spring MVC注解例子,异步请求,错误处理

    在这个“最全的Spring MVC注解例子”中,我们将深入探讨Spring MVC的核心注解,以及如何实现异步请求处理和错误管理。 1. **Spring MVC核心注解** - `@Controller`:标记一个类为处理HTTP请求的控制器。这是Spring...

    基于注解Spring MVC环境搭建

    在“基于注解的Spring MVC环境搭建”中,我们将深入探讨如何利用注解来简化配置,快速建立一个运行中的Web项目。这篇博文(尽管描述为空,但提供了链接)很可能是关于创建一个基本的Spring MVC项目并使用注解来管理...

    spring mvc + spring + hibernate 全注解整合开发视频教程 12

    在本教程中,我们将深入探讨如何使用Spring MVC、Spring和Hibernate三大框架进行全注解的整合开发。这个视频教程系列的第12部分,将帮助开发者掌握如何在Java Web项目中高效地集成这三个核心框架,实现松耦合、可...

    Spring MVC实例 MVC注解配置

    在这个实例中,我们将探讨如何利用MVC注解配置来简化Spring MVC应用的设置和管理。MVC模式(Model-View-Controller)是软件设计的一种经典模式,它将业务逻辑、数据和用户界面分离,使得代码更加模块化,易于维护。 ...

    基于注解驱动的 Spring MVC

    8. **异常处理**:Spring MVC 提供了 @ExceptionHandler 注解,允许我们在 Controller 中直接处理异常,提高代码的可读性和可维护性。 9. **视图解析**:在返回字符串 "listBoard" 的情况下,Spring MVC 会根据配置...

    spring mvc + spring + hibernate 全注解整合开发视频教程 11

    使用Spring MVC,开发者可以通过注解来简化配置,例如@Controller、@RequestMapping和@ResponseBody等,这些注解使得控制器类和方法的定义更为直观。 Spring框架则是一个全面的企业级应用开发平台,它不仅包含...

    基于jpa+hibernate+spring+spring mvc注解方式项目

    **基于JPA+Hibernate+Spring+Spring MVC注解方式项目详解** 在现代Java Web开发中,Spring框架扮演了核心角色,而Spring MVC作为其MVC(Model-View-Controller)实现,提供了强大的Web应用程序构建能力。同时,JPA...

    使用 Spring 2.5 基于注解驱动的 Spring MVC.doc

    总结一下,Spring 2.5 中基于注解的 Spring MVC 提供了一种更为简洁、灵活的编程模型,降低了配置的复杂度,提升了开发效率。`@Controller`、`@RequestMapping`、`@Autowired` 和 `@ModelAttribute` 等注解共同构成...

    spring mvc + spring + hibernate 全注解整合开发视频教程 04

    在本视频教程“Spring MVC + Spring + Hibernate 全注解整合开发视频教程 04”中,我们将深入探讨Java企业级开发中的三大核心技术——Spring、Spring MVC和Hibernate的集成与应用,尤其是通过注解实现的简化配置。...

    基于注解的Spring MVC小demo

    2. **@Controller**: 这是Spring MVC中的一个关键注解,标记在类上表示该类是一个处理请求的Controller。Controller类中的方法通常会处理HTTP请求,并返回一个ModelAndView对象,包含视图名和模型数据。 3. **@...

    基于注解的Spring MVC demo

    【基于注解的Spring MVC demo】是一个用于演示和学习如何在Spring MVC框架中使用注解进行Web应用开发的实例。Spring MVC是Spring框架的一部分,它为构建基于Java的Web应用程序提供了一个模型-视图-控制器(MVC)架构...

    使用 Spring 2.5 基于注解驱动的 Spring MVC

    ### 使用 Spring 2.5 基于注解驱动的 Spring MVC #### 概述与背景 自从Spring 2.0版本对Spring MVC框架进行了重大升级之后,Spring 2.5再次对该框架进行了显著改进,引入了注解驱动的功能。这使得开发人员能够更加...

    spring MVC AOP注解方式如何拦截controller 例子

    本文将详细介绍如何使用AspectJ注解在Spring MVC中实现AOP拦截Controller方法,并提供一个具体的例子。 首先,我们需要了解Spring AOP的基础概念。AOP允许我们定义“切面”,这些切面包含了业务逻辑中横切关注点的...

    Spring 2.5 基于注解驱动的Spring MVC

    Spring 2.5引入了一种基于注解的新方式来驱动Spring MVC框架,使得开发者能够更加简洁、直观地配置和管理控制器。这一变化显著提升了开发效率,减少了XML配置文件的复杂性,同时也使得代码更加模块化。 ### 1. 基于...

    Spring mvc 超简完整注解可运行Demo

    Spring MVC 是一个基于 Java 的轻量级 Web 开发框架,它是 Spring 框架的一部分,专为构建 MVC(Model-View-Controller)架构的应用程序而设计。本示例提供了超简化的、完整的、注解驱动的 Spring MVC 应用程序,...

Global site tag (gtag.js) - Google Analytics