??xml version="1.0" encoding="utf-8" standalone="yes"?>亚洲AV成人精品日韩一区,亚洲最大免费视频网,亚洲AⅤ视频一区二区三区http://www.tkk7.com/AstroQi/category/32427.htmlI'm Astro Qi. If call me, please send email to closoastroqi@126.comzh-cnFri, 20 Jun 2008 08:50:34 GMTFri, 20 Jun 2008 08:50:34 GMT60Spring 2.5 注解驱动?Spring MVChttp://www.tkk7.com/AstroQi/archive/2008/06/20/209486.htmlAstro.QiAstro.QiFri, 20 Jun 2008 08:13:00 GMThttp://www.tkk7.com/AstroQi/archive/2008/06/20/209486.htmlhttp://www.tkk7.com/AstroQi/comments/209486.htmlhttp://www.tkk7.com/AstroQi/archive/2008/06/20/209486.html#Feedback0http://www.tkk7.com/AstroQi/comments/commentRss/209486.htmlhttp://www.tkk7.com/AstroQi/services/trackbacks/209486.htmlZ注解的配|有来流行的势QSpring 2.5 应q种势Qؓ(f) Spring MVC 提供?jin)完全基于注解的配置。本文将介绍 Spring 2.5 新增?Sping MVC 注解功能Q讲q如何用注解配|替换传l的Z XML ?Spring MVC 配置?/blockquote>

概述

l?Spring 2.0 ?Spring MVC q行重大升后,Spring 2.5 又ؓ(f) Spring MVC 引入?jin)注解驱动功能。现在你无须?Controller l承M接口Q无需?XML 配置文g中定义请求和 Controller 的映关p,仅仅使用注解可以让一?POJO h Controller 的绝大部分功?—?Spring MVC 框架的易用性得C(jin)q一步的增强.在框架灵zL、易用性和扩展性上QSpring MVC 已经全面越?jin)其它?MVC 框架Q伴随着 Spring 一路高qq,可以预见 Spring MVC ?MVC ?jng)场上的吸引力将来不可抗拒?/p>

本文介l?Spring 2.5 新增?Sping MVC 注解功能Q讲q如何用注解配|替换传l的Z XML ?Spring MVC 配置?/p>



回页?/strong>


一个简单的Z注解?Controller

使用q低版本 Spring MVC 的读者都知道Q当创徏一?Controller Ӟ我们需要直接或间接地实?org.springframework.web.servlet.mvc.Controller 接口。一般情况下Q我们是通过l承 SimpleFormController ?MultiActionController 来定义自q Controller 的。在定义 Controller 后,一个重要的事g是在 Spring MVC 的配|文件中通过 HandlerMapping 定义h和控制器的映关p,以便两者关联v来?/p>

来看一下基于注解的 Controller 是如何定义做到这一点的Q下面是使用注解?BbtForumControllerQ?/p>
清单 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 class BbtForumController {
            @Autowired
            private BbtForumService bbtForumService;
            @RequestMapping //<——②
            public String listAllBoard() {
            bbtForumService.getAllBoard();
            System.out.println("call listAllBoard method.");
            return "listBoard";
            }
            }
            

从上面代码中Q我们可以看?BbtForumController 和一般的cdƈ没有区别Q它没有实现MҎ(gu)的接口,因而是一个地道的 POJO。让q个 POJO 与众不同的魔就?Spring MVC 的注解!

??处用了(jin)两个注解Q分别是 @Controller ?@RequestMapping。在“使用 Spring 2.5 Z注解驱动?IoC”q篇文章里,W者曾l指?gu)?@Controller、@Service 以及(qing) @Repository ?@Component 注解的作用是{h(hun)的:(x)一个类成ؓ(f) Spring 容器?Bean。由?Spring MVC ?Controller 必须事先是一?BeanQ所?@Controller 注解是不可缺的?/p>

真正?BbtForumController 具备 Spring MVC Controller 功能的是 @RequestMapping q个注解。@RequestMapping 可以标注在类定义处,?Controller 和特定请求关联v来;q可以标注在Ҏ(gu){֐处,以便q一步对hq行分流。在 ?处,我们?BbtForumController 兌“/forum.do”的请求,??处,我们具体地指?listAllBoard() Ҏ(gu)来处理请求。所以在cd明处标注?@RequestMapping 相当于让 POJO 实现?Controller 接口Q而在Ҏ(gu)定义处的 @RequestMapping 相当于让 POJO 扩展 Spring 预定义的 ControllerQ如 SimpleFormController {)(j)?/p>

Z(jin)让基于注解的 Spring MVC 真正工作hQ需要在 Spring MVC 对应?xxx-servlet.xml 配置文g中做一些手脚。在此之前,q是先来看一?web.xml 的配|吧Q?/p>
清单 2. web.xmlQ启?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 服务层的配置文g -->
            <context-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:applicationContext.xml</param-value>
            </context-param>
            <!--  Spring 容器启动监听?-->
            <listener>
            <listener-class>org.springframework.web.context.ContextLoaderListener
            </listener-class>
            </listener>
            <!--  Spring MVC 的ServletQ它?yu)加载WEB-INF/annomvc-servlet.xml ?
            配置文gQ以启动Spring MVC模块-->
            <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 中定义了(jin)一个名?annomvc ?Spring MVC 模块Q按?Spring MVC 的契U,需要在 WEB-INF/annomvc-servlet.xml 配置文g中定?Spring MVC 模块的具体配|。annomvc-servlet.xml 的配|内容如下所C:(x)

清单 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">
            <!-- ①:(x)对web包中的所有类q行扫描Q以完成Bean创徏和自动依赖注入的功能 -->
            <context:component-scan base-package="com.baobaotao.web"/>
            <!-- ②:(x)启动Spring MVC的注解功能,完成h和注解POJO的映?-->
            <bean class="org.springframework.web.servlet.mvc.annotation.
            AnnotationMethodHandlerAdapter"/>
            <!--  ③:(x)Ҏ(gu)型视囑֐U的解析Q即在模型视囑֐U添加前后缀 -->
            <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
            p:prefix="/WEB-INF/jsp/" p:suffix=".jsp"/>
            </beans>
            

因ؓ(f) Spring 所有功能都?Bean 的基上演化而来Q所以必M先将 Controller 变成 BeanQ这是通过在类中标?@Controller q在 annomvc-servlet.xml 中启用组件扫描机制来完成的,??所C?/p>

??处,配置?jin)一?AnnotationMethodHandlerAdapterQ它负责Ҏ(gu) Bean 中的 Spring MVC 注解?Bean q行加工处理Qɘq些 Bean 变成控制器ƈ映射特定?URL h?/p>

??处的工作是定义模型视囑֐U的解析规则Q这里我们用了(jin) Spring 2.5 的特D命名空_(d)?p 命名I间Q它?yu)原先需要通过 <property> 元素配置的内容{化ؓ(f) <bean> 属性配|,在一定程度上化了(jin) <bean> 的配|?/p>

启动 TomcatQ发?http://localhost/forum.do URL hQBbtForumController ?listAllBoard() Ҏ(gu)响应这个请求,q{?WEB-INF/jsp/listBoard.jsp 的视N面?/p>



回页?/strong>


让一?Controller 处理多个 URL h

在低版本?Spring MVC 中,我们可以通过l承 MultiActionController 让一?Controller 处理多个 URL h。?@RequestMapping 注解后,q个功能更加Ҏ(gu)实现?jin)。请看下面的代码Q?/p>

