您的位置:首页 > 编程语言 > Java开发

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

2014-08-11 16:44 381 查看
http://www.ibm.com/developerworks/cn/java/j-lo-spring25-mvc/

 Spring基于注解驱动的SpringMVC

基于注解的配置有越来越流行的趋势,Spring2.5顺应这种趋势,为SpringMVC提供了完全基于注解的配置。本文将介绍Spring2.5新增的SpingMVC注解功能,讲述如何使用注解配置替换传统的基于XML的SpringMVC配置。

 

概述
一个简单的基于注解的Controller
让一个Controller处理多个URL请求
处理方法入参如何绑定URL参数
请求处理方法的签名规约
注册自己的属性编辑器
如何准备数据
小结
参考资料
评论

概述

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

本文将介绍Spring2.5新增的SpingMVC注解功能,讲述如何使用注解配置替换传统的基于XML的SpringMVC配置。

回页首

一个简单的基于注解的Controller

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

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

清单1.BbtForumController.java

packagecom.baobaotao.web;

importcom.baobaotao.service.BbtForumService;
importorg.springframework.beans.factory.annotation.Autowired;
importorg.springframework.stereotype.Controller;
importorg.springframework.web.bind.annotation.ModelAttribute;
importorg.springframework.web.bind.annotation.RequestMapping;
importorg.springframework.web.bind.annotation.RequestMethod;

importjava.util.Collection;

@Controller//<——①
@RequestMapping("/forum.do")
publicclassBbtForumController{

@Autowired
privateBbtForumServicebbtForumService;

@RequestMapping//<——②
publicStringlistAllBoard(){
bbtForumService.getAllBoard();
System.out.println("calllistAllBoardmethod.");
return"listBoard";
}
}


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

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

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

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

清单2.web.xml:启用Spring容器和SpringMVC框架

<?xmlversion="1.0"encoding="UTF-8"?>
<web-appxmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaeehttp://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"version="2.5">
<display-name>SpringAnnotationMVCSample</display-name>
<!--Spring服务层的配置文件-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext
4000
.xml</param-value>
</context-param>

<!--Spring容器启动监听器-->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>

<!--SpringMVC的Servlet,它将加载WEB-INF/annomvc-servlet.xml的
配置文件,以启动SpringMVC模块-->
<servlet>
<servlet-name>annomvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<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的SpringMVC模块,按照SpringMVC的契约,需要在WEB-INF/annomvc-servlet.xml配置文件中定义SpringMVC模块的具体配置。annomvc-servlet.xml的配置内容如下所示:

清单3.annomvc-servlet.xml

<?xmlversion="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/beanshttp://www.springframework.org/schema/beans/spring-beans-2.5.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context-2.5.xsd">
<!--①:对web包中的所有类进行扫描,以完成Bean创建和自动依赖注入的功能-->
<context:component-scanbase-package="com.baobaotao.web"/>

<!--②:启动SpringMVC的注解功能,完成请求和注解POJO的映射-->
<beanclass="org.springframework.web.servlet.mvc.annotation.
AnnotationMethodHandlerAdapter"/>

<!--③:对模型视图名称的解析,即在模型视图名称添加前后缀-->
<beanclass="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中的SpringMVC注解对Bean进行加工处理,使这些Bean变成控制器并映射特定的URL请求。

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

启动Tomcat,发送http://localhost/forum.doURL请求,BbtForumController的listAllBoard()方法将响应这个请求,并转向WEB-INF/jsp/listBoard.jsp的视图页面。

让一个Controller处理多个URL请求

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

清单3.每个请求处理参数对应一个URL

</pre><prename="code"class="java">packagecom.baobaotao.web;

importcom.baobaotao.service.BbtForumService;
importorg.springframework.beans.factory.annotation.Autowired;
importorg.springframework.stereotype.Controller;
importorg.springframework.web.bind.annotation.RequestMapping;

