专注于互联网--专注于架构

最新标签
网站地图
文章索引
Rss订阅

首页 »Java教程 » springmvc:使用 Spring 2.5 基于注解驱动的 Spring MVC »正文

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

来源: 发布时间:星期四, 2008年12月18日 浏览:213次 评论:0
=atitle>概述

继 Spring 2.0 对 Spring MVC 进行重大升级后Spring 2.5 又为 Spring MVC 引入了注解驱动功能现在你无须让 Controller 继承任何接口无需在 XML 配置文件中定义请求和 Controller 映射关系仅仅使用注解就可以让个 POJO 具有 Controller 绝大部分功能 —— Spring MVC 框架易用性得到了进增强.在框架灵活性、易用性和扩展性上Spring MVC 已经全面超越了其它 MVC 框架伴随着 Spring 路高唱猛进可以预见 Spring MVC 在 MVC 市场上吸引力将越来越不可抗拒

本文将介绍 Spring 2.5 新增 Sping MVC 注解功能讲述如何使用注解配置替换传统基于 XML Spring MVC 配置







回页首





=atitle>个简单基于注解 Controller

使用过低版本 Spring MVC 读者都知道:当创建个 Controller 时我们需要直接或间接地实现 org.springframework.web.servlet.mvc.Controller 接口般情况下我们是通过继承 SimpleFormController 或 MultiActionController 来定义自己 Controller 在定义 Controller 后个重要事件是在 Spring MVC 配置文件中通过 HandlerMapping 定义请求和控制器映射关系以便将两者关联起来

来看下基于注解 Controller 是如何定义做到这下面是使用注解 BbtForumController:


清单 1. BbtForumController.java
package com.baobaotao.web; import com.baobaotao.service.BbtForumService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import java.util.Collection; @Controller //<——① @RequestMapping("/forum.do") public BbtForumController { @Autowired private BbtForumService bbtForumService; @RequestMapping //<——② public String listAllBoard { bbtForumService.getAllBoard; .out.prln("call listAllBoard method."); "listBoard"; } }




从上面代码中我们可以看出 BbtForumController 和类并没有区别它没有实现任何特殊接口因而是个地道 POJO让这个 POJO 和众区别魔棒就是 Spring MVC 注解!

在 ① 处使用了两个注解分别是 @Controller 和 @RequestMapping在“使用 Spring 2.5 基于注解驱动 IoC”这篇文章里笔者曾经指出过 @Controller、@Service 以及 @Repository 和 @Component 注解作用是等价:将个类成为 Spring 容器 Bean由于 Spring MVC Controller 必须事先是个 Bean所以 @Controller 注解是不可缺少

真正让 BbtForumController 具备 Spring MVC Controller 功能是 @RequestMapping 这个注解@RequestMapping 可以标注在类定义处将 Controller 和特定请求关联起来;还可以标注在思路方法签名处以便进步对请求进行分流在 ① 处我们让 BbtForumController 关联“/forum.do”请求而 ② 处我们具体地指定 listAllBoard 思路方法来处理请求所以在类声明处标注 @RequestMapping 相当于让 POJO 实现了 Controller 接口而在思路方法定义处 @RequestMapping 相当于让 POJO 扩展 Spring 预定义 Controller(如 SimpleFormController 等)

为了让基于注解 Spring MVC 真正工作起来需要在 Spring MVC 对应 xxx-servlet.xml 配置文件中做些手脚在此的前还是先来看下 web.xml 配置吧:


清单 2. web.xml:启用 Spring 容器和 Spring MVC 框架
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5"> <display-name>Spring Annotation MVC Sample</display-name> <!-- Spring 服务层配置文件 --> <context-param> <param-name>contextConfigLocation</param-name> <param-value>path:applicationContext.xml</param-value> </context-param> <!-- Spring 容器启动监听器 --> <listener> <listener->org.springframework.web.context.ContextLoaderListener </listener-> </listener> <!-- Spring MVC Servlet它将加载WEB-INF/annomvc-servlet.xml 配置文件以启动Spring MVC模块--> <servlet> <servlet-name>annomvc</servlet-name> <servlet->org.springframework.web.servlet.DispatcherServlet </servlet-> <load-on-startup>2</load-on-startup> </servlet> <servlet-mapping> <servlet-name>annomvc</servlet-name> <url-pattern>*.do</url-pattern> </servlet-mapping> </web-app>