清单 3. 每个h处理参数对应一?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 class BbtForumController {
            @Autowired
            private BbtForumService bbtForumService;
            @RequestMapping("/listAllBoard.do") // <—??
            public String listAllBoard() {
            bbtForumService.getAllBoard();
            System.out.println("call listAllBoard method.");
            return "listBoard";
            }
            @RequestMapping("/listBoardTopic.do") // <—??
            public String listBoardTopic(int topicId) {
            bbtForumService.getBoardTopics(topicId);
            System.out.println("call listBoardTopic method.");
            return "listTopic";
            }
            }
            

在这里,我们分别????处ؓ(f) listAllBoard() ?listBoardTopic() Ҏ(gu)标注?@RequestMapping 注解Q分别指定这两个Ҏ(gu)处理?URL hQ这相当于将 BbtForumController 攚wؓ(f) MultiActionController。这?/listAllBoard.do ?URL h由 listAllBoard() 负责处理Q?/listBoardTopic.do?topicId=1 ?URL h则由 listBoardTopic() Ҏ(gu)处理?/p>

对于处理多个 URL h?Controller 来说Q我们們֐于通过一?URL 参数指定 Controller 处理Ҏ(gu)的名Uͼ?method=listAllBoardQ,而非直接通过不同?URL 指定 Controller 的处理方法。?@RequestMapping 注解很容易实现这个常用的需求。来看下面的代码Q?/p>
清单 4. 一?Controller 对应一?URLQ由h参数军_h处理Ҏ(gu)
            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")  // <—??指定控制器对应U(ku)RLh
            public class BbtForumController {
            @Autowired
            private BbtForumService bbtForumService;
            // <—??如果URLh中包?method=listAllBoard"的参敎ͼ由本Ҏ(gu)q行处理
            @RequestMapping(params = "method=listAllBoard")
            public String listAllBoard() {
            bbtForumService.getAllBoard();
            System.out.println("call listAllBoard method.");
            return "listBoard";
            }
            // <—??如果URLh中包?method=listBoardTopic"的参敎ͼ由本Ҏ(gu)q行处理
            @RequestMapping(params = "method=listBoardTopic")
            public String listBoardTopic(int topicId) {
            bbtForumService.getBoardTopics(topicId);
            System.out.println("call listBoardTopic method.");
            return "listTopic";
            }
            }
            

在类定义处标注的 @RequestMapping ?BbtForumController 处理所有包?/bbtForum.do ?URL hQ?BbtForumController 中的h处理Ҏ(gu)?URL h的分规则在 ???处定义分规则按?URL ?method h参数定。所以分别在cd义处和方法定义处使用 @RequestMapping 注解Q就可以很容易通过 URL 参数指定 Controller 的处理方法了(jin)?/p>

@RequestMapping 注解中除?params 属性外Q还有一个常用的属性是 methodQ它可以?Controller Ҏ(gu)处理特定 HTTP h方式的请求,如让一个方法处?HTTP GET hQ而另一个方法处?HTTP POST hQ如下所C:(x)


清单 4. 让请求处理方法处理特定的 HTTP hҎ(gu)
            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 class BbtForumController {
            @RequestMapping(params = "method=createTopic",method = RequestMethod.POST)
            public String createTopic(){
            System.out.println("call createTopic method.");
            return "createTopic";
            }
            }
            

q样只有?/bbtForum.do?method=createTopic h?HTTP POST 方式提交ӞcreateTopic() Ҏ(gu)才会(x)q行处理?/p>



回页?/strong>


处理Ҏ(gu)入参如何l定 URL 参数

按契U绑?/span>

Controller 的方法标注了(jin) @RequestMapping 注解后,它就能处理特定的 URL h。我们不要问:(x)h处理Ҏ(gu)入参是如何绑?URL 参数的呢Q在回答q个问题?sh)前先来看下面的代码Q?/p>

清单 5. 按参数名匚wq行l定

            @RequestMapping(params = "method=listBoardTopic")
            //<—??topicId入参是如何绑定URLh参数的?
            public String listBoardTopic(int topicId) {
            bbtForumService.getBoardTopics(topicId);
            System.out.println("call listBoardTopic method.");
            return "listTopic";
            }
            

当我们发?http://localhost//bbtForum.do?method=listBoardTopic&topicId=10 ?URL hӞSpring 不但?listBoardTopic() Ҏ(gu)处理q个hQ而且q将 topicId h参数在类型{换后l定?listBoardTopic() Ҏ(gu)?topicId 入参上。?listBoardTopic() Ҏ(gu)的返回类型是 StringQ它?yu)被解析为逻辑视图的名U。也是?Spring 在如何给处理Ҏ(gu)入参自动赋g?qing)如何将处理?gu)q回D{化ؓ(f) ModelAndView 中的q程中存在一套潜在的规则Q不熟?zhn)q个规则׃可能很好地开发基于注解的h处理Ҏ(gu)Q因此了(jin)解这个潜在规则无疑成为理?Spring MVC 框架Z注解功能的核?j)问题?/p>

我们不妨从最常见的开始说P(x)h处理Ҏ(gu)入参的类型可以是 Java 基本数据cd?String cdQ这时方法入参按参数名匹配的原则l定?URL h参数Q同时还自动完成 String cd?URL h参数到请求处理方法参数类型的转换。下面给出几个例子:(x)

  • listBoardTopic(int topicId)Q和 topicId URL h参数l定Q?
  • listBoardTopic(int topicId,String boardName)Q分别和 topicId、boardName URL h参数l定Q?
?

特别的,如果入参是基本数据类型(?int、long、float {)(j)QURL h参数中一定要有对应的参数Q否则将抛出 TypeMismatchException 异常Q提C无法将 null 转换为基本数据类型?/p>

另外Q请求处理方法的入参也可以一?JavaBeanQ如下面?User 对象可以作Z个入参:(x)


清单 6. User.javaQ一?JavaBean
            package com.baobaotao.web;
            public class User {
            private int userId;
            private String userName;
            //省略get/setterҎ(gu)
            public String toString(){
            return this.userName +","+this.userId;
            }
            }
            

下面是将 User 作ؓ(f) listBoardTopic() h处理Ҏ(gu)的入参:(x)


清单 7. 使用 JavaBean 作ؓ(f)h处理Ҏ(gu)的入?/strong>
            @RequestMapping(params = "method=listBoardTopic")
            public String listBoardTopic(int topicId,User user) {
            bbtForumService.getBoardTopics(topicId);
            System.out.println("topicId:"+topicId);
            System.out.println("user:"+user);
            System.out.println("call listBoardTopic method.");
            return "listTopic";
            }
            

q时Q如果我们用以下的 URL hQhttp://localhost/bbtForum.do?method=listBoardTopic&topicId=1&userId=10&userName=tom

topicId URL 参数绑定到 topicId 入参上,?userId ?userName URL 参数绑定到 user 对象?userId ?userName 属性中。和 URL h中不允许没有 topicId 参数不同Q虽?User ?userId 属性的cd是基本数据类型,但如?URL 中不存在 userId 参数QSpring 也不?x)报错,此?user.userId gؓ(f) 0。如?User 对象拥有一?dept.deptId 的联属性,那么它将?dept.deptId URL 参数l定?/p>

通过注解指定l定?URL 参数

如果我们x(chng)变这U默认的按名U匹配的{略Q比如让 listBoardTopic(int topicId,User user) 中的 topicId l定?id q个 URL 参数Q那么可以通过对入参?@RequestParam 注解来达到目的:(x)

清单 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 class BbtForumController {
            @RequestMapping(params = "method=listBoardTopic")
            public String listBoardTopic(@RequestParam("id") int topicId,User user) {
            bbtForumService.getBoardTopics(topicId);
            System.out.println("topicId:"+topicId);
            System.out.println("user:"+user);
            System.out.println("call listBoardTopic method.");
            return "listTopic";
            }
            …
            }
            

q里Q对 listBoardTopic() h处理Ҏ(gu)?topicId 入参标注?@RequestParam("id") 注解Q所以它?yu)?id ?URL 参数l定?/p>

