当前位置: 代码迷 >> VC/MFC >> Spring MVC 中的基于引语的 Controller
  详细解决方案

Spring MVC 中的基于引语的 Controller

热度:372   发布时间:2016-05-02 03:56:13.0
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,[email protected]描的注解类型 [email protected],不过,[email protected] [email protected],[email protected]@[email protected]gt;??
  2. <context:component-scan?base-package="org.zachary.spring3.anno.web"?/>??
[xml] view plaincopy
  1. <!--?切 记,这不是必需的!除非你把注解的?Controller?一个个的注册到容器中。相信大家还是喜欢用?context:compnent- scan?吧。不要认为在?Spring?MVC?中才提到?context:component-scan,就认为它只能扫 [email protected][email protected],不过,[email protected] [email protected],[email protected]@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?和被注解 [email protected][email protected]的优 先级比定义了其它的?HandlerMapping?的优先级要高(如果定义了其它的话)。 ??
  5. ??如果 没有定义?DefaultAnnotationHandlerMapping,并不代表不能映射到相应的?handler?上。因为如果你定义了其它 的?HandlerMapping,[email protected]正好能匹配上,那么没 有?DefaultAnnotationHandlerMapping,@Controller?一样可以如鱼得水的被捕获到。 ??
  6. ??当然,[email protected][email protected]g。 ??
  7. ??</description>??
  8. </bean>??
[xml] view plaincopy
  1. <bean?class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">??
  2. ??<description>??
  3. ??这点是必需的还是非必需的呢???
  4. ??如 果定义了?DefaultAnnotationHandlerMapping,它就可以将请求来的?url?和被注解 [email protected][email protected]的优 先级比定义了其它的?HandlerMapping?的优先级要高(如果定义了其它的话)。??
  5. ??如 果没有定义?DefaultAnnotationHandlerMapping,并不代表不能映射到相应的?handler?上。因为如果你定义了其它 的?HandlerMapping,[email protected]正好能匹配上,那么没 有?DefaultAnnotationHandlerMapping,@Controller?一样可以如鱼得水的被捕获到。??
  6. ??当然,[email protected][email protected]g。??
  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>??
[xml] view plaincopy
  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. [email protected][email protected]abean,不需要实现任何接口。标注了 ?
  3. [email protected],借助?<context:component-scan>,框架能自动识别到这就是一个?Controller ?
  4. ?*/??
  5. @Controller??
  6. public?class?MyController?{ ??
  7. ??//?...... ??
  8. }??
[java] view plaincopy
  1. /**?
  2. [email protected][email protected]abean,不需要实现任何接口。标注了?
  3. [email protected],借助?<context:component-scan>,框架能自动识别到这就是一个?Controller?
  4. ?*/??
  5. @Controller??
  6. public?class?MyController?{??
  7. ??//?......??
  8. }??