@Controller
publicclassBbtForumController{
@Autowired
privateBbtForumServicebbtForumService;

@RequestMapping("/listAllBoard.do")//<——①
publicStringlistAllBoard(){
bbtForumService.getAllBoard();
System.out.println("calllistAllBoardmethod.");
return"listBoard";
}

@RequestMapping("/listBoardTopic.do")//<——②
publicStringlistBoardTopic(inttopicId){
bbtForumService.getBoardTopics(topicId);
System.out.println("calllistBoardTopicmethod.");
return"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,由请求参数决定请求处理方法

packagecom.baobaotao.web;

importcom.baobaotao.service.BbtForumService;
importorg.springframework.beans.factory.annotation.Autowired;
importorg.springframework.stereotype.Controller;
importorg.springframework.web.bind.annotation.RequestMapping;

@Controller
@RequestMapping("/bbtForum.do")//<——①指定控制器对应URL请求
publicclassBbtForumController{

@Autowired
privateBbtForumServicebbtForumService;

//<——②如果URL请求中包括"method=listAllBoard"的参数,由本方法进行处理
@RequestMapping(params="method=listAllBoard")
publicStringlistAllBoard(){
bbtForumService.getAllBoard();
System.out.println("calllistAllBoardmethod.");
return"listBoard";
}

//<——③如果URL请求中包括"method=listBoardTopic"的参数,由本方法进行处理
@RequestMapping(params="method=listBoardTopic")
publicStringlistBoardTopic(inttopicId){
bbtForumService.getBoardTopics(topicId);
System.out.println("calllistBoardTopicmethod.");
return"listTopic";
}
}


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

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

清单4.让请求处理方法处理特定的HTTP请求方法

packagecom.baobaotao.web;

importcom.baobaotao.service.BbtForumService;
importorg.springframework.beans.factory.annotation.Autowired;
importorg.springframework.stereotype.Controller;
importorg.springframework.web.bind.annotation.RequestMapping;
importorg.springframework.web.bind.annotation.RequestMethod;

@Controller
@RequestMapping("/bbtForum.do")
publicclassBbtForumController{

@RequestMapping(params="method=createTopic",method=RequestMethod.POST)
publicStringcreateTopic(){
System.out.println("callcreateTopicmethod.");
return"createTopic";
}
}


这样只有当/bbtForum.do?method=createTopic请求以HTTPPOST方式提交时,createTopic()方法才会进行处理。

处理方法入参如何绑定URL参数

按契约绑定

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

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

@RequestMapping(params="method=listBoardTopic")
//<——①topicId入参是如何绑定URL请求参数的?
publicStringlistBoardTopic(inttopicId){
bbtForumService.getBoardTopics(topicId);
System.out.println("calllistBoardTopicmethod.");
return"listTopic";
}


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

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

listBoardTopic(inttopicId):和topicIdURL请求参数绑定;
listBoardTopic(inttopicId,StringboardName):分别和topicId、boardNameURL请求参数绑定;


特别的,如果入参是基本数据类型(如int、long、float等),URL请求参数中一定要有对应的参数,否则将抛出TypeMismatchException异常,提示无法将null转换为基本数据类型。

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

清单6.User.java:一个JavaBean

packagecom.baobaotao.web;

publicclassUser{
privateintuserId;
privateStringuserName;
//省略get/setter方法
publicStringtoString(){
returnthis.userName+","+this.userId;
}
}


下面是将User作为listBoardTopic()请求处理方法的入参:

清单7.使用JavaBean作为请求处理方法的入参

@RequestMapping(params="method=listBoardTopic")
publicStringlistBoardTopic(inttopicId,Useruser){
bbtForumService.getBoardTopics(topicId);
System.out.println("topicId:"+topicId);
System.out.println("user:"+user);
System.out.println("calllistBoardTopicmethod.");
return"listTopic";
}


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

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

通过注解指定绑定的URL参数

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

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

packagecom.baobaotao.web;

importorg.springframework.web.bind.annotation.RequestMapping;
importorg.springframework.web.bind.annotation.RequestParam;



@Controller
@RequestMapping("/bbtForum.do")
publicclassBbtForumController{

@RequestMapping(params="method=listBoardTopic")
publicStringlistBoardTopic(@RequestParam("id")inttopicId,Useruser){
bbtForumService.getBoardTopics(topicId);
System.out.println("topicId:"+topicId);
System.out.println("user:"+user);
System.out.println("calllistBoardTopicmethod.");
return"listTopic";
}

}


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

绑定模型对象中某个属性

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

清单9.使用ModelMap访问请示对应的隐含模型对象

@RequestMapping(params="method=listBoardTopic")
publicStringlistBoardTopic(@RequestParam("id")inttopicId,
Useruser,ModelMapmodel){
bbtForumService.getBoardTopics(topicId);
System.out.println("topicId:"+topicId);
System.out.println("user:"+user);
//①将user对象以currUser为键放入到model中
model.addAttribute("currUser",user);
return"listTopic";
}


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

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

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

清单10.使模型对象的特定属性具有Session范围的作用域

packagecom.baobaotao.web;


importorg.springframework.ui.ModelMap;
importorg.springframework.
d1e9
web.bind.annotation.SessionAttributes;

@Controller
@RequestMapping("/bbtForum.do")
@SessionAttributes("currUser")//①将ModelMap中属性名为currUser的属性
//放到Session属性列表中,以便这个属性可以跨请求访问
publicclassBbtForumController{

@RequestMapping(params="method=listBoardTopic")
publicStringlistBoardTopic(@RequestParam("id")inttopicId,Useruser,
ModelMapmodel){
bbtForumService.getBoardTopics(topicId);
System.out.println("topicId:"+topicId);
System.out.println("user:"+user);
model.addAttribute("currUser",user);//②向ModelMap中添加一个属性
return"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.class),当然也可以指定多个类,如@SessionAttributes(types
={User.class,Dept.class}),还可以联合使用属性名和属性类型指定:@SessionAttributes(types={User.class,Dept.class},value={“attr1”,”attr2”})。

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

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

packagecom.baobaotao.web;


importorg.springframework.ui.ModelMap;
importorg.springframework.web.bind.annotation.SessionAttributes;

@Controller
@RequestMapping("/bbtForum.do")
@SessionAttributes("currUser")//①将ModelMap中属性名为currUser的属性
//放到Session属性列表中,以便这个属性可以跨请求访问
publicclassBbtForumController{

@RequestMapping(params="method=listBoardTopic")
publicStringlistBoardTopic(@RequestParam("id")inttopicId,Useruser,
ModelMapmodel){
bbtForumService.getBoardTopics(topicId);
System.out.println("topicId:"+topicId);
System.out.println("user:"+user);
model.addAttribute("currUser",user);//②向ModelMap中添加一个属性
return"listTopic";
}

}


在②处,我们向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属性上了。

请求处理方法的签名规约

方法入参

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

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

清单12.使用SessionStatus控制Session级别的模型属性

@RequestMapping(method=RequestMethod.POST)
publicStringprocessSubmit(@ModelAttributeOwnerowner,
BindingResultresult,SessionStatusstatus){//<——①
newOwnerValidator().validate(owner,result);
if(result.hasErrors()){
return"ownerForm";
}
else{
this.clinic.storeOwner(owner);
status.setComplete();//<——②
return"redirect:owner.do?ownerId="+owner.getId();
}
}


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

方法返回参数

在低版本的SpringMVC中,请求处理方法的返回值类型都必须是ModelAndView。而在Spring2.5中,你拥有多种灵活的选择。通过下表进行说明:

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

注册自己的属性编辑器

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

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

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

清单13.注册框架级的自定义属性编辑器

<beanclass="org.springframework.web.servlet.mvc.annotation.
AnnotationMethodHandlerAdapter"<
<propertyname="webBindingInitializer">
<beanclass="com.baobaotao.web.MyBindingInitializer"/>
</property>
</bean>


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

清单14.自定义属性编辑器

<prename="code"class="java">packagecom.baobaotao.web;


importorg.springframework.ui.ModelMap;
importorg.springframework.web.bind.annotation.SessionAttributes;

@Controller
@RequestMapping("/bbtForum.do")
@SessionAttributes("currUser")//①将ModelMap中属性名为currUser的属性
//放到Session属性列表中,以便这个属性可以跨请求访问
publicclassBbtForumController{

@RequestMapping(params="method=listBoardTopic")
publicStringlistBoardTopic(@RequestParam("id")inttopicId,Useruser,
ModelMapmodel){
bbtForumService.getBoardTopics(topicId);
System.out.println("topicId:"+topicId);
System.out.println("user:"+user);
model.addAttribute("currUser",user);//②向ModelMap中添加一个属性
return"listTopic";
}

}




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

清单15.注册Controller级的自定义属性编辑器

@Controller
publicclassMyFormController{

@InitBinder
publicvoidinitBinder(WebDataBinderbinder){
SimpleDateFormatdateFormat=newSimpleDateFormat("yyyy-MM-dd");
dateFormat.setLenient(false);
binder.registerCustomEditor(Date.class,newCustomDateEditor(dateFormat,false));
}

}


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

回页首

如何准备数据

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

清单16.定义为处理请求准备数据的方法

packagecom.baobaotao.web;

importcom.baobaotao.service.BbtForumService;
importorg.springframework.beans.factory.annotation.Autowired;
importorg.springframework.stereotype.Controller;
importorg.springframework.ui.ModelMap;
importorg.springframework.web.bind.annotation.ModelAttribute;
importorg.springframework.web.bind.annotation.RequestMapping;
importorg.springframework.web.bind.annotation.RequestParam;
importorg.springframework.web.bind.annotation.SessionAttributes;

importjava.util.ArrayList;
importjava.util.List;
importjava.util.Set;

@Controller
@RequestMapping("/bbtForum.do")
publicclassBbtForumController{

@Autowired
privateBbtForumServicebbtForumService;

@ModelAttribute("items")//<——①向模型对象中添加一个名为items的属性
publicList<String>populateItems(){
List<String>lists=newArrayList<String>();
lists.add("item1");
lists.add("item2");
returnlists;
}

@RequestMapping(params="method=listAllBoard")
publicStringlistAllBoard(@ModelAttribute("currUser")Useruser,ModelMapmodel){
bbtForumService.getAllBoard();
//<——②在此访问模型中的items属性
System.out.println("model.items:"+((List<String>)model.get("items")).size());
return"listBoard";
}
}


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

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

 

小结

Spring2.5对SpringMVC进行了很大增强,现在我们几乎完全可以使用基于注解的SpringMVC完全替换掉原来基于接口SpringMVC程序。基于注解的SpringMVC比之于基于接口的SpringMVC拥有以下几点好处:

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

 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息