l定模型对象中某个属?/span>

Spring 2.0 定义?jin)一?org.springframework.ui.ModelMap c,它作为通用的模型数据承载对象,传递数据供视图所用。我们可以在h处理Ҏ(gu)中声明一?ModelMap cd的入参,Spring ?x)将本次h模型对象引用通过该入参传递进来,q样可以在h处理Ҏ(gu)内部讉K模型对象?jin)。来看下面的例子Q?/p>
清单 9. 使用 ModelMap 讉KL(fng)对应的隐含模型对?/strong>
            @RequestMapping(params = "method=listBoardTopic")
            public String listBoardTopic(@RequestParam("id")int topicId,
            User user,ModelMap model) {
            bbtForumService.getBoardTopics(topicId);
            System.out.println("topicId:" + topicId);
            System.out.println("user:" + user);
            //?user对象以currUser为键攑օ到model?
            model.addAttribute("currUser",user);
            return "listTopic";
            }
            

对于当次h所对应的模型对象来_(d)其所有属性都存攑ֈ request 的属性列表中。象上面的例子,ModelMap 中的 currUser 属性将攑ֈ request 的属性列表中Q所以可以在 JSP 视图面中通过 request.getAttribute(“currUser”) 或者通过 ${currUser} EL 表达式访问模型对象中?user 对象。从q个角度上看Q?ModelMap 相当于是一个向 request 属性列表中d对象的一条管道,借由 ModelMap 对象的支持,我们可以在一个不依赖 Servlet API ?Controller 中向 request 中添加属性?/p>

在默认情况下QModelMap 中的属性作用域?request U别是,也就是说Q当本次hl束后,ModelMap 中的属性将销毁。如果希望在多个h中共?ModelMap 中的属性,必须其属性{存到 session 中,q样 ModelMap 的属性才可以被跨h讉K?/p>

Spring 允许我们有选择地指?ModelMap 中的哪些属性需要{存到 session 中,以便下一个请求属对应?ModelMap 的属性列表中q能讉K到这些属性。这一功能是通过cd义处标注 @SessionAttributes 注解来实现的。请看下面的代码Q?/p>
清单 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属性列表中Q以便这个属性可以跨h讉K
            public class BbtForumController {
            …
            @RequestMapping(params = "method=listBoardTopic")
            public String listBoardTopic(@RequestParam("id")int topicId, User user,
            ModelMap model) {
            bbtForumService.getBoardTopics(topicId);
            System.out.println("topicId:" + topicId);
            System.out.println("user:" + user);
            model.addAttribute("currUser",user); //②向ModelMap中添加一个属?
            return "listTopic";
            }
            }
            

我们??处添加了(jin)一?ModelMap 属性,其属性名?currUserQ??处通过 @SessionAttributes 注解?ModelMap 中名?currUser 的属性放|到 Session 中,所以我们不但可以在 listBoardTopic() h所对应?JSP 视图面中通过 request.getAttribute(“currUser”) ?session.getAttribute(“currUser”) 获取 user 对象Q还可以在下一个请求所对应?JSP 视图面中通过 session.getAttribute(“currUser”) ?ModelMap#get(“currUser”) 讉K到这个属性?/p>

q里我们仅将一?ModelMap 的属性放?Session 中,其实 @SessionAttributes 允许指定多个属性。你可以通过字符串数l的方式指定多个属性,?@SessionAttributes({“attr1”,”attr2”})。此外,@SessionAttributes q可以通过属性类型指定要 session 化的 ModelMap 属性,?@SessionAttributes(types = User.class)Q当然也可以指定多个c,?@SessionAttributes(types = {User.class,Dept.class})Q还可以联合使用属性名和属性类型指定:(x)@SessionAttributes(types = {User.class,Dept.class},value={“attr1”,”attr2”})?/p>

上面讲述?jin)如何往ModelMap中放|属性以?qing)如何ModelMap中的属性拥有Session域的作用范围。除?jin)在JSP视图面中通过传统的方法访问ModelMap中的属性外Q读者朋友可能会(x)问:(x)是否可以ModelMap中的属性绑定到h处理Ҏ(gu)的入参中呢?{案是肯定的。Spring为此提供?jin)一个@ModelAttribute的注解,下面是用@ModelAttribute注解的例子:(x)

清单 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") //①让ModelMap的currUser属性拥有sessionU作用域
            public class BbtForumController {
            @Autowired
            private BbtForumService bbtForumService;
            @RequestMapping(params = "method=listBoardTopic")
            public String listBoardTopic(@RequestParam("id")int topicId, User user,
            ModelMap model) {
            bbtForumService.getBoardTopics(topicId);
            System.out.println("topicId:" + topicId);
            System.out.println("user:" + user);
            model.addAttribute("currUser",user); //②向ModelMap中添加一个属?
            return "listTopic";
            }
            @RequestMapping(params = "method=listAllBoard")
            //③将ModelMap中的
            public String listAllBoard(@ModelAttribute("currUser") User user) {
            //currUser属性绑定到user入参中?
            bbtForumService.getAllBoard();
            System.out.println("user:"+user);
            return "listBoard";
            }
            }
            

??处,我们?ModelMap 中添加一个名?currUser 的属性,??外的注解使这?currUser 属性拥有了(jin) session U的作用域。所以,我们可以??处通过 @ModelAttribute 注解?ModelMap 中的 currUser 属性绑定以h处理Ҏ(gu)?user 入参中?/p>

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

以执行listBoardTopic()h处理Ҏ(gu)Q然后再讉K以下URLQ?http://localhost/sample/bbtForum.do?method=listAllBoard

你将可以看到 listAllBoard() ?user 入参已经成功l定?listBoardTopic() 中注册的 session U的 currUser 属性上?jin)?/p>



回页?/strong>


h处理Ҏ(gu)的签名规U?/span>

Ҏ(gu)入参