web.xml 中定义了个名为 annomvc Spring MVC 模块按照 Spring MVC 契约需要在 WEB-INF/annomvc-servlet.xml 配置文件中定义 Spring MVC 模块具体配置annomvc-servlet.xml 配置内容如下所示:

=atitle>清单 3. annomvc-servlet.xml

<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd"> <!-- ①:对web包中所有类进行扫描以完成Bean创建和自动依赖注入功能 --> <context:component-scan base-package="com.baobaotao.web"/> <!-- ②:启动Spring MVC注解功能完成请求和注解POJO映射 --> <bean ="org.springframework.web.servlet.mvc.annotation. AnnotationMethodHandlerAdapter"/> <!-- ③:对模型视图名称解析即在模型视图名称添加前后缀 --> <bean ="org.springframework.web.servlet.view.InternalResourceViewResolver" p:prefix="/WEB-INF/jsp/" p:suffix=".jsp"/> </beans>




Spring 所有功能都在 Bean 基础上演化而来所以必须事先将 Controller 变成 Bean这是通过在类中标注 @Controller 并在 annomvc-servlet.xml 中启用组件扫描机制来完成如 ① 所示

在 ② 处配置了个 AnnotationMethodHandlerAdapter它负责根据 Bean 中 Spring MVC 注解对 Bean 进行加工处理使这些 Bean 变成控制器并映射特定 URL 请求

而 ③ 处工作是定义模型视图名称解析规则这里我们使用了 Spring 2.5 特殊命名空间即 p 命名空间它将原先需要通过 <property> 元素配置内容转化为 <bean> 属性配置定程度上简化了 <bean> 配置

启动 Tomcat发送 http://localhost/forum.do URL 请求BbtForumController listAllBoard 思路方法将响应这个请求并转向 WEB-INF/jsp/listBoard.jsp 视图页面







回页首





=atitle>让个 Controller 处理多个 URL 请求

在低版本 Spring MVC 中我们可以通过继承 MultiActionController 让个 Controller 处理多个 URL 请求使用 @RequestMapping 注解后这个功能更加容易实现了请看下面代码:

=atitle>清单 3. 每个请求处理参数对应个 URL