● @RequestMapping
Java代码 复制代码?
  1. /** ?
  2. [email protected],也可以出现在方法上。如果出现在类级别上,那请求的?url?为?类级别 ?
  3. [email protected][email protected][email protected] ?
  4. [email protected] ?
  5. ?*/??
  6. @Controller??
  7. @RequestMapping("/my") ??
  8. public?class?MyController?{ ??
  9. ?? ??
  10. ??/** ?
  11. [email protected],那么想匹配到这个方法来处理请求,url?必须为?/my/somelist。 ?
  12. [email protected],url?为?/somelist?即可。同时,请求方法必须为?POST ?
  13. ???*/??
  14. ??@RequestMapping(value="/somelist",?method=RequestMethod.POST); ??
  15. ??public?String?getSomeList()?{...} ??
  16. ??
  17. ????/** ?
  18. [email protected]求处理的时候,可以指定两个属性。除了我们在上面刚使用过的 ?
  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. [email protected],它和?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. }??
[java] view plaincopy
  1. /**?
  2. [email protected],也可以出现在方法上。如果出现在类级别上,那请求的?url?为?类级别?
  3. [email protected][email protected][email protected]?
  4. [email protected]?
  5. ?*/??
  6. @Controller??
  7. @RequestMapping("/my")??
  8. public?class?MyController?{??
  9. ????
  10. ??/**?
  11. [email protected],那么想匹配到这个方法来处理请求,url?必须为?/my/somelist。?
  12. [email protected],url?为?/somelist?即可。同时,请求方法必须为?POST?
  13. ???*/??
  14. ??@RequestMapping(value="/somelist",?method=RequestMethod.POST);??
  15. ??public?String?getSomeList()?{...}??
  16. ??
  17. ????/**?
  18. [email protected]求处理的时候,可以指定两个属性。除了我们在上面刚使用过的?
  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. [email protected],它和?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. [email protected] ?
  18. ???*/??
  19. ??@RequestMapping("/test") ??
  20. ??public?String?test(int?userId)?{?...?} ??
  21. ??
  22. ??/** ?
  23. ???*?当我们不想使用?userId?作为方法的参数名,即不想使用默认的数据绑定方式。如果我们要使用?id?作为方法 ?
  24. ???*?的参数,为了保证名称为?userId?的请求参数可以绑定到新的名称为?id?的方法参数上,我们就可以使用? ?
  25. [email protected]@RequestParam?只可以标注于方法参数上。 ?
  26. ???* ?
  27. ???*?如果请求参数中有?age,和方法的参数名称一致,[email protected]?递 ?
  28. ???*?age,我们又不想定义成?Integer,很显然框架会注入?null?值,[email protected] ?
  29. ???*?的?required?属性决定的,默认就是?true。如果我们定义成?false, ?
  30. [email protected](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. }??
[java] view plaincopy
  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. [email protected]?
  18. ???*/??
  19. ??@RequestMapping("/test")??
  20. ??public?String?test(int?userId)?{?...?}??
  21. ??
  22. ??/**?
  23. ???*?当我们不想使用?userId?作为方法的参数名,即不想使用默认的数据绑定方式。如果我们要使用?id?作为方法?
  24. ???*?的参数,为了保证名称为?userId?的请求参数可以绑定到新的名称为?id?的方法参数上,我们就可以使用??
  25. [email protected]@RequestParam?只可以标注于方法参数上。?
  26. ???*?
  27. ???*?如果请求参数中有?age,和方法的参数名称一致,[email protected]?递?
  28. ???*?age,我们又不想定义成?Integer,很显然框架会注入?null?值,[email protected]?
  29. ???*?的?required?属性决定的,默认就是?true。如果我们定义成?false,?
  30. [email protected](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. [email protected][email protected],这是?Spring?3.0?之后引入的。 ?
  7. ???* ?
  8. ???*?在这个例子中,请求的?url?必须满足类似?/my/user/zhangsan/18?这样的格式才能匹配方法。url?模板里 ?
  9. [email protected]stParam?类似,这里就不再赘述了。 ?
  10. ???* ?
  11. [email protected]@RequestParam?的区别在于: ?
  12. [email protected]:/my//user/zhangsan/18 ?
  13. [email protected]:/my//user?nickname=zhangsan&age=18 ?
  14. ???*/??
  15. ??@RequestMapping("/user/{nickname}/{age}"); ??
  16. ??public?String?getUserInfo(@PathVariable("nickname")?String?name,?@PathVariable?int?age)?{...} ??
  17. }??
[java] view plaincopy
  1. @Controller??
  2. @RequestMapping("/my")??
  3. public?class?MyController?{??
  4. ??
  5. ??/**?
  6. [email protected][email protected],这是?Spring?3.0?之后引入的。?
  7. ???*?
  8. ???*?在这个例子中,请求的?url?必须满足类似?/my/user/zhangsan/18?这样的格式才能匹配方法。url?模板里?
  9. [email protected]stParam?类似,这里就不再赘述了。?
  10. ???*?
  11. [email protected]@RequestParam?的区别在于:?
  12. [email protected]:/my//user/zhangsan/18?
  13. [email protected]:/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,[email protected],那这部分我们只讲? ?
  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. }??
[java] view plaincopy
  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,[email protected],那这部分我们只讲??
  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. [email protected]?求正文到对象转换的方向,现在来讲讲另外一个方 ?
  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,[email protected]??。但它仍然是合法的,因 ?
  12. [email protected],此注解将告知框架,将?User?对象作为影响正文返回?什么?对象 ?
  13. ???*?作为响应正文!所以,HttpMessageConverter?在这里就起到作用了。这里讨论的[email protected],所以 ?
  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. }??
[java] view plaincopy
  1. /**?
  2. [email protected]?求正文到对象转换的方向,现在来讲讲另外一个方?
  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,[email protected]??。但它仍然是合法的,因?
  12. [email protected],此注解将告知框架,将?User?对象作为影响正文返回?什么?对象?
  13. ???*?作为响应正文!所以,HttpMessageConverter?在这里就起到作用了。这里讨论的[email protected],所以?
  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. [email protected]??据,而不需要在处理请求的方法里添加?ModelMap?或 ?
  3. ?*?Model?类型的参数。 ?
  4. ?* ?
  5. [email protected](存数据)上,也可以标注在方法参数(取数据)上。 ?
  6. ?*/??
  7. @Controller??
  8. @RequestMapping("/my") ??
  9. public?class?MyController?{ ??
  10. ??
  11. ??/** ?
  12. ???*?在处理该请求时,方法的返回类型是?User,[email protected]delAttribute ?
  13. ???*?注解,表示将返回的对象以?"user"?为?key?放入模型数据里。这里的?key?值默认值是返回的数据类型首字母 ?
  14. ???*?小写的结果。如果想自定义?key,[email protected]("myAttribute"),那么模型数据将会将? ?
  15. ???*?User?对象绑定到?key?为?"myAttribute"?上。 ?
  16. ???*? ?
  17. ???*?jsp?里可以这样访问模型里的数据: ?
  18. ???*???age:?${user.age} ?
  19. ???*???name:?${user.name} ?
  20. ???*???job:?${user.job} ?
  21. ???* ?
  22. ???*?当然,[email protected] ?
  23. ???*/??
  24. ??@RequestMapping("/user") ??
  25. ??@ModelAttribute??
  26. ??public?User?getUser()?{ ??
  27. ????return?new?User(18,?"Jack",?"计算机"); ??
  28. ??} ??
  29. ?? ??
  30. ??/** ?
  31. [email protected],表示要从模型数据里取?key?为?"user"?的对象,绑定在方法 ?
  32. ???*?参数上。如果这样做的话,其实你是得不到上面的那个请求放入的?User?对象,得到的是另外一个对象。其实 ?
  33. ???*?也好理解,这是两个互相独立的请求,作用域不一样。要想达到我们的目的,即能够从模型数据里取数据,需要 ?
  34. [email protected] ?
  35. ???*/??
  36. ??@RequestMapping("/user2") ??
  37. ??public?String?showUser(@ModelAttribute?User?user)?{ ??
  38. ????System.out.println(user); ??
  39. ????return?null; ??
  40. ??} ??
  41. }??
[java] view plaincopy
  1. /**?
  2. [email protected]??据,而不需要在处理请求的方法里添加?ModelMap?或?
  3. ?*?Model?类型的参数。?
  4. ?*?
  5. [email protected](存数据)上,也可以标注在方法参数(取数据)上。?
  6. ?*/??
  7. @Controller??
  8. @RequestMapping("/my")??
  9. public?class?MyController?{??
  10. ??
  11. ??/**?
  12. ???*?在处理该请求时,方法的返回类型是?User,[email protected]delAttribute?
  13. ???*?注解,表示将返回的对象以?"user"?为?key?放入模型数据里。这里的?key?值默认值是返回的数据类型首字母?
  14. ???*?小写的结果。如果想自定义?key,[email protected]("myAttribute"),那么模型数据将会将??
  15. ???*?User?对象绑定到?key?为?"myAttribute"?上。?
  16. ???*??
  17. ???*?jsp?里可以这样访问模型里的数据:?
  18. ???*???age:?${user.age}?
  19. ???*???name:?${user.name}?
  20. ???*???job:?${user.job}?
  21. ???*?
  22. ???*?当然,[email protected]?
  23. ???*/??
  24. ??@RequestMapping("/user")??
  25. ??@ModelAttribute??
  26. ??public?User?getUser()?{??
  27. ????return?new?User(18,?"Jack",?"计算机");??
  28. ??}??
  29. ????
  30. ??/**?
  31. [email protected],表示要从模型数据里取?key?为?"user"?的对象,绑定在方法?
  32. ???*?参数上。如果这样做的话,其实你是得不到上面的那个请求放入的?User?对象,得到的是另外一个对象。其实?
  33. ???*?也好理解,这是两个互相独立的请求,作用域不一样。要想达到我们的目的,即能够从模型数据里取数据,需要?
  34. [email protected]?
  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. [email protected]@ModelAttribute?类似,[email protected] ?
  3. ?*?中或从?session?中取数据。 ?
  4. ?* ?
  5. [email protected]??面的类的声明中,只有属性名为?"the-attribute"?的数 ?
  6. ?*?据才会纳入到?session?的管理。 ?
  7. ?* ?
  8. [email protected]??方法,来表明将哪些数据通过?session?进行管理。这里 ?
  9. ?*?我们使用的是指定属性名称的方式,但通过类型来指定也是可行的,如: ?
  10. [email protected](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?的管理,[email protected] ?
  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. }??
[java] view plaincopy
  1. /**?
  2. [email protected]@ModelAttribute?类似,[email protected]?
  3. ?*?中或从?session?中取数据。?
  4. ?*?
  5. [email protected]??面的类的声明中,只有属性名为?"the-attribute"?的数?
  6. ?*?据才会纳入到?session?的管理。?
  7. ?*?
  8. [email protected]??方法,来表明将哪些数据通过?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.class,?new?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. }??
[java] view plaincopy
  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.class,?new?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. }??
[java] view plaincopy
  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>??
[xml] view plaincopy
  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 /> 移除了。
  相关解决方案