我们知道标注?@RequestMapping 注解?Controller Ҏ(gu)成Z(jin)h处理Ҏ(gu)QSpring MVC 允许极其灉|的请求处理方法签名方式。对于方法入参来_(d)它允许多U类型的入参Q通过下表q行说明Q?/p>
h处理Ҏ(gu)入参的可选类?/th> 说明
Java 基本数据cd?String 默认情况下将按名U匹配的方式l定?URL 参数上,可以通过 @RequestParam 注解改变默认的绑定规?/td>
request/response/session 既可以是 Servlet API 的也可以?Portlet API 对应的对象,Spring ?x)将它们l定?Servlet ?Portlet 容器的相应对象上
org.springframework.web.context.request.WebRequest 内部包含?request 对象
java.util.Locale l定?request 对应?Locale 对象?/td>
java.io.InputStream/java.io.Reader 可以借此讉K request 的内?/td>
java.io.OutputStream / java.io.Writer 可以借此操作 response 的内?/td>
M标注?@RequestParam 注解的入?/td> 被标?@RequestParam 注解的入参将l定到特定的 request 参数上?/td>
java.util.Map / org.springframework.ui.ModelMap 它绑?Spring MVC 框架中每个请求所创徏的潜在的模型对象Q它们可以被 Web 视图对象讉KQ如 JSPQ?/td>
命o(h)/表单对象Q注Q一般称l定使用 HTTP GET 发送的 URL 参数的对象ؓ(f)命o(h)对象Q而称l定使用 HTTP POST 发送的 URL 参数的对象ؓ(f)表单对象Q?/td> 它们的属性将以名U匹配的规则l定?URL 参数上,同时完成cd的{换。而类型{换的规则可以通过 @InitBinder 注解或通过 HandlerAdapter 的配|进行调?/td>
org.springframework.validation.Errors / org.springframework.validation.BindingResult 为属性列表中的命?表单对象的校验结果,注意(g)验结果参数必ȝ跟在命o(h)/表单对象的后?/td>
rg.springframework.web.bind.support.SessionStatus 可以通过该类?status 对象昑ּl束表单的处理,q相当于触发 session 清除其中的通过 @SessionAttributes 定义的属?/td>

Spring MVC 框架的易用之处在于,你可以按L序定义h处理Ҏ(gu)的入参(除了(jin) Errors ?BindingResult 必须紧跟在命令对?表单参数后面以外Q,Spring MVC ?x)根据反机制自动将对应的对象通过入参传递给h处理Ҏ(gu)。这U机制让开发者完全可以不依赖 Servlet API 开发控制层的程序,当请求处理方法需要特定的对象Ӟ仅仅需要在参数列表中声明入参即可,不需要考虑如何获取q些对象QSpring MVC 框架p一个大家一?#8220;不辞辛苦”Cؓ(f)我们准备好了(jin)所需的一切。下面演CZ下?SessionStatus 的例子:(x)


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

processSubmit() Ҏ(gu)中的 owner 表单对象绑定到 ModelMap ?#8220;owner”属性中Qresult 参数用于存放(g)?owner l果的对象,?status 用于控制表单处理的状态。在 ?处,我们通过调用 status.setComplete() Ҏ(gu)Q该 Controller 所有放?session U别的模型属性数据将?session 中清I?/p>

Ҏ(gu)q回参数

在低版本?Spring MVC 中,h处理Ҏ(gu)的返回值类型都必须?ModelAndView。而在 Spring 2.5 中,你拥有多U灵zȝ选择。通过下表q行说明Q?/p>
h处理Ҏ(gu)入参的可选类?/th> 说明
void

此时逻辑视图名由h处理Ҏ(gu)对应?URL 定Q如以下的方法:(x)

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

对应的逻辑视图名ؓ(f)“welcome”

String

此时逻辑视图名ؓ(f)q回的字W,如以下的Ҏ(gu)Q?/p>
@RequestMapping(method = RequestMethod.GET)
                        public String setupForm(@RequestParam("ownerId") int ownerId, ModelMap model) {
                        Owner owner = this.clinic.loadOwner(ownerId);
                        model.addAttribute(owner);
                        return "ownerForm";
                        }
                        

对应的逻辑视图名ؓ(f)“ownerForm”

org.springframework.ui.ModelMap

和返回类型ؓ(f) void 一P逻辑视图名取决于对应h?URLQ如下面的例子:(x)

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

对应的逻辑视图名ؓ(f)“vets”Q返回的 ModelMap 被作ؓ(f)h对应的模型对象,可以?JSP 视图面中访问到?/p>

ModelAndView 当然q可以是传统?ModelAndView?/td>

应该说?String 作ؓ(f)h处理Ҏ(gu)的返回值类型是比较通用的方法,q样q回的逻辑视图名不?x)和h URL l定Q具有很大的灉|性,而模型数据又可以通过 ModelMap 控制。当然直接用传l的 ModelAndView 也不׃ؓ(f)一个好的选择?/p>



回页?/strong>


注册自己的属性编辑器

Spring MVC 有一套常用的属性编辑器Q这包括基本数据cd?qing)其包裹cȝ属性编辑器、String 属性编辑器、JavaBean 的属性编辑器{。但有时我们q需要向 Spring MVC 框架注册一些自定义的属性编辑器Q如特定旉格式的属性编辑器是其中一例?/p>

Spring MVC 允许向整?Spring 框架注册属性编辑器Q它们对所?Controller 都有影响。当?Spring MVC 也允总向某?Controller 注册属性编辑器Q对其它?Controller 没有影响。前者可以通过 AnnotationMethodHandlerAdapter 的配|做刎ͼ而后者则可以通过 @InitBinder 注解实现?/p>

下面先看向整?Spring MVC 框架注册的自定义~辑器:(x)


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

MyBindingInitializer 实现?WebBindingInitializer 接口Q在接口Ҏ(gu)中通过 binder 注册多个自定义的属性编辑器Q其代码如下所C:(x)


清单 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 class MyBindingInitializer implements WebBindingInitializer {
            public void initBinder(WebDataBinder binder, WebRequest request) {
            SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
            dateFormat.setLenient(false);
            binder.registerCustomEditor(Date.class,
            new CustomDateEditor(dateFormat, false));
            binder.registerCustomEditor(String.class, new StringTrimmerEditor(false));
            }
            }
            

如果希望某个属性编辑器仅作用于特定?ControllerQ可以在 Controller 中定义一个标?@InitBinder 注解的方法,可以在该Ҏ(gu)中向 Controller ?jin)注册若q个属性编辑器Q来看下面的代码Q?/p>
清单 15. 注册 Controller U的自定义属性编辑器
            @Controller
            public class MyFormController {
            @InitBinder
            public void initBinder(WebDataBinder binder) {
            SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
            dateFormat.setLenient(false);
            binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, false));
            }
            …
            }
            

注意被标?@InitBinder 注解的方法必L有一?WebDataBinder cd的入参,以便 Spring MVC 框架注册属性编辑器?WebDataBinder 对象传递进来?/p>



回页?/strong>


如何准备数据

在编?Controller Ӟ常常需要在真正q入h处理Ҏ(gu)前准备一些数据,以便h处理或视图渲染时使用。在传统?SimpleFormController 里,是通过复写?referenceData() Ҏ(gu)来准备引用数据的。在 Spring 2.5 Ӟ可以Q何一个拥有返回值的Ҏ(gu)标注?@ModelAttributeQ其返回值将?x)进入到模型对象的属性列表中。来看下面的例子Q?/p>
清单 16. 定义为处理请求准备数据的Ҏ(gu)
            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 class BbtForumController {
            @Autowired
            private BbtForumService bbtForumService;
            @ModelAttribute("items")//<——①向模型对象中d一个名为items的属?
            public List<String> populateItems() {
            List<String> lists = new ArrayList<String>();
            lists.add("item1");
            lists.add("item2");
            return lists;
            }
            @RequestMapping(params = "method=listAllBoard")
            public String listAllBoard(@ModelAttribute("currUser")User user, ModelMap model) {
            bbtForumService.getAllBoard();
            //<——②在此讉K模型中的items属?
            System.out.println("model.items:" + ((List<String>)model.get("items")).size());
            return "listBoard";
            }
            }
            

??处,通过使用 @ModelAttribute 注解QpopulateItem() Ҏ(gu)在Mh处理Ҏ(gu)执行前调用,Spring MVC ?x)将该方法返回g“items”为名攑օ到隐含的模型对象属性列表中?/p>

所以在 ?处,我们可以通过 ModelMap 入参讉K?items 属性,当执?listAllBoard() h处理Ҏ(gu)Ӟ?处将在控制台打印?#8220;model.items:2”的信息。当然我们也可以在请求的视图中访问到模型对象中的 items 属性?/p>



回页?/strong>


Spring 2.5 ?Spring MVC q行?jin)很大增强,现在我们几乎完全可以使用Z注解?Spring MVC 完全替换掉原来基于接?Spring MVC E序。基于注解的 Spring MVC 比之于基于接口的 Spring MVC 拥有以下几点好处Q?/p>

  • 方便h和控制器的映;
  • 方便h处理Ҏ(gu)入参l定URL参数Q?
  • Controller 不必l承M接口Q它仅是一个简单的 POJO?

但是Z注解?Spring MVC q不完美Q还存在优化的空_(d)因ؓ(f)在某些配|上它比Z XML 的配|更J琐。比如对于处理多个请求的 Controller 来说Q假设我们用一?URL 参数指定调用的处理方法(?xxx.do?method=listBoardTopicQ,当用注解时Q每个请求处理方法都必须使用 @RequestMapping() 注解指定对应?URL 参数Q如 @RequestMapping(params = "method=listBoardTopic")Q,而在 XML 配置中我们仅需要配|一?ParameterMethodNameResolver 可以了(jin)?/p>

Astro.Qi 2008-06-20 16:13 发表评论
]]>
Spring 2.5 注释驱动?IoC 功能http://www.tkk7.com/AstroQi/archive/2008/06/20/209482.htmlAstro.QiAstro.QiFri, 20 Jun 2008 08:08:00 GMThttp://www.tkk7.com/AstroQi/archive/2008/06/20/209482.htmlhttp://www.tkk7.com/AstroQi/comments/209482.htmlhttp://www.tkk7.com/AstroQi/archive/2008/06/20/209482.html#Feedback0http://www.tkk7.com/AstroQi/comments/commentRss/209482.htmlhttp://www.tkk7.com/AstroQi/services/trackbacks/209482.htmlZ注释QAnnotationQ的配置有越来越行的趋势,Spring 2.5 应q种势Q提供了(jin)完全Z注释配置 Bean、装?Bean 的功能,(zhn)可以用基于注释的 Spring IoC 替换原来Z XML 的配|。本文通过实例详细讲述?Spring 2.5 Z注释 IoC 功能的用?/blockquote>

概述

注释配置相对?XML 配置h很多的优势:(x)

  • 它可以充分利?Java 的反机制获取类l构信息Q这些信息可以有效减配|的工作。如使用 JPA 注释配置 ORM 映射Ӟ我们׃需要指?PO 的属性名、类型等信息Q如果关p表字段?PO 属性名、类型都一_(d)(zhn)甚x(chng)需~写d属性映信息——因些信息都可以通过 Java 反射机制获取?
  • 注释?Java 代码位于一个文件中Q?XML 配置采用独立的配|文Ӟ大多数配|信息在E序开发完成后都不?x)调_(d)如果配置信息?Java 代码攑֜一P有助于增强程序的内聚性。而采用独立的 XML 配置文gQ程序员在编写一个功能时Q往往需要在E序文g和配|文件中不停切换Q这U思维上的不连贯会(x)降低开发效率?

因此在很多情况下Q注释配|比 XML 配置更受Ƣ迎Q注释配|有q一步流行的势。Spring 2.5 的一大增强就是引入了(jin)很多注释c,现在(zhn)已l可以用注释配|完成大部分 XML 配置的功能。在q篇文章里,我们向(zhn)讲qC用注释进?Bean 定义和依赖注入的内容?/p>



回页?/strong>


原来我们是怎么做的

在用注释配|之前,先来回顾一下传l上是如何配|?Bean q完?Bean 之间依赖关系的徏立。下面是 3 个类Q它们分别是 Office、Car ?BossQ这 3 个类需要在 Spring 容器中配|ؓ(f) BeanQ?/p>

Office 仅有一个属性:(x)


清单 1. Office.java
            package com.baobaotao;
            public class Office {
            private String officeNo =”001”;
            //省略 get/setter
            @Override
            public String toString() {
            return "officeNo:" + officeNo;
            }
            }
            

Car 拥有两个属性:(x)


清单 2. Car.java
            package com.baobaotao;
            public class Car {
            private String brand;
            private double price;
            // 省略 get/setter
            @Override
            public String toString() {
            return "brand:" + brand + "," + "price:" + price;
            }
            }
            

Boss 拥有 Office ?Car cd的两个属性:(x)


清单 3. Boss.java
            package com.baobaotao;
            public class Boss {
            private Car car;
            private Office office;
            // 省略 get/setter
            @Override
            public String toString() {
            return "car:" + car + "\n" + "office:" + office;
            }
            }
            

我们?Spring 容器中将 Office ?Car 声明?BeanQƈ注入?Boss Bean 中:(x)下面是用传l?XML 完成q个工作的配|文?beans.xmlQ?/p>
清单 4. beans.xml 以上三个类配置?Bean
            <?xml version="1.0" encoding="UTF-8" ?>
            <beans xmlns="http://www.springframework.org/schema/beans"
            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
            xsi:schemaLocation="http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
            <bean id="boss" class="com.baobaotao.Boss">
            <property name="car" ref="car"/>
            <property name="office" ref="office" />
            </bean>
            <bean id="office" class="com.baobaotao.Office">
            <property name="officeNo" value="002"/>
            </bean>
            <bean id="car" class="com.baobaotao.Car" scope="singleton">
            <property name="brand" value=" U旗 CA72"/>
            <property name="price" value="2000"/>
            </bean>
            </beans>
            

当我们运行以下代码时Q控制台正打?boss 的信息:(x)


清单 5. 试c:(x)AnnoIoCTest.java
            import org.springframework.context.ApplicationContext;
            import org.springframework.context.support.ClassPathXmlApplicationContext;
            public class AnnoIoCTest {
            public static void main(String[] args) {
            String[] locations = {"beans.xml"};
            ApplicationContext ctx =
            new ClassPathXmlApplicationContext(locations);
            Boss boss = (Boss) ctx.getBean("boss");
            System.out.println(boss);
            }
            }
            

q说?Spring 容器已经正确完成?Bean 创徏和装配的工作?/p>



回页?/strong>


使用 @Autowired 注释

Spring 2.5 引入?@Autowired 注释Q它可以对类成员变量、方法及(qing)构造函数进行标注,完成自动装配的工作。来看一下?@Autowired q行成员变量自动注入的代码:(x)


清单 6. 使用 @Autowired 注释?Boss.java
            package com.baobaotao;
            import org.springframework.beans.factory.annotation.Autowired;
            public class Boss {
            @Autowired
            private Car car;
            @Autowired
            private Office office;
            …
            }
            

Spring 通过一?BeanPostProcessor ?@Autowired q行解析Q所以要?@Autowired 起作用必M先在 Spring 容器中声?AutowiredAnnotationBeanPostProcessor Bean?/p>
清单 7. ?@Autowired 注释工作h
            <?xml version="1.0" encoding="UTF-8" ?>
            <beans xmlns="http://www.springframework.org/schema/beans"
            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
            xsi:schemaLocation="http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
            <!-- ?BeanPostProcessor 自动v作用Q对标注 @Autowired ?Bean q行自动注入 -->
            <bean class="org.springframework.beans.factory.annotation.
            AutowiredAnnotationBeanPostProcessor"/>
            <!-- U除 boss Bean 的属性注入配|的信息 -->
            <bean id="boss" class="com.baobaotao.Boss"/>
            <bean id="office" class="com.baobaotao.Office">
            <property name="officeNo" value="001"/>
            </bean>
            <bean id="car" class="com.baobaotao.Car" scope="singleton">
            <property name="brand" value=" U旗 CA72"/>
            <property name="price" value="2000"/>
            </bean>
            </beans>
            

q样Q当 Spring 容器启动ӞAutowiredAnnotationBeanPostProcessor 扫?Spring 容器中所?BeanQ当发现 Bean 中拥?@Autowired 注释时就扑ֈ和其匚wQ默认按cd匚wQ的 BeanQƈ注入到对应的地方中去?/p>

按照上面的配|,Spring 直接采?Java 反射机制?Boss 中的 car ?office q两个私有成员变量进行自动注入。所以对成员变量使用 @Autowired 后,(zhn)大可将它们?setter Ҏ(gu)Q?code>setCar() ?setOffice()Q从 Boss 中删除?/p>

当然Q?zhn)也可以通过 @Autowired Ҏ(gu)法或构造函数进行标注,来看下面的代码:(x)


清单 8. ?@Autowired 注释标注?Setter Ҏ(gu)?/strong>
            package com.baobaotao;
            public class Boss {
            private Car car;
            private Office office;
            @Autowired
            public void setCar(Car car) {
            this.car = car;
            }
            @Autowired
            public void setOffice(Office office) {
            this.office = office;
            }
            …
            }
            

q时Q?code>@Autowired 查找被标注的方法的入参cd?BeanQƈ调用Ҏ(gu)自动注入q些 Bean。而下面的使用Ҏ(gu)则对构造函数进行标注:(x)


清单 9. ?@Autowired 注释标注在构造函C
            package com.baobaotao;
            public class Boss {
            private Car car;
            private Office office;
            @Autowired
            public Boss(Car car ,Office office){
            this.car = car;
            this.office = office ;
            }
            …
            }
            

׃ Boss() 构造函数有两个入参Q分别是 car ?officeQ?code>@Autowired 分别寻扑֒它们cd匚w?BeanQ将它们作ؓ(f) Boss(Car car ,Office office) 的入参来创徏 Boss Bean?/p>



回页?/strong>


当候?Bean 数目不ؓ(f) 1 时的应对Ҏ(gu)

在默认情况下使用 @Autowired 注释q行自动注入ӞSpring 容器中匹配的候?Bean 数目必须有且仅有一个。当找不C个匹配的 Bean ӞSpring 容器抛?BeanCreationException 异常Qƈ指出必须臛_拥有一个匹配的 Bean。我们可以来做一个实验:(x)


清单 10. 候?Bean 数目?0 ?/strong>
            <?xml version="1.0" encoding="UTF-8" ?>
            <beans xmlns="http://www.springframework.org/schema/beans"
            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
            xsi:schemaLocation="http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans-2.5.xsd ">
            <bean class="org.springframework.beans.factory.annotation.
            AutowiredAnnotationBeanPostProcessor"/>
            <bean id="boss" class="com.baobaotao.Boss"/>
            <!-- ?office Bean 注释?-->
            <!-- <bean id="office" class="com.baobaotao.Office">
            <property name="officeNo" value="001"/>
            </bean>-->
            <bean id="car" class="com.baobaotao.Car" scope="singleton">
            <property name="brand" value=" U旗 CA72"/>
            <property name="price" value="2000"/>
            </bean>
            </beans>
            

׃ office Bean 被注释掉?jin),所?Spring 容器中将没有cd?Office ?Bean ?jin),?Boss ?office 属性标注了(jin) @AutowiredQ当启动 Spring 容器Ӟ异常׃生了(jin)?/p>

当不能确?Spring 容器中一定拥有某个类?Bean Ӟ可以在需要自动注入该c?Bean 的地方可以?@Autowired(required = false)Q这{于告诉 SpringQ在找不到匹?Bean 时也不报错。来看一下具体的例子Q?/p>
清单 11. 使用 @Autowired(required = false)
            package com.baobaotao;
            import org.springframework.beans.factory.annotation.Autowired;
            import org.springframework.beans.factory.annotation.Required;
            public class Boss {
            private Car car;
            private Office office;
            @Autowired
            public void setCar(Car car) {
            this.car = car;
            }
            @Autowired(required = false)
            public void setOffice(Office office) {
            this.office = office;
            }
            …
            }
            

当然Q一般情况下Q?@Autowired 的地斚w是需要注?Bean 的,使用?jin)自动注入而又允许不注入的情况一般仅?x)在开发期或测试期到Q如Z(jin)快速启?Spring 容器Q仅引入一些模块的 Spring 配置文gQ,所?@Autowired(required = false) ?x)很用到?/p>

和找不到一个类型匹?Bean 相反的一个错误是Q如?Spring 容器中拥有多个候?BeanQSpring 容器在启动时也会(x)抛出 BeanCreationException 异常。来看下面的例子Q?/p>
清单 12. ?beans.xml 中配|两?Office cd?Bean
            …
            <bean id="office" class="com.baobaotao.Office">
            <property name="officeNo" value="001"/>
            </bean>
            <bean id="office2" class="com.baobaotao.Office">
            <property name="officeNo" value="001"/>
            </bean>
            …
            

我们?Spring 容器中配|了(jin)两个cd?Office cd?BeanQ当?Boss ?office 成员变量q行自动注入ӞSpring 容器无法确定到底要用哪一?BeanQ因此异常发生了(jin)?/p>

Spring 允许我们通过 @Qualifier 注释指定注入 Bean 的名Uͼq样歧义消除了(jin)Q可以通过下面的方法解军_常:(x)


清单 13. 使用 @Qualifier 注释指定注入 Bean 的名U?/strong>
            @Autowired
            public void setOffice(@Qualifier("office")Office office) {
            this.office = office;
            }
            

@Qualifier("office") 中的 office ?Bean 的名Uͼ所?@Autowired ?@Qualifier l合使用Ӟ自动注入的策略就?byType 转变?byName ?jin)?code>@Autowired 可以Ҏ(gu)员变量、方法以?qing)构造函数进行注释,?@Qualifier 的标注对象是成员变量、方法入参、构造函数入参。正是由于注释对象的不同Q所?Spring 不将 @Autowired ?@Qualifier l一成一个注释类。下面是Ҏ(gu)员变量和构造函数入参进行注释的代码Q?/p>

Ҏ(gu)员变量进行注释:(x)


清单 14. Ҏ(gu)员变量?@Qualifier 注释
            public class Boss {
            @Autowired
            private Car car;
            @Autowired
            @Qualifier("office")
            private Office office;
            …
            }
            

Ҏ(gu)造函数入参进行注释:(x)


清单 15. Ҏ(gu)造函数变量?@Qualifier 注释
            public class Boss {
            private Car car;
            private Office office;
            @Autowired
            public Boss(Car car , @Qualifier("office")Office office){
            this.car = car;
            this.office = office ;
            }
            }
            

@Qualifier 只能?@Autowired l合使用Q是?@Autowired 有益的补充。一般来Ԍ@Qualifier Ҏ(gu)法签名中入参q行注释?x)降低代码的可读性,而对成员变量注释则相对好一些?/p>



回页?/strong>


使用 JSR-250 的注?/span>

Spring 不但支持自己定义?@Autowired 的注释,q支持几个由 JSR-250 规范定义的注释,它们分别?@Resource?code>@PostConstruct 以及(qing) @PreDestroy?/p>

@Resource