package com.baobaotao.web; import com.baobaotao.service.BbtForumService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; @Controller public BbtForumController { @Autowired private BbtForumService bbtForumService; @RequestMapping("/listAllBoard.do") // <—— ① public String listAllBoard { bbtForumService.getAllBoard; .out.prln("call listAllBoard method."); "listBoard"; } @RequestMapping("/listBoardTopic.do") // <—— ② public String listBoardTopic( topicId) { bbtForumService.getBoardTopics(topicId); .out.prln("call listBoardTopic method."); "listTopic"; } }




在这里我们分别在 ① 和 ② 处为 listAllBoard 和 listBoardTopic 思路方法标注了 @RequestMapping 注解分别指定这两个思路方法处理 URL 请求这相当于将 BbtForumController 改造为 MultiActionController这样 /listAllBoard.do URL 请求将由 listAllBoard 负责处理而 /listBoardTopic.do?topicId=1 URL 请求则由 listBoardTopic 思路方法处理

对于处理多个 URL 请求 Controller 来说我们倾向于通过个 URL 参数指定 Controller 处理思路方法名称(如 method=listAllBoard)而非直接通过区别 URL 指定 Controller 处理思路方法使用 @RequestMapping 注解很容易实现这个常用需求来看下面代码:


清单 4. 个 Controller 对应个 URL由请求参数决定请求处理思路方法
package com.baobaotao.web; import com.baobaotao.service.BbtForumService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; @Controller @RequestMapping("/bbtForum.do") // <—— ① 指定控制器对应URL请求 public BbtForumController { @Autowired private BbtForumService bbtForumService; // <—— ② 如果URL请求中包括"method=listAllBoard"参数由本思路方法进行处理 @RequestMapping(params = "method=listAllBoard") public String listAllBoard { bbtForumService.getAllBoard; .out.prln("call listAllBoard method."); "listBoard"; } // <—— ③ 如果URL请求中包括"method=listBoardTopic"参数由本思路方法进行处理 @RequestMapping(params = "method=listBoardTopic") public String listBoardTopic( topicId) { bbtForumService.getBoardTopics(topicId); .out.prln("call listBoardTopic method."); "listTopic"; } }




在类定义处标注 @RequestMapping 让 BbtForumController 处理所有包含 /bbtForum.do URL 请求而 BbtForumController 中请求处理思路方法对 URL 请求分流规则在 ② 和 ③ 处定义分流规则按照 URL method 请求参数确定所以分别在类定义处和思路方法定义处使用 @RequestMapping 注解就可以很容易通过 URL 参数指定 Controller 处理思路方法了

@RequestMapping 注解中除了 params 属性外还有个常用属性是 method它可以让 Controller 思路方法处理特定 HTTP 请求方式请求如让个思路方法处理 HTTP GET 请求而另个思路方法处理 HTTP POST 请求如下所示:


清单 4. 让请求处理思路方法处理特定 HTTP 请求思路方法
package com.baobaotao.web; import com.baobaotao.service.BbtForumService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; @Controller @RequestMapping("/bbtForum.do") public BbtForumController { @RequestMapping(params = "method=createTopic",method = RequestMethod.POST) public String createTopic{ .out.prln("call createTopic method."); "createTopic"; } }




这样只有当 /bbtForum.do?method=createTopic 请求以 HTTP POST 方式提交时createTopic 思路方法才会进行处理







回页首





=atitle>处理思路方法入参如何绑定 URL 参数

=smalltitle>按契约绑定

Controller 思路方法标注了 @RequestMapping 注解后它就能处理特定 URL 请求我们不禁要问:请求处理思路方法入参是如何绑定 URL 参数呢?在回答这个问题的前先来看下面代码:

=atitle>清单 5. 按参数名匹配进行绑定

@RequestMapping(params = "method=listBoardTopic") //<—— ① topicId入参是如何绑定URL请求参数? public String listBoardTopic( topicId) { bbtForumService.getBoardTopics(topicId); .out.prln("call listBoardTopic method."); "listTopic"; }




当我们发送 http://localhost//bbtForum.do?method=listBoardTopic&topicId=10 URL 请求时Spring 不但让 listBoardTopic 思路方法处理这个请求而且还将 topicId 请求参数在类型转换后绑定到 listBoardTopic 思路方法 topicId 入参上而 listBoardTopic 思路方法返回类型是 String它将被解析为逻辑视图名称也就是说 Spring 在如何给处理思路方法入参自动赋值以及如何将处理思路方法返回值转化为 ModelAndView 中过程中存在套潜在规则不熟悉这个规则就不可能很好地开发基于注解请求处理思路方法因此了解这个潜在规则无疑成为理解 Spring MVC 框架基于注解功能核心问题

我们不妨从最常见开始说起:请求处理思路方法入参类型可以是 Java 基本数据类型或 String 类型这时思路方法入参按参数名匹配原则绑定到 URL 请求参数同时还自动完成 String 类型 URL 请求参数到请求处理思路方法参数类型转换下面给出几个例子:

  • listBoardTopic( topicId):和 topicId URL 请求参数绑定;
  • listBoardTopic( topicId,String boardName):分别和 topicId、boardName URL 请求参数绑定;
 特别如果入参是基本数据类型(如 、long、float 等)URL 请求参数中定要有对应参数否则将抛出 TypeMismatchException 异常提示无法将 null 转换为基本数据类型

另外请求处理思路方法入参也可以个 JavaBean如下面 User 对象就可以作为个入参:


清单 6. User.java:个 JavaBean
package com.baobaotao.web; public User { private userId; private String userName; //省略get/ter思路方法 public String toString{ this.userName +","+this.userId; } }




下面是将 User 作为 listBoardTopic 请求处理思路方法入参:


清单 7. 使用 JavaBean 作为请求处理思路方法入参
@RequestMapping(params = "method=listBoardTopic") public String listBoardTopic( topicId,User user) { bbtForumService.getBoardTopics(topicId); .out.prln("topicId:"+topicId); .out.prln("user:"+user); .out.prln("call listBoardTopic method."); "listTopic"; }




这时如果我们使用以下 URL 请求:http://localhost/bbtForum.do?method=listBoardTopic&topicId=1&userId=10&userName=tom

topicId URL 参数将绑定到 topicId 入参上而 userId 和 userName URL 参数将绑定到 user 对象 userId 和 userName 属性中和 URL 请求中不允许没有 topicId 参数区别虽然 User userId 属性类型是基本数据类型但如果 URL 中不存在 userId 参数Spring 也不会报错此时 user.userId 值为 0如果 User 对象拥有个 dept.deptId 级联属性那么它将和 dept.deptId URL 参数绑定

=smalltitle>通过注解指定绑定 URL 参数

如果我们想改变这种默认按名称匹配策略比如让 listBoardTopic( topicId,User user) 中 topicId 绑定到 id 这个 URL 参数那么可以通过对入参使用 @RequestParam 注解来达到目:

=atitle>清单 8. 通过 @RequestParam 注解指定

package com.baobaotao.web; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; … @Controller @RequestMapping("/bbtForum.do") public BbtForumController { @RequestMapping(params = "method=listBoardTopic") public String listBoardTopic(@RequestParam("id") topicId,User user) { bbtForumService.getBoardTopics(topicId); .out.prln("topicId:"+topicId); .out.prln("user:"+user); .out.prln("call listBoardTopic method."); "listTopic"; } … }




这里对 listBoardTopic 请求处理思路方法 topicId 入参标注了 @RequestParam("id") 注解所以它将和 id URL 参数绑定

=smalltitle>绑定模型对象中某个属性

Spring 2.0 定义了个 org.springframework.ui.ModelMap 类它作为通用模型数据承载对象传递数据供视图所用我们可以在请求处理思路方法中声明个 ModelMap 类型入参Spring 会将本次请求模型对象引用通过该入参传递进来这样就可以在请求处理思路方法内部访问模型对象了来看下面例子:


清单 9. 使用 ModelMap 访问请示对应隐含模型对象
@RequestMapping(params = "method=listBoardTopic") public String listBoardTopic(@RequestParam("id") topicId, User user,ModelMap model) { bbtForumService.getBoardTopics(topicId); .out.prln("topicId:" + topicId); .out.prln("user:" + user); //① 将user对象以currUser为键放入到model中 model.addAttribute("currUser",user); "listTopic"; }




对于当次请求所对应模型对象来说其所有属性都将存放到 request 属性列表中象上面例子ModelMap 中 currUser 属性将放到 request 属性列表中所以可以在 JSP 视图页面中通过 request.getAttribute(“currUser”) 或者通过 ${currUser} EL 表达式访问模型对象中 user 对象从这个角度上看 ModelMap 相当于是个向 request 属性列表中添加对象条管道借由 ModelMap 对象支持我们可以在个不依赖 Servlet API Controller 中向 request 中添加属性

在默认情况下ModelMap 中属性作用域是 request 级别是也就是说当本次请求结束后ModelMap 中属性将销毁如果希望在多个请求中共享 ModelMap 中属性必须将其属性转存到 session 中这样 ModelMap 属性才可以被跨请求访问

Spring 允许我们有选择地指定 ModelMap 中哪些属性需要转存到 session 中以便下个请求属对应 ModelMap 属性列表中还能访问到这些属性功能是通过类定义处标注 @SessionAttributes 注解来实现请看下面代码:


清单 10. 使模型对象特定属性具有 Session 范围作用域
package com.baobaotao.web; … import org.springframework.ui.ModelMap; import org.springframework.web.bind.annotation.SessionAttributes; @Controller @RequestMapping("/bbtForum.do") @SessionAttributes("currUser") //①将ModelMap中属性名为currUser属性 //放到Session属性列表中以便这个属性可以跨请求访问 public BbtForumController { … @RequestMapping(params = "method=listBoardTopic") public String listBoardTopic(@RequestParam("id") topicId, User user, ModelMap model) { bbtForumService.getBoardTopics(topicId); .out.prln("topicId:" + topicId); .out.prln("user:" + user); model.addAttribute("currUser",user); //②向ModelMap中添加个属性 "listTopic"; } }




我们在 ② 处添加了个 ModelMap 属性其属性名为 currUser而 ① 处通过 @SessionAttributes 注解将 ModelMap 中名为 currUser 属性放置到 Session 中所以我们不但可以在 listBoardTopic 请求所对应 JSP 视图页面中通过 request.getAttribute(“currUser”) 和 session.getAttribute(“currUser”) 获取 user 对象还可以在下个请求所对应 JSP 视图页面中通过 session.getAttribute(“currUser”) 或 ModelMap#get(“currUser”) 访问到这个属性

这里我们仅将个 ModelMap 属性放入 Session 中其实 @SessionAttributes 允许指定多个属性你可以通过方式指定多个属性如 @SessionAttributes({“attr1”,”attr2”})此外@SessionAttributes 还可以通过属性类型指定要 session 化 ModelMap 属性如 @SessionAttributes(types = User.)当然也可以指定多个类如 @SessionAttributes(types = {User.,Dept.})还可以联合使用属性名和属性类型指定:@SessionAttributes(types = {User.,Dept.},value={“attr1”,”attr2”})

上面讲述了如何往ModelMap中放置属性以及如何使ModelMap中属性拥有Session域作用范围除了在JSP视图页面中通过传统思路方法访问ModelMap中属性外读者朋友可能会问:是否可以将ModelMap中属性绑定到请求处理思路方法入参中呢?答案是肯定Spring为此提供了个@ModelAttribute注解下面是使用@ModelAttribute注解例子:

=atitle>清单 11. 使模型对象特定属性具有 Session 范围作用域

package com.baobaotao.web; import com.baobaotao.service.BbtForumService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.ModelMap; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.SessionAttributes; import org.springframework.web.bind.annotation.ModelAttribute; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; @Controller @RequestMapping("/bbtForum.do") @SessionAttributes("currUser") //①让ModelMapcurrUser属性拥有session级作用域 public BbtForumController { @Autowired private BbtForumService bbtForumService; @RequestMapping(params = "method=listBoardTopic") public String listBoardTopic(@RequestParam("id") topicId, User user, ModelMap model) { bbtForumService.getBoardTopics(topicId); .out.prln("topicId:" + topicId); .out.prln("user:" + user); model.addAttribute("currUser",user); //②向ModelMap中添加个属性 "listTopic"; } @RequestMapping(params = "method=listAllBoard") //③将ModelMap中 public String listAllBoard(@ModelAttribute("currUser") User user) { //currUser属性绑定到user入参中 bbtForumService.getAllBoard; .out.prln("user:"+user); "listBoard"; } }




在 ② 处我们向 ModelMap 中添加个名为 currUser 属性而 ① 外注解使这个 currUser 属性拥有了 session 级作用域所以我们可以在 ③ 处通过 @ModelAttribute 注解将 ModelMap 中 currUser 属性绑定以请求处理思路方法 user 入参中

所以当我们先以下 URL 请求: http://localhost/bbtForum.do?method=listBoardTopic&id=1&userName=tom&dept.deptId=12

以执行listBoardTopic请求处理思路方法然后再访问以下URL: http://localhost/sample/bbtForum.do?method=listAllBoard

你将可以看到 listAllBoard user 入参已经成功绑定到 listBoardTopic 中注册 session 级 currUser 属性上了







回页首





=atitle>请求处理思路方法签名规约

=smalltitle>思路方法入参

我们知道标注了 @RequestMapping 注解 Controller 思路方法就成为了请求处理思路方法Spring MVC 允许极其灵活请求处理思路方法签名方式对于思路方法入参来说它允许多种类型入参通过下表进行介绍说明:

请求处理思路方法入参可选类型 介绍说明
Java 基本数据类型和 String 默认情况下将按名称匹配方式绑定到 URL 参数上可以通过 @RequestParam 注解改变默认绑定规则
request/response/session 既可以是 Servlet API 也可以是 Portlet API 对应对象Spring 会将它们绑定到 Servlet 和 Portlet 容器相应对象上
org.springframework.web.context.request.WebRequest 内部包含了 request 对象
java.util.Locale 绑定到 request 对应 Locale 对象上
java.io.InputStream/java.io.Reader 可以借此访问 request 内容
java.io.OutputStream / java.io.Writer 可以借此操作 response 内容
任何标注了 @RequestParam 注解入参 被标注 @RequestParam 注解入参将绑定到特定 request 参数上
java.util.Map / org.springframework.ui.ModelMap 它绑定 Spring MVC 框架中每个请求所创建潜在模型对象它们可以被 Web 视图对象访问(如 JSP)
命令/表单对象(注:般称绑定使用 HTTP GET 发送 URL 参数对象为命令对象而称绑定使用 HTTP POST 发送 URL 参数对象为表单对象) 它们属性将以名称匹配规则绑定到 URL 参数上同时完成类型转换而类型转换规则可以通过 @InitBinder 注解或通过 HandlerAdapter 配置进行调整
org.springframework.validation.Errors / org.springframework.validation.BindingResult 为属性列表中命令/表单对象校验结果注意检验结果参数必须紧跟在命令/表单对象后面
rg.springframework.web.bind.support.SessionStatus 可以通过该类型 status 对象显式结束表单处理这相当于触发 session 清除其中通过 @SessionAttributes 定义属性

Spring MVC 框架易用的处在于你可以按任意顺序定义请求处理思路方法入参(除了 Errors 和 BindingResult 必须紧跟在命令对象/表单参数后面以外)Spring MVC 会根据反射机制自动将对应对象通过入参传递给请求处理思路方法这种机制让开发者完全可以不依赖 Servlet API 开发控制层当请求处理思路方法需要特定对象时仅仅需要在参数列表中声明入参即可不需要考虑如何获取这些对象Spring MVC 框架就象个大管家样“不辞辛苦”地为我们准备好了所需下面演示下使用 SessionStatus 例子:


清单 12. 使用 SessionStatus 控制 Session 级别模型属性
@RequestMapping(method = RequestMethod.POST) public String processSubmit(@ModelAttribute Owner owner, BindingResult result, SessionStatus status) {//<——① OwnerValidator.validate(owner, result); (result.hasErrors) { "ownerForm"; } { this.clinic.storeOwner(owner); status.Complete;//<——② "redirect:owner.do?ownerId=" + owner.getId; } }




processSubmit 思路方法中 owner 表单对象将绑定到 ModelMap “owner”属性中result 参数用于存放检验 owner 结果对象而 status 用于控制表单处理状态在 ② 处我们通过 status.Complete 思路方法该 Controller 所有放在 session 级别模型属性数据将从 session 中清空

=smalltitle>思路方法返回参数

在低版本 Spring MVC 中请求处理思路方法返回值类型都必须是 ModelAndView而在 Spring 2.5 中你拥有多种灵活选择通过下表进行介绍说明:

请求处理思路方法入参可选类型 介绍说明
void 此时逻辑视图名由请求处理思路方法对应 URL 确定如以下思路方法:

@RequestMapping("/welcome.do") public void welcomeHandler { }




对应逻辑视图名为“welcome”


String 此时逻辑视图名为返回如以下思路方法:

@RequestMapping(method = RequestMethod.GET) public String upForm(@RequestParam("ownerId") ownerId, ModelMap model) { Owner owner = this.clinic.loadOwner(ownerId); model.addAttribute(owner); "ownerForm"; }




对应逻辑视图名为“ownerForm”


org.springframework.ui.ModelMap 和返回类型为 void 逻辑视图名取决于对应请求 URL如下面例子:

@RequestMapping("/vets.do") public ModelMap vetsHandler { ModelMap(this.clinic.getVets); }




对应逻辑视图名为“vets”返回 ModelMap 将被作为请求对应模型对象可以在 JSP 视图页面中访问到


ModelAndView 当然还可以是传统 ModelAndView

应该说使用 String 作为请求处理思路方法返回值类型是比较通用思路方法这样返回逻辑视图名不会和请求 URL 绑定具有很大灵活性而模型数据又可以通过 ModelMap 控制当然直接使用传统 ModelAndView 也不失为个好选择







回页首





=atitle>注册自己属性编辑器

Spring MVC 有套常用属性编辑器这包括基本数据类型及其包裹类属性编辑器、String 属性编辑器、JavaBean 属性编辑器等但有时我们还需要向 Spring MVC 框架注册些自定义属性编辑器如特定时间格式属性编辑器就是其中

Spring MVC 允许向整个 Spring 框架注册属性编辑器它们对所有 Controller 都有影响当然 Spring MVC 也允许仅向某个 Controller 注册属性编辑器对其它 Controller 没有影响前者可以通过 AnnotationMethodHandlerAdapter 配置做到而后者则可以通过 @InitBinder 注解实现

下面先看向整个 Spring MVC 框架注册自定义编辑器:


清单 13. 注册框架级自定义属性编辑器
>bean ="org.springframework.web.servlet.mvc.annotation. AnnotationMethodHandlerAdapter"< >property name="webBindingInitializer"< >bean ="com.baobaotao.web.MyBindingInitializer"/< >/property< >/bean<




MyBindingInitializer 实现了 WebBindingInitializer 接口在接口思路方法中通过 binder 注册多个自定义属性编辑器其代码如下所示:


清单 14.自定义属性编辑器
package org.springframework.samples.petclinic.web; import java.text.SimpleDateFormat; import java.util.Date; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.propertyeditors.CustomDateEditor; import org.springframework.beans.propertyeditors.StringTrimmerEditor; import org.springframework.samples.petclinic.Clinic; import org.springframework.samples.petclinic.PetType; import org.springframework.web.bind.WebDataBinder; import org.springframework.web.bind.support.WebBindingInitializer; import org.springframework.web.context.request.WebRequest; public MyBindingInitializer implements WebBindingInitializer { public void initBinder(WebDataBinder binder, WebRequest request) { SimpleDateFormat dateFormat = SimpleDateFormat("yyyy-MM-dd"); dateFormat.Lenient(false); binder.registerCustomEditor(Date., CustomDateEditor(dateFormat, false)); binder.registerCustomEditor(String., StringTrimmerEditor(false)); } }




如果希望某个属性编辑器仅作用于特定 Controller可以在 Controller 中定义个标注 @InitBinder 注解思路方法可以在该思路方法中向 Controller 了注册若干个属性编辑器来看下面代码:


清单 15. 注册 Controller 级自定义属性编辑器
@Controller public MyFormController { @InitBinder public void initBinder(WebDataBinder binder) { SimpleDateFormat dateFormat = SimpleDateFormat("yyyy-MM-dd"); dateFormat.Lenient(false); binder.registerCustomEditor(Date., CustomDateEditor(dateFormat, false)); } … }




注意被标注 @InitBinder 注解思路方法必须拥有个 WebDataBinder 类型入参以便 Spring MVC 框架将注册属性编辑器 WebDataBinder 对象传递进来







回页首





=atitle>如何准备数据

在编写 Controller 时常常需要在真正进入请求处理思路方法前准备些数据以便请求处理或视图渲染时使用在传统 SimpleFormController 里是通过复写其 referenceData 思路方法来准备引用数据在 Spring 2.5 时可以将任何个拥有返回值思路方法标注上 @ModelAttribute使其返回值将会进入到模型对象属性列表中来看下面例子:


清单 16. 定义为处理请求准备数据思路方法
package com.baobaotao.web; import com.baobaotao.service.BbtForumService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.ModelMap; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.SessionAttributes; import java.util.ArrayList; import java.util.List; import java.util.Set; @Controller @RequestMapping("/bbtForum.do") public BbtForumController { @Autowired private BbtForumService bbtForumService; @ModelAttribute("items")//<——①向模型对象中添加个名为items属性 public List<String> populateItems { List<String> lists = ArrayList<String>; lists.add("item1"); lists.add("item2"); lists; } @RequestMapping(params = "method=listAllBoard") public String listAllBoard(@ModelAttribute("currUser")User user, ModelMap model) { bbtForumService.getAllBoard; //<——②在此访问模型中items属性 .out.prln("model.items:" + ((List<String>)model.get("items")).size); "listBoard"; } }




在 ① 处通过使用 @ModelAttribute 注解populateItem 思路方法将在任何请求处理思路方法执行前Spring MVC 会将该思路方法返回值以“items”为名放入到隐含模型对象属性列表中

所以在 ② 处我们就可以通过 ModelMap 入参访问到 items 属性当执行 listAllBoard 请求处理思路方法时② 处将在控制台打印出“model.items:2”信息当然我们也可以在请求视图中访问到模型对象中 items 属性







回页首





=atitle>小结

Spring 2.5 对 Spring MVC 进行了很大增强现在我们几乎完全可以使用基于注解 Spring MVC 完全替换掉原来基于接口 Spring MVC 基于注解 Spring MVC 比的于基于接口 Spring MVC 拥有以下几点好处:

  • 方便请求和控制器映射;
  • 方便请求处理思路方法入参绑定URL参数;
  • Controller 不必继承任何接口它仅是个简单 POJO
但是基于注解 Spring MVC 并不完美还存在优化空间在某些配置上它比基于 XML 配置更繁琐比如对于处理多个请求 Controller 来说假设我们使用个 URL 参数指定处理思路方法(如 xxx.do?method=listBoardTopic)当使用注解时每个请求处理思路方法都必须使用 @RequestMapping 注解指定对应 URL 参数(如 @RequestMapping(params = "method=listBoardTopic"))而在 XML 配置中我们仅需要配置个 ParameterMethodNameResolver 就可以了


TAG: Spring

0

相关文章

读者评论

发表评论

  • 昵称:
  • 内容: