`
daweiangel
  • 浏览: 325768 次
  • 性别: Icon_minigender_1
  • 来自: 北京
文章分类
社区版块
存档分类

Spring 2.5新特性-第二部分-Spring MVC中的新特性

阅读更多
Spring 框架从创建伊始就致力于为复杂问题提供强大的、非侵入性的解决方案。Spring 2.0当中为缩减XML配置文件数量引入定制命名空间功能,从此它便深深植根于核心Spring框架(aop、context、jee、jms、 lang、tx和util命名空间)、Spring Portfolio项目(例如Spring Security)和非Spring项目中(例如CXF)。

Spring 2.5推出了一整套注解,作为基于XML的配置的替换方案。注解可用于Spring管理对象的自动发现、依赖注入、生命周期方法、Web层配置和单元/集成测试。

探索Spring 2.5中引入的注解技术系列文章由三部分组成,本文是其中的第二篇,它主要讲述了Web层中的注解支持。最后一篇文章将着重介绍可用于集成和测试的其它特性。

这个系列文章的第一部分论述了Java注解(annotation)是如何代替XML来配置Spring管理对象和依赖注入的。我们再用一个例子回顾一下:
@Controller
 public class ClinicController {

    private final Clinic clinic;

    @Autowired
    public ClinicController(Clinic clinic) {
       this.clinic = clinic;
    }
    ...


@Controller表明ClinicController是Web层组件,@Autowired请求一个被依赖注入的Clinic实例。这个例子只需要少量的XML语句就能使容器识别两个注解,并限定组件的扫描范围:

<context:component-scan base-package="org.springframework.samples.petclinic"/>


这对Web层可谓是个福音,因为在这层Spring的XML配置文件已日益臃肿,甚至可能还不如层下的配置来得有用。控制器掌握着许多属性,例如视图名称、表单对象名称和验证器类型,这些多是关乎配置的,甚少关于依赖注入的。通过bean定义继承,或者避免配置变化不是很频繁的属性,也可以有效的管理类似的配置。不过以我的经验,很多开发人员都不会这样做,结果就是XML文件总比实际需要的要庞大。不过 @Controller和@Autowired对Web层的配置会产生积极的作用。

在系列文章的第二部分我们将继续讨论这个问题,并浏览Spring 2.5在Web层的注解技术。这些注解被非正式的称为@MVC,它涉及到了Spring MVC和Spring Porlet MVC,实际上本文讨论的大部分功能都可以应用在这两个框架上。
从Controller到@Controller

与第一部分讨论的注解相比,@MVC已不只是作为配置的一种替换方案这样简单了,考虑下面这个著名的Spring MVC控制器签名:

public interface Controller {
    ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse 
response) throws Exception;
 }


所有的Spring MVC控制器要么直接实现Controller接口,要么就得扩展类似AbstractController、 SimpleFormController、 MultiActionController或AbstractWizardFormController这样的基类实现。正是Controller接口允许Spring MVC的DispatcherServlet把所有上述对象都看作是“处理器(handlers)”,并在一个名为 SimpleControllerHandlerAdapter的适配器的帮助下调用它们。

@MVC从三个重要的方面改变了这个程序设计模型:

   1. 不需要任何接口或者基类。
   2. 允许有任意数量的请求处理方法。
   3. 在方法签名上具有高度的灵活性。

考虑到以上三个要点,就可以说很公平的说@MVC不仅仅是个替换方案了,它将会是Spring MVC的控制器技术演变过程中下一个重要步骤。

DispatcherServlet在名为AnnotationMethodHandlerAdapter的适配器帮助下调用被注解的控制器。正是这个适配器做了大量工作支持我们此后将会讨论的注解,同时也是它有效的取代了对于控制器基类的需求。
@RequestMapping简介

我们还是从一个类似于传统的Spring MVC Controller控制器开始:

@Controller
 public class AccountsController {

    private AccountRepository accountRepository;

    @Autowired
    public AccountsController(AccountRepository accountRepository) {
       this.accountRepository = accountRepository;
    }

    @RequestMapping("/accounts/show")
    public ModelAndView show(HttpServletRequest request,
                             HttpServletResponse response) throws Exception {
       String number = ServletRequestUtils.getStringParameter(request, "number");
       ModelAndView mav = new ModelAndView("/WEB-INF/views/accounts/show.jsp");
       mav.addObject("account", accountRepository.findAccount(number));
       return mav;
    }

}

此处与以往的不同在于,这个控制器并没有扩展Controller接口,并且它用@RequestMapping注解指明show()是映射到 URI路径 “/accounts/show”的请求处理方法。除此以外,其余代码都是一个典型的Spring MVC控制器应有的内容。

在将上述的方法完全转化到@MVC后,我们会再回过头来看@RequestMapping,但是在此之前还有一点需要提请注意,上面的请求映射URI也可匹配带有任意扩展名的URI路径,例如:

/accounts/show.htm
 /accounts/show.xls
 /accounts/show.pdf
 ...


灵活的请求处理方法签名

我们曾经承诺过要提供灵活的方法签名,现在来看一下成果。输入的参数中移除了响应对象,增加了一个代表模型的Map;返回的不再是ModelAndView,而是一个字符串,指明呈现响应时要用的视图名字:

@RequestMapping("/accounts/show")
 public String show(HttpServletRequest request, Map<String, Object> model)
 throws Exception {
    String number = ServletRequestUtils.getStringParameter(request, "number");
    model.put("account", accountRepository.findAccount(number));
    return "/WEB-INF/views/accounts/show.jsp";
 }


Map输入参数是一个“隐式的”模型,对于我们来说在调用方法前创建它很方便,其中添加的键—值对数据便于在视图中解析应用。本例视图为show.jsp页面。

@MVC可以接受多种类型的输入参数,例如 HttpServletRequest/HttpServletResponse、HttpSession、Locale、InputStream、 OutputStream、File[]等等,它们的顺序不受任何限制;同样它也允许多种返回类型,例如ModelAndView、Map、 String,或者什么都不返回。你可以查看@RequestMapping的JavaDoc以了解它支持的所有输入和返回参数类型。

有种令人感兴趣的情形是当方法没有指定视图时(例如返回类型为void)会有什么事情发生,按照惯例DispatcherServlet要再使用请求URI的路径信息,不过要移去前面的斜杠和扩展名。让我们把返回类型改为void:

@RequestMapping("/accounts/show")
 public void show(HttpServletRequest request, Map<String, Object> model) throws Exception {
    String number = ServletRequestUtils.getStringParameter(request, "number");
    model.put("account", accountRepository.findAccount(number));
 }


对于给定的请求处理方法和“/accounts/show”的请求映射,我们可以期望DispatcherServlet能够获得“accounts/show”的默认视图名称,当它与如下适当的视图解析器结合共同作用时,会产生与前面指明返回视图名同样的结果:

<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="prefix" value="/WEB-INF/views/" />
    <property name="suffix" value=".jsp" />
 </bean>


强烈推荐视图名称依赖惯例的方式,因为这样可以从控制器代码中消除硬编码的视图名称。如果你想定制 DispatcherServlet获取默认视图名的方式,就在servlet上下文环境中配置一个你自己的 RequestToViewNameTranslator实现,并为其bean id赋名为“viewNameTranslator”。
用@RequestParam提取和解析参数

@MVC另外一个特性是其提取和解析请求参数的能力。让我们继续重构上面的方法,并在其中添加@RequestParam注解:

@RequestMapping("/accounts/show")
 public void show(@RequestParam("number") String number, Map<String, Object> model) {
    model.put("account", accountRepository.findAccount(number));
 }

这里@RequestParam注解可以用来提取名为“number”的String类型的参数,并将之作为输入参数传入。 @RequestParam支持类型转换,还有必需和可选参数。类型转换目前支持所有的基本Java类型,你可通过定制的PropertyEditors 来扩展它的范围。下面是一些例子,其中包括了必需和可选参数:

@RequestParam(value="number", required=false) String number
 @RequestParam("id") Long id
 @RequestParam("balance") double balance
 @RequestParam double amount


注意,最后一个例子没有提供清晰的参数名。当且仅当代码带调试符号编译时,结果会提取名为“amount ”的参数,否则,将抛出IllegalStateException异常,因为当前的信息不足以从请求中提取参数。由于这个原因,在编码时最好显式的指定参数名。
继续@RequestMapping的讨论

把@RequestMapping放在类级别上是合法的,这可令它与方法级别上的@RequestMapping注解协同工作,取得缩小选择范围的效果,下面是一些例子。

类级别:

RequestMapping("/accounts/*")

方法级别:
@RequestMapping(value="delete", method=RequestMethod.POST)
@RequestMapping(value="index", method=RequestMethod.GET, params="type=checking")
@RequestMapping


第一个方法级的请求映射和类级别的映射结合,当HTTP方法是POST时与路径“/accounts/delete”匹配;第二个添加了一个要求,就是名为“type”的请求参数和其值“checking”都需要在请求中出现;第三个根本就没有指定路径,这个方法匹配所有的 HTTP方法,如果有必要的话可以用它的方法名。下面改写我们的方法,使它可以依靠方法名进行匹配,程序如下:

@Controller
 @RequestMapping("/accounts/*")
 public class AccountsController {

    @RequestMapping(method=RequestMethod.GET)
    public void show(@RequestParam("number") String number, Map<String, Object> model)
   {
       model.put("account", accountRepository.findAccount(number));
    }
    ...


方法匹配的请求是“/accounts/show”,依据的是类级别的@RequestMapping指定的匹配路径“/accounts/*”和方法名“show”。
消除类级别的请求映射

Web层注解频遭诟病是有事实依据的,那就是嵌入源代码的URI路径。这个问题很好矫正,URI路径和控制器类之间的匹配关系用XML配置文件去管理,只在方法级的映射中使用@RequestMapping注解。

我们将配置一个ControllerClassNameHandlerMapping,它使用依赖控制器类名字的惯例,将URI映射到控制器:

<bean class="org.springframework.web.servlet.mvc.support.ControllerClassNameHandlerMapping"/>

现在“/accounts/*”这样的请求都被匹配到AccountsController上,它与方法级别上的@RequestMapping注解协作的很好,只要添加上方法名就能够完成上述映射。此外,既然我们的方法并不会返回视图名称,我们现在就可以依据惯例匹配类名、方法名、URI路径和视图名。

当@Controller被完全转换为@MVC后,程序的写法如下:

@Controller
 public class AccountsController {

     private AccountRepository accountRepository;

     @Autowired
    public AccountsController(AccountRepository accountRepository) {
       this.accountRepository = accountRepository;
    }

     @RequestMapping(method=RequestMethod.GET)
    public void show(@RequestParam("number") String number, Map<String, Object> model)
   {
       model.put("account", accountRepository.findAccount(number));
    }
    ...


对应的XML配置文件如下:

<context:component-scan base-package="com.abc.accounts"/>

  <bean class="org.springframework.web.servlet.mvc.support.ControllerClassNameHandlerMapping"/>

  <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="prefix" value="/WEB-INF/views/" />
    <property name="suffix" value=".jsp" />
 </bean>


你可以看出这是一个最精减的XML。程序里注解中没有嵌入URI路径,也没有显式指定视图名,请求处理方法也只有很简单的一行,方法签名与我们的需求精准匹配,其它的请求处理方法也很容易添加。不需要基类,也不需要XML(至少也是没有直接配置控制器),我们就能获得上述所有优势。

也许接下来你就可以看到,这种程序设计模型是多么有效了。
@MVC表单处理

一个典型的表单处理场景包括:获得可编辑对象,在编辑模式下显示它持有的数据、允许用户提交并最终进行验证和保存变化数据。Spring MVC提供下列几个特性辅助进行上述所有活动:数据绑定机制,完全用从请求参数中获得的数据填充一个对象;支持错误处理和验证;JSP表单标记库;基类控制器。使用@MVC,除了由于@ModelAttribute、@InitBinder和@SessionAttributes这些注解的存在而不再需要基类控制器外,其它一切都不需要改变。
@ModelAttribute注解

看一下这些请求处理方法签名:

@RequestMapping(method=RequestMethod.GET)
 public Account setupForm() {
    ...
 }

  @RequestMapping(method=RequestMethod.POST)
 public void onSubmit(Account account) {
    ...
 }


它们是非常有效的请求处理方法签名。第一个方法处理初始的HTTP GET请求,准备被编辑的数据,返回一个Account对象供Spring MVC表单标签使用。第二个方法在用户提交更改时处理随后的HTTP POST请求,并接收一个Account对象作为输入参数,它是Spring MVC的数据绑定机制用请求中的参数自动填充的。这是一个非常简单的程序模型。

Account对象中含有要被编辑的数据。在Spring MVC的术语当中,Account被称作是表单模型对象。这个对象必须通过某个名称让表单标签(还有数据绑定机制)知道它的存在。下面是从JSP页面中截取的部分代码,引用了一个名为“account”的表单模型对象:
<form:form modelAttribute="account" method="post">

    Account Number: <form:input path="number"/><form:errors path="number"/>
    ...
 </form>


即使我们没有在任何地方指定“account”的名称,这段JSP程序也会和上面所讲的方法签名协作的很好。这是因为@MVC用返回对象的类型名称作为默认值,因此一个Account类型的对象默认的就对应一个名为“account”的表单模型对象。如果默认的不合适,我们就可以用 @ModelAttribute来改变它的名称,如下所示:

@RequestMapping(method=RequestMethod.GET)
 public @ModelAttribute("account") SpecialAccount setupForm() {
    ...
 }
  @RequestMapping(method=RequestMethod.POST)
 public void update(@ModelAttribute("account") SpecialAccount account) {
    ...
 }

@ModelAttribute同样也可放在方法级的位置上,取得的效果稍有不同:

@ModelAttribute
 public Account setupModelAttribute() {
    ...
 }


此处setupModelAttribute()不是一个请求处理方法,而是任何请求处理方法被调用之前,用来准备表单项模型对象的一个方法。对那些熟悉 Spring MVC的老用户来说,这和SimpleFormController的formBackingObject()方法是非常相似的。

最初的GET方法中我们得到一次表单模型对象,在随后的POST方法中当我们依靠数据绑定机制用用户所做的改变覆盖已有的Account对象时,我们会第二次得到它,在这种表单处理场景中把@ModelAttribute放在方法上是很有用的。当然,作为一种两次获得对象的替换方案,我们也可以在两次请求过程中将它保存进HTTP的会话(session),这就是我们下面将要分析的情况。
用@SessionAttributes存储属性

@SessionAttributes注解可以用来指定请求过程中要放进session中的表单模型对象的名称或类型,下面是一些例子:

@Controller
 @SessionAttributes("account")
 public class AccountFormController {
    ...
 }

  @Controller
 @SessionAttributes(types = Account.class)
 public class AccountFormController {
    ...
 }


根据上面的注解,AccountFormController会在初始的GET方法和随后的POST方法之间,把名为 “account”的表单模型对象(或者象第二个例子中的那样,把所有Account类型的表单模型对象)存入HTTP会话(session)中。不过,当有改变连续发生的时候,就应当把属性对象从会话中移除了。我们可以借助SessionStatus实例来做这件事,如果把它添加进onSubmit的方法签名中,@MVC会完成这个任务:

@RequestMapping(method=RequestMethod.POST)
 public void onSubmit(Account account, SessionStatus sessionStatus) {
    ...
    sessionStatus.setComplete(); // Clears @SessionAttributes
 }


定制数据绑定

有时数据绑定需要定制,例如我们也许需要指定必需填写的域,或者需要为日期、货币金额等类似事情注册定制的PropertyEditors。用@MVC实现这些功能是非常容易的:

@InitBinder
 public void initDataBinder(WebDataBinder binder) {
    binder.setRequiredFields(new String[] {"number", "name"});
 }


@InitBinder注解的方法可以访问@MVC用来绑定请求参数的DataBinder实例,它允许我们为每个控制器定制必须项。
数据绑定结果和验证

数据绑定也许会导致类似于类型转换或域缺失的错误。不管发生什么错误,我们都希望能返回到编辑的表单,让用户自行更正。要想实现这个目的,我们可直接在方法签名的表单模型对象后面追加一个BindingResult对象,例程如下:

@RequestMapping(method=RequestMethod.POST)
 public ModelAndView onSubmit(Account account, BindingResult bindingResult) {
    if (bindingResult.hasErrors()) {
       ModelAndView mav = new ModelAndView();
       mav.getModel().putAll(bindingResult.getModel());
       return mav;
    }
    // Save the changes and redirect to the next view...
 }


发生错误时我们返回到出现问题的视图,并把从BindingResult得到的属性增加到模型上,这样特定域的错误就能够反馈给用户。要注意的是,我们并没有指定一个显式的视图名,而是允许DispatcherServlet依靠与入口URI路径信息匹配的默认视图名。

调用Validator对象并把BindingResult传给它,仅这一行代码就可实现验证操作。这允许我们在一个地方收集绑定和验证错误:

@RequestMapping(method=RequestMethod.POST)
 public ModelAndView onSubmit(Account account, BindingResult bindingResult) {
    accountValidator.validate(account, bindingResult);
    if (bindingResult.hasErrors()) {
       ModelAndView mav = new ModelAndView();
       mav.getModel().putAll(bindingResult.getModel());
       return mav;
    }
    // Save the changes and redirect to the next view...
 }


现在是时候结束我们的Spring 2.5 Web层注解(非正式称法为@MVC)之旅了。
总结

Web层的注解已经证明是相当有用的,不仅是因为它能够大大减少XML配置文件的数量,而且还在于它能成就一个可自由访问 Spring MVC控制器技术的精致、灵活和简洁的程序设计模型。我们强烈推荐使用“惯例优先原则(convention-over-configuration)” 特性,以及以处理器映射为中心的策略给控制器派发请求,避免在源码中嵌入URI路径或是定义显式的视图名引用。

最后是本文没有讨论,但值得关注的一些非常重要的Spring MVC扩展。最新发布的Spring Web Flow版本2添加了一些特性,例如基于JSF视图的Spring MVC、Spring的JavaScript库,还有支持更先进编辑场景的高级状态和导航管理。

查看英文原文:Spring 2.5: New Features in Spring MVC。
分享到:
评论

相关推荐

    Spring2.5新特性.rar

    Spring2.5的新特性-第一部分.doc 本文将主要关注于简化的配置和在Spring应用程序上下文(application context)核心新增的基于注解的功能;...Spring_2.5新特性-第二部分-Spring_MVC中的新特性.doc

    spring2.5的一个新特性

    标题中的“spring2.5的一个新特性”指的Spring框架的第2.5版本中引入的重要更新。Spring是一个广泛使用的Java企业级应用开发框架,它提供了丰富的功能来简化应用程序的构建,包括依赖注入、面向切面编程(AOP)、...

    struts-2.5.26-min-lib.zip

    Struts 2是Java平台上的一个开源MVC框架,它为构建基于J2EE的Web应用程序提供了强大的支持。Struts 2.5.26-min-lib.zip是一个精简版的库文件集合,主要包含了运行Struts 2应用程序所需的最小依赖库。这个压缩包可能...

    struts2.1+spring2.5+hibernate3.3整合之第一步(spring2.5+hibernate3.3)

    Struts2.1、Spring2.5和Hibernate3.3是经典的Java企业级开发框架组合,它们各自在应用程序的不同层面提供了强大的支持。本篇将详细阐述这三个组件的整合过程,以及各自的核心特性。 首先,Struts2是一个基于MVC设计...

    Spring2.5-中文参考手册 chm格式

    《Spring2.5-中文参考手册》是一部针对Spring框架2.5版本的详尽指南,旨在帮助开发者深入了解和掌握这一核心Java企业级开发框架。Spring以其模块化、依赖注入和面向切面编程(AOP)等特性,在Java应用开发领域占据着...

    spring2.5参考手册(spring-reference.pdf)

    - **版本历史**: Spring 2.5是Spring框架发展过程中的一个重要版本,相较于早期版本,它引入了许多新的特性以适应不断变化的开发需求和技术趋势。 #### 2. Spring的核心模块 - **IoC容器**: 提供了依赖注入功能,...

    spring2.5源码

    Spring MVC在2.5版本中也有了不少提升,包括对RESTful风格的支持、更灵活的视图解析、模型属性绑定以及错误处理等。`@RequestMapping`注解的引入,使得处理HTTP请求变得更加简洁。 7. **资源访问** Spring 2.5...

    spring2.5中文参考手册

    5. **MVC框架**:Spring MVC是Spring框架的一部分,用于构建Web应用程序。它提供了一个模型-视图-控制器的架构模式,使得开发者可以更专注于业务逻辑,而不需要处理太多的HTTP细节。 6. **单元测试**:Spring支持...

    struts1和2,spring2.5,hibernate3.2 jar包下载

    在Spring2.5版本中,它引入了对Java 5特性的支持,如枚举、注解和泛型。核心特性包括IoC容器,允许开发者通过XML或注解进行bean的管理和装配;AOP用于实现跨切面的关注点,如日志、事务管理等。Spring2.5还加强了与...

    Spring2.5开发简明教程中文版(1-4章有书签)

    **第二章:Spring核心容器** 这一章深入讲解了Spring的核心组件——核心容器。核心容器包括BeanFactory和ApplicationContext,它们负责管理应用对象的生命周期和配置。读者将学习如何创建Bean定义,理解属性注入,...

    简单易懂的Spring2.5简明教程

    Spring 2.5是其发展过程中的一个重要版本,它为开发者提供了许多改进和新特性。本教程旨在为初学者提供一个简洁易懂的Spring 2.5入门指南。 1. **IoC(Inversion of Control)容器**:Spring的核心特性之一是控制...

    Hibernate3.2 Struts2.0 Spring2.5中文参考文档

    Spring MVC是Spring框架的一部分,提供了类似于Struts2的MVC实现。Spring的核心特性是DI,允许开发者在运行时动态地将对象组合在一起,降低了组件之间的耦合。AOP则允许我们编写关注点分离的代码,比如日志、事务...

    Spring2.5 Struts2.0 TopLink Ext2例子

    标题 "Spring2.5 Struts2.0 TopLink Ext2例子" 涉及到的是一个集成使用四个关键开源框架的示例项目,这些框架在Web应用开发中扮演着重要角色。下面将详细介绍这些框架以及它们如何协同工作。 1. **Spring**(2.5...

    Spring2.5学习笔记

    ### Spring2.5 学习笔记详解 #### 一、Spring 框架简介 Spring 是一个开源的轻量级 Java 开发框架,主要用于简化企业级应用的开发工作。Spring 提供了一系列强大的功能,比如控制反转 (Inversion of Control, IOC)...

    spring2.5学习PPT 传智博客

    Spring 2.5 是一个重要的版本,它包含了许多关键特性,如控制反转(IoC)和面向切面编程(AOP),旨在简化企业级Java应用的开发。本大纲涵盖了从环境搭建到高级特性的详细讲解。 1. **全面阐释Spring及其功能** - ...

    spring2.5 api以及源代码

    在"压缩包子文件的文件名称列表"中,"cutfile.exe"可能是一个剪切或处理文件的工具,而"spring2.5.5_API.rar.f2"看起来像是Spring 2.5.5 API文档的一部分,但文件名中的".f2"可能表示它是分卷压缩文件的第二部分,...

    spring 2.5

    这个版本引入了许多新特性,优化了已有功能,并且为后续版本奠定了坚实的基础。以下是对Spring 2.5核心知识点的详细阐述: 1. **依赖注入(Dependency Injection, DI)增强**: - Spring 2.5对依赖注入进行了显著...

    string 帮助手册(spring2.5)

    《Spring 2.5 帮助手册》是Spring框架2.5版本的官方文档,旨在为开发者提供全面且深入的指导。Spring框架是Java开发领域中的一个核心框架,尤其在企业级应用开发中占据重要地位。它通过依赖注入(Dependency ...

    spring2.5常用到的jar包

    Spring 2.5版本是其历史上的一个重要版本,它引入了许多新特性并优化了已有的功能。以下是一些关于Spring 2.5中常用jar包及其功能的详细解释: 1. **spring-core.jar**:这是Spring框架的基础,包含了IoC...

Global site tag (gtag.js) - Google Analytics