@Resource 的作用相当于 @AutowiredQ只不过 @Autowired ?byType 自动注入Q面 @Resource 默认?byName 自动注入|了(jin)?code>@Resource 有两个属性是比较重要的,分别?name ?typeQSpring ?@Resource 注释?name 属性解析ؓ(f) Bean 的名字,?type 属性则解析?Bean 的类型。所以如果?name 属性,则?byName 的自动注入策略,而?type 属性时则?byType 自动注入{略。如果既不指?name 也不指定 type 属性,q时通过反射机制使用 byName 自动注入{略?/p>

Resource 注释cM?Spring 发布包的 lib/j2ee/common-annotations.jar cd中,因此在用之前必d其加入到目的类库中。来看一个?@Resource 的例子:(x)


清单 16. 使用 @Resource 注释?Boss.java
            package com.baobaotao;
            import javax.annotation.Resource;
            public class Boss {
            // 自动注入cd?Car ?Bean
            @Resource
            private Car car;
            // 自动注入 bean 名称?office ?Bean
            @Resource(name = "office")
            private Office office;
            }
            

一般情况下Q我们无需使用cM?@Resource(type=Car.class) 的注释方式,因ؓ(f) Bean 的类型信息可以通过 Java 反射从代码中获取?/p>

要让 JSR-250 的注释生效,除了(jin)?Bean cM标注q些注释外,q需要在 Spring 容器中注册一个负责处理这些注释的 BeanPostProcessorQ?/p>
<bean
            class="org.springframework.context.annotation.CommonAnnotationBeanPostProcessor"/>
            

CommonAnnotationBeanPostProcessor 实现?BeanPostProcessor 接口Q它负责扫描使用?JSR-250 注释?BeanQƈ对它们进行相应的操作?/p>

@PostConstruct ?@PreDestroy

Spring 容器中的 Bean 是有生命周期的,Spring 允许?Bean 在初始化完成后以?Bean 销毁前执行特定的操作,(zhn)既可以通过实现 InitializingBean/DisposableBean 接口来定制初始化之后 / 销毁之前的操作Ҏ(gu)Q也可以通过 <bean> 元素?init-method/destroy-method 属性指定初始化之后 / 销毁之前调用的操作Ҏ(gu)。关?Spring 的生命周期,W者在《精?Spring 2.x—企业应用开发精解》第 3 章进行了(jin)详细的描qͼ有兴的读者可以查阅?/p>

JSR-250 为初始化之后/销毁之前方法的指定定义?jin)两个注释类Q分别是 @PostConstruct ?@PreDestroyQ这两个注释只能应用于方法上。标注了(jin) @PostConstruct 注释的方法将在类实例化后调用Q而标注了(jin) @PreDestroy 的方法将在类销毁之前调用?/p>
清单 17. 使用 @PostConstruct ?@PreDestroy 注释?Boss.java
            package com.baobaotao;
            import javax.annotation.Resource;
            import javax.annotation.PostConstruct;
            import javax.annotation.PreDestroy;
            public class Boss {
            @Resource
            private Car car;
            @Resource(name = "office")
            private Office office;
            @PostConstruct
            public void postConstruct1(){
            System.out.println("postConstruct1");
            }
            @PreDestroy
            public void preDestroy1(){
            System.out.println("preDestroy1");
            }
            …
            }
            

(zhn)只需要在Ҏ(gu)前标?@PostConstruct ?@PreDestroyQ这些方法就?x)?Bean 初始化后或销毁之前被 Spring 容器执行?jin)?/p>

我们知道Q不是通过实现 InitializingBean/DisposableBean 接口Q还是通过 <bean> 元素?init-method/destroy-method 属性进行配|,都只能ؓ(f) Bean 指定一个初始化 / 销毁的Ҏ(gu)。但是?@PostConstruct ?@PreDestroy 注释却可以指定多个初始化 / 销毁方法,那些被标?@PostConstruct ?@PreDestroy 注释的方法都?x)在初始?/ 销毁时被执行?/p>

通过以下的测试代码,(zhn)将可以看到 Bean 的初始化 / 销毁方法是如何被执行的Q?/p>
清单 18. 试cM?/strong>
            package com.baobaotao;
            import org.springframework.context.support.ClassPathXmlApplicationContext;
            public class AnnoIoCTest {
            public static void main(String[] args) {
            String[] locations = {"beans.xml"};
            ClassPathXmlApplicationContext ctx =
            new ClassPathXmlApplicationContext(locations);
            Boss boss = (Boss) ctx.getBean("boss");
            System.out.println(boss);
            ctx.destroy();// 关闭 Spring 容器Q以触发 Bean 销毁方法的执行
            }
            }
            

q时Q?zhn)看到标注?jin) @PostConstruct ?postConstruct1() Ҏ(gu)在 Spring 容器启动Ӟ创徏 Boss Bean 的时候被触发执行Q而标注了(jin) @PreDestroy 注释?preDestroy1() Ҏ(gu)在 Spring 容器关闭前销?Boss Bean 的时候被触发执行?/p>



回页?/strong>


使用 <context:annotation-config/> 化配|?/span>

Spring 2.1 d?jin)一个新?context ?Schema 命名I间Q该命名I间Ҏ(gu)释驱动、属性文件引入、加载期l入{功能提供了(jin)便捷的配|。我们知道注释本w是不会(x)做Q何事情的Q它仅提供元数据信息。要使元数据信息真正起作用,必须让负责处理这些元数据的处理器工作h?

而我们前面所介绍?AutowiredAnnotationBeanPostProcessor ?CommonAnnotationBeanPostProcessor 是处理q些注释元数据的处理器。但是直接在 Spring 配置文g中定义这?Bean 昑־比较W拙。Spring 为我们提供了(jin)一U方便的注册q些 BeanPostProcessor 的方式,q就?<context:annotation-config/>。请看下面的配置Q?/p>
清单 19. 调整 beans.xml 配置文g
            <?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: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">
            <context:annotation-config/>
            <bean id="boss" class="com.baobaotao.Boss"/>
            <bean id="office" class="com.baobaotao.Office">
            <property name="officeNo" value="001"/>
            </bean>
            <bean id="car" class="com.baobaotao.Car" scope="singleton">
            <property name="brand" value=" U旗 CA72"/>
            <property name="price" value="2000"/>
            </bean>
            </beans>
            

<context:annotationconfig/> 隐式地?Spring 容器注册 AutowiredAnnotationBeanPostProcessor?code>CommonAnnotationBeanPostProcessor?code>PersistenceAnnotationBeanPostProcessor 以及(qing) equiredAnnotationBeanPostProcessor q?4 ?BeanPostProcessor?/p>

在配|文件中使用 context 命名I间之前Q必d <beans> 元素中声?context 命名I间?/p>



回页?/strong>


使用 @Component

虽然我们可以通过 @Autowired ?@Resource ?Bean cM使用自动注入功能Q但?Bean q是?XML 文g中通过 <bean> q行定义 —?也就是说Q在 XML 配置文g中定?BeanQ通过 @Autowired ?@Resource ?Bean 的成员变量、方法入参或构造函数入参提供自动注入的功能。能否也通过注释定义 BeanQ从 XML 配置文g中完全移?Bean 定义的配|呢Q答案是肯定的,我们通过 Spring 2.5 提供?@Component 注释可以达到这个目标了(jin)?/p>

下面Q我们完全用注释定?Bean q完?Bean 之间装配Q?/p>
清单 20. 使用 @Component 注释?Car.java
            package com.baobaotao;
            import org.springframework.stereotype.Component;
            @Component
            public class Car {
            …
            }
            

仅需要在cd义处Q?@Component 注释可以将一个类定义?Spring 容器中的 Bean。下面的代码?Office 定义Z?BeanQ?/p>
清单 21. 使用 @Component 注释?Office.java
            package com.baobaotao;
            import org.springframework.stereotype.Component;
            @Component
            public class Office {
            private String officeNo = "001";
            …
            }
            

q样Q我们就可以?Boss cM通过 @Autowired 注入前面定义?Car ?Office Bean ?jin)?/p>
清单 22. 使用 @Component 注释?Boss.java
            package com.baobaotao;
            import org.springframework.beans.factory.annotation.Autowired;
            import org.springframework.beans.factory.annotation.Required;
            import org.springframework.beans.factory.annotation.Qualifier;
            import org.springframework.stereotype.Component;
            @Component("boss")
            public class Boss {
            @Autowired
            private Car car;
            @Autowired
            private Office office;
            …
            }
            

@Component 有一个可选的入参Q用于指?Bean 的名Uͼ?Boss 中,我们将 Bean 名称定义?#8220;boss”。一般情况下QBean 都是 singleton 的,需要注?Bean 的地方仅需要通过 byType {略可以自动注入了(jin)Q所以大可不必指?Bean 的名U?/p>

在?@Component 注释后,Spring 容器必须启用cL描机制以启用注释驱动 Bean 定义和注释驱?Bean 自动注入的策略。Spring 2.5 ?context 命名I间q行?jin)扩展,提供了(jin)这一功能Q请看下面的配置Q?/p>
清单 23. 化版?beans.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: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">
            <context:component-scan base-package="com.baobaotao"/>
            </beans>
            

q里Q所有通过 <bean> 元素定义 Bean 的配|内容已l被U除Q仅需要添加一?<context:component-scan/> 配置px(chng)有问题(sh)(jin)——Spring XML 配置文g得到?jin)极致的化(当然配置元数据还是需要的Q只不过以注释Ş式存在Ş?jin)?j)?lt;context:component-scan/> ?base-package 属性指定了(jin)需要扫描的cdQ类包及(qing)光归子包中所有的c都?x)被处理?/p>

<context:component-scan/> q允许定义过滤器基包下的某些类U_或排除。Spring 支持以下 4 U类型的qo(h)方式Q通过下表说明Q?/p>
?1. 扫描qo(h)方式
qo(h)器类?/th> 说明
注释 假如 com.baobaotao.SomeAnnotation 是一个注释类Q我们可以将使用该注释的c过滤出来?/td>
cd指定 通过全限定类名进行过滤,如?zhn)可以指定?com.baobaotao.Boss U_扫描Q而将 com.baobaotao.Car 排除在外?/td>
正则表达?/td> 通过正则表达式定义过滤的c,如下所C:(x) com\.baobaotao\.Default.*
AspectJ 表达?/td> 通过 AspectJ 表达式定义过滤的c,如下所C:(x) com. baobaotao..*Service+

下面是一个简单的例子Q?/p>
<context:component-scan base-package="com.baobaotao">
            <context:include-filter type="regex"
            expression="com\.baobaotao\.service\..*"/>
            <context:exclude-filter type="aspectj"
            expression="com.baobaotao.util..*"/>
            </context:component-scan>
            

值得注意的是 <context:component-scan/> 配置不但启用了(jin)对类包进行扫描以实施注释驱动 Bean 定义的功能,同时q启用了(jin)注释驱动自动注入的功能(卌隐式地在内部注册?AutowiredAnnotationBeanPostProcessor ?CommonAnnotationBeanPostProcessorQ,因此当?<context:component-scan/> 后,可以将 <context:annotation-config/> U除?jin)?/p>

默认情况下通过 @Component 定义?Bean 都是 singleton 的,如果需要用其它作用范围的 BeanQ可以通过 @Scope 注释来达到目标,如以下代码所C:(x)


清单 24. 通过 @Scope 指定 Bean 的作用范?/strong>
            package com.baobaotao;
            import org.springframework.context.annotation.Scope;
            …
            @Scope("prototype")
            @Component("boss")
            public class Boss {
            …
            }
            

q样Q当?Spring 容器中获?boss Bean Ӟ每次q回的都是新的实例了(jin)?/p>



回页?/strong>


采用hҎ(gu)语义的注?/span>

Spring 2.5 中除?jin)提?@Component 注释外,q定义了(jin)几个拥有Ҏ(gu)语义的注释,它们分别是:(x)@Repository?code>@Service ?@Controller。在目前?Spring 版本中,q?3 个注释和 @Component 是等效的Q但是从注释cȝ命名上,很容易看?gu)?3 个注释分别和持久层、业务层和控制层QWeb 层)(j)相对应。虽然目前这 3 个注释和 @Component 相比没有什么新意,?Spring 在以后的版本中为它们添加特D的功能。所以,如果 Web 应用E序采用?jin)经典的三层分层l构的话Q最好在持久层、业务层和控制层分别采用 @Repository?code>@Service ?@Controller 对分层中的类q行注释Q而用 @Component 寚w些比较中立的c进行注释?/p>



回页?/strong>


注释配置?XML 配置的适用场合

是否有了(jin)q些 IOC 注释Q我们就可以完全摒除原来 XML 配置的方式呢Q答案是否定的。有以下几点原因Q?/p>

  • 注释配置不一定在先天上优?sh)?XML 配置。如?Bean 的依赖关pL固定的,Q如 Service 使用?jin)哪几?DAO c)(j)Q这U配|信息不?x)在部v时发生调_(d)那么注释配置优(sh) XML 配置Q反之如果这U依赖关pM(x)在部|时发生调整QXML 配置昄又优?sh)注释配|,因ؓ(f)注释是对 Java 源代码的调整Q?zhn)需要重新改写源代码q新编译才可以实施调整?
  • 如果 Bean 不是自己~写的类Q如 JdbcTemplate?code>SessionFactoryBean {)(j)Q注释配|将无法实施Q此?XML 配置是唯一可用的方式?
  • 注释配置往往是类U别的,?XML 配置则可以表现得更加灉|。比如相比于 @Transaction 事务注释Q?aop/tx 命名I间的事务配|更加灵zd单?

所以在实现应用中,我们往往需要同时用注释配|和 XML 配置Q对于类U别且不?x)发生变动的配置可以优先考虑注释配置Q而对于那些第三方cM?qing)容易发生调整的配置则应优先考虑使用 XML 配置。Spring ?x)在具体实?Bean 创徏?Bean 注入之前这两种配置方式的元信息融合在一赗?/p>



回页?/strong>


Spring ?2.1 以后Ҏ(gu)释配|提供了(jin)强力的支持,注释配置功能成ؓ(f) Spring 2.5 的最大的亮点之一。合理地使用 Spring 2.5 的注释配|,可以有效减少配置的工作量Q提高程序的内聚性。但是这q不意味着传统 XML 配置走向消亡,在第三方c?Bean 的配|,以及(qing)那些诸如数据源、缓存池、持久层操作模板cR事务管理等内容的配|上QXML 配置依然拥有不可替代的地位?/p>

Astro.Qi 2008-06-20 16:08 发表评论
]]>
վ֩ģ壺 999|| һƵѹۿ| ޳_վͼƬ| Ʒ޵һ| ѹվ߹ۿͼ| þëƬѿһ| 99þþƷ| õƵ| 벻Ļ18| ˿ۺ| ޻߹ۿ| ޳aƬ߹ۿ| պëƬƵ| þѶƷ˾| ɫavѹۿ| ޹һƷ| һ߲߲| ͼƬУ԰С˵| ³˿Ƭһ| ޾ƷѶ| ޹㽶ˬAVƬþ | ؼëƬѲ| ٸһ| ߹ۿƵ| ޾ƷMV߹ۿ| avƬþ| ޾ƷպAV | ŮԸ߰վ| ˾ҹƵѹ| þҹҹ³³Ƭ| ԲľƷƵѿ| AVպƷþþþ| ޳a޳av| Ƶ| aaƵ| ԴƵ| ѾþþƷ99þ| ŮƵվ| þ㽶߿ۿƬ| 337Pձŷ޴󵨾Ʒ| 97Ƶѹۿ|