简介
从诞生之初,Spring 框架就坚守它的宗旨:简化企业级应用开发,同时给复杂问题提供强大的、非侵入性解决方案。一年前发布的 Spring2.0 就把这些主题推到了一个新的高度。XML Schema 的支持和自定义命名空间的使用大大减少了基于 XML 的配置。使用 Java5 及更新版本 java 的开发人员如今可以利用植入了像泛型(generic)和注解等新语言特性的 Spring 库。最近,和 AspectJ 表达式语言的紧密集成,使得以非侵入方式添加跨越定义良好的 Spring 管理对象分组的行为成为可能。
新发布的 Spring2.5 继续坚持了这个发展趋向,特别是为那些使用 Java 5 或更新版本 java 的开发人员提供了进一步简化而强大的新特性。这些新特性包括:注解驱动的依赖性注入(annotation-driven dependency injection),使用注解而非 XML 元数据来自动侦测 classpath 上的 Spring 组件,注解对生命周期方法的支持,一个新的 web 控制器模型将请求映射到加注解的方法上,在测试框架中支持 Junit4,Spring XML 命名空间的新增内容,等等。
本文是探讨这些新特性的 3 篇系列文章中的第一篇。本文将主要关注于简化的配置和在 Spring 应用程序上下文(application context)核心新增的基于注解的功能;第二篇文章将涵盖 web 层可用的新特性;最后一篇文章将着重介绍集成和测试的新增性能。这一系列的三篇文章中引用的例子都基于 Spring PetClinic 应用程序范例。此范例最近被重构以用于展示 Spring 最新功能,并被包含于 Spring 2.5 的发布下载包中,可以从 Spring Framework 下载网页下载。查看"samples/petclinic"目录下的"readme.txt"文件可以得知关于如何构建和部署PetClinic 应用程序,掌握本文提到的新技术的最佳方法也许就是对PetClinic 应用程序中所展示的特性进行试验。
Spring 支持 JSR-250 注解
Java EE5 中引入了“Java 平台的公共注解(Common Annotations for the Java Platform)”,而且该公共注解从 Java SE 6 一开始就被包含其中。 2006 年 5 月,BEA 系统宣布了他们在一个名为 Pitchfork 的项目上与 Interface21 的合作,该项目提供了基于 Spring 的 Java EE 5 编程模型的实现,包括支持用于注入(injection)、拦截( interception)和事务处理(transactions)的 JSR-250 注解和 EJB 3 注解 (JSR-220)。 在 2.5 版本中,Spring 框架的核心(core)现在支持以下 JSR-250 注解:
- @Resource
- @PostConstruct
- @PreDestroy
结合 Spring,这些注解在任何开发环境下都可以使用——无论是否有应用程序服务器——甚至是集成测试环境都可以。激活这样的支持仅仅是注册一个单独的 Spring post-processor 的事情:
<span color="#3f7f7f"><bean</span> <span color="#7f007f">class</span>=<span color="#2a00ff">"org.springframework.context.annotation.CommonAnnotationBeanPostProcessor"</span><span color="#3f7f7f">/></span> <br></br>
### @Resource 注解
@Resource 注解被用来激活一个命名资源(named resource)的依赖注入,在 JavaEE 应用程序中,该注解被典型地转换为绑定于 JNDI context 中的一个对象。 Spring 确实支持使用 **@Resource通过 JNDI lookup 来解析对象,默认地,拥有与@Resource** 注解所提供名字相匹配的“bean name(bean 名字)”的 Spring 管理对象会被注入。 在下面的例子中,Spring 会向加了注解的 setter 方法传递 bean 名为“dataSource”的 Spring 管理对象的引用。
<strong>@Resource(name="dataSource")</strong><br></br><span color="#7f0055"><strong>public void</strong></span> setDataSource(DataSource dataSource) {<br></br><span color="#7f0055"><strong>this</strong></span>.<span color="#0000c0">dataSource</span> = dataSource;<br></br>}
直接使用 **@Resource注解一个域(field)同样是可能的。通过不暴露 setter 方法,代码愈发紧凑并且还提供了域不可修改的额外益处。正如下面将要证明的,@Resource** 注解甚至不需要一个显式的字符串值,在没有提供任何值的情况下,域名将被当作默认值。
@Resource<br></br><span color="#7f0055"><strong>private</strong></span> DataSource dataSource; <span color="#3f7f5f">// inject the bean named 'dataSource'</span>
该方式被应用到 setter 方法的时候,默认名是从相应的属性衍生出来,换句话说,命名为 **‘setDataSource’的方法被用来处理名为’dataSource’** 的属性。
<span color="#7f0055"><strong>private</strong></span> DataSource <span color="#0000c0">dataSource</span>;<br></br>@Resource<br></br><span color="#7f0055"><strong>public void</strong></span> setDataSource(DataSource dataSource) {<br></br><span color="#7f0055"><strong>this</strong></span>.<span color="#0000c0">dataSource</span> = dataSource;<br></br>}
当 **@Resource没有显式提供名字的时候,如果根据默认名字找不到对应的 Spring 管理对象,注入机制会回滚至类型匹配(type-match)。如果刚好只有一个 Spring 管理对象符合该依赖的类型,那么它会被注入。通过设置CommonAnnotationBeanPostProcessor 的‘fallbackToDefaultTypeMatch’** 属性为“false”(默认值是“true”)可以禁用这一特性。
<span color="#3f7f7f"><bean</span> <span color="#7f007f">class</span>=<span color="#2a00ff">"org.springframework.context.annotation.CommonAnnotationBeanPostProcessor"</span><span color="#3f7f7f">></span><br></br><span color="#3f7f7f"><property</span> <span color="#7f007f">name</span>=<span color="#2a00ff">"fallbackToDefaultTypeMatch"</span> <span color="#7f007f">value</span>=<span color="#2a00ff">"false"</span><span color="#3f7f7f">/></span><br></br><span color="#3f7f7f"></bean></span>
正如上文所提到的,在解析标有 **@Resource注解的依赖时,Spring 支持 JNDI-lookup。如若要强制对所有使用@Resource注解的依赖进行 JNDI lookup,那也只要将CommonAnnotationBeanPostProcessor的’alwaysUseJndiLookup’** 标识设置为 true 就可以了(默认值是 false)。
<span color="#3f7f7f"><bean</span> <span color="#7f007f">class</span>=<span color="#2a00ff">"org.springframework.context.annotation.CommonAnnotationBeanPostProcessor"</span><span color="#3f7f7f">></span><br></br><span color="#3f7f7f"><property</span> <span color="#7f007f">name</span>=<span color="#2a00ff">"alwaysUseJndiLookup"</span> <span color="#7f007f">value</span>=<span color="#2a00ff">"true"</span><span color="#3f7f7f">/></span><br></br><span color="#3f7f7f"></bean></span>
另一个选择是,激活指定为‘resource-ref-mappings’的依据全局 JNDI 名的查找,在@Resource注解内提供‘mappedName’属性。即使目标对象实际上是一个 JNDI 资源,仍然推荐引入一个 Spring 管理对象,这样可以提供一个间接层并且因此降低耦合程度。自 Spring2.0 开始添加命名空间以来,定义一个委托 Spring 处理 JNDI lookup 的 bean 也变得愈发简练:
<span color="#3f7f7f"><jee:jndi-lookup</span> <span color="#7f007f">id</span>=<span color="#2a00ff">"dataSource"</span> <span color="#7f007f">jndi-name</span>=<span color="#2a00ff">"java:comp/env/jdbc/petclinic"</span><span color="#3f7f7f">/></span> <br></br>
这个方法的优点在于间接层带来了巨大的部署弹性。比如说,一个单独的系统测试环境应该不再需要 JNDI 注册。在这种情况下,在系统测试配置中可以提供如下的 bean 定义:
<span color="#3f7f7f"><bean</span> <span color="#7f007f">id</span>=<span color="#2a00ff">"dataSource"</span> <span color="#7f007f">class</span>=<span color="#2a00ff">"org.springframework.jdbc.datasource.DriverManagerDataSource"</span><br></br><span color="#7f007f">p:driverClassName</span>=<span color="#2a00ff">"${jdbc.driverClassName}"</span><br></br><span color="#7f007f">p:url</span>=<span color="#2a00ff">"${jdbc.url}"</span><br></br><span color="#7f007f">p:username</span>=<span color="#2a00ff">"${jdbc.username}"</span><br></br><span color="#7f007f">p:password</span>=<span color="#2a00ff">"${jdbc.password}"</span><span color="#3f7f7f">/></span>
顺便提一下,上面的例子中,实际的 JDBC 连接属性从一个属性文件(properties file)解析而来,在这个属性文件里,关键字与提供的 ${占位符}互相对应,这需要注册一个名为PropertyPlaceholderConfigurer的BeanFactoryPostProcessor实现来完成。这是具体化那些属性(通常是针对特定环境的属性)常用的技术,这些属性可能比其他配置修改得更为频繁。
<span color="#3f7f7f"><bean</span> <span color="#7f007f">class</span>=<span color="#2a00ff">"org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"</span><span color="#3f7f7f">></span><br></br><span color="#3f7f7f"><property</span> <span color="#7f007f">name</span>=<span color="#2a00ff">"location"</span> <span color="#7f007f">value</span>=<span color="#2a00ff">"classpath:jdbc.properties"</span><span color="#3f7f7f">/></span><br></br><span color="#3f7f7f"></bean></span>
Srping2.5 中新加入了‘context’命名空间,这个命名空间让我们能够得到更为简洁的方式来实现属性占位符(property placeholder)的配置:
<span color="#3f7f7f"><context:property-placeholder</span> <span color="#7f007f">location</span>=<span color="#2a00ff">"classpath:jdbc.properties"</span><span color="#3f7f7f">/></span><br></br>
### 生命周期注解:@PostConstruct 和@PreDestroy
@PostConstruct 和 **@PreDestroy注解分别用来触发 Spring 的初始化和销毁回调。这个特性在原有基础上得到了扩展,但并没有替代在 Spring2.5 之前版本中提供的同样的回调的另两个选项。第一个选项是实现 Spring 的InitializingBean** 和DisposableBean 接口中的一个或两个。这两个接口都需要一个回调方法的实现 (分别是afterPropertiesSet()和destroy() )。这种基于接口的方法利用了 Spring 自动识别任何实现这些接口的 Spring 管理对象的能力,因而不再需要另外的配置。另一方面,Spring 的一个关键目标是尽可能的非侵入。因此,许多 Spring 用户并不采用实现这些 Spring 特定接口的方法,而利用第二个选项,那就是提供他们自己的初始化和销毁方法。尽管入侵性小,但缺点在于使用这个方式的话就必须显式声明bean元素的init-method或destroy-method属性。显式配置有时候是必须的,例如当回调需要在开发人员控制能力之外的代码上被调用的时候。PetClinic 应用程序很好地说明了这个场景。当它和 JDBC 配置一起运行的时候,会用到一个第三方DataSource,并且它显式声明了一个destroy-method。另外要注意到的是,单独的连接池数据源是dataSource的另一个部署选项,并且不需要修改任何代码。
<span color="#3f7f7f"><bean</span> <span color="#7f007f">id</span>=<span color="#2a00ff">"dataSource"</span><br></br><span color="#7f007f">class</span>=<span color="#2a00ff">"org.apache.commons.dbcp.BasicDataSource"</span><p><strong>destroy-method="close"</strong><span color="#7f007f">p:driverClassName</span>=<span color="#2a00ff">"${jdbc.driverClassName}"</span></p><br></br><span color="#7f007f">p:url</span>=<span color="#2a00ff">"${jdbc.url}"</span><br></br><span color="#7f007f">p:username</span>=<span color="#2a00ff">"${jdbc.username}"</span><br></br><span color="#7f007f">p:password</span>=<span color="#2a00ff">"${jdbc.password}"</span><span color="#3f7f7f">/></span>
在使用 Spring2.5 的过程中,如果一个对象需要调用一个初始化的回调方法的话,这个回调方法可以采用@PostConstruct来注解。例如一个假想的例子,一个后台任务需要在启动的时候就开始对一个文件目录进行轮询:
<span color="#7f0055"><strong>public class</strong></span> FilePoller {<p><strong>@PostConstruct</strong><span color="#7f0055"><strong>public void</strong></span> startPolling() {</p><br></br> ...<br></br> }<br></br> ...<br></br>}
类似地,一个在 Spring 管理对象上用@PreDestroy注解的方法会在这个对象寄宿的应用程序上下文(application context)关闭的时候被调用。
<span color="#7f0055"><strong>public class</strong></span> FilePoller {<p><strong>@PreDestroy</strong><span color="#7f0055"><strong>public void</strong></span> stopPolling() {</p><br></br> ...<br></br> }<br></br> ...<br></br>}
在添加了对 JSR-250 注解的支持以后,现在的 Spring2.5 结合前面提到的两种生命周期方法的长处。将 **@PostConstruct和@PreDestroy** 作为方法层注解加入,足可以实现在受 Spring 管理的上下文(context)中触发回调。换句话说,不需要另外基于 XML 的配置。同时,这两个注解是 Java 语言本身的一部分(甚至被包括在 Java SE 版本 6 中),所以无需引入特定 Spring 包。这两个注解拥有在其他环境中也能理解的标识语义的优点,随着时间的推移,Java 开发人员可能会发现这些注解在第三方开发库中被越来越多的运用到。最后,基于注解生命周期回调的其中一个有趣的结果是,不止一个方法可以带有这两个注解中的任何一个,并且所有注解了的方法会被调用。
激活刚刚描述的关于@Resource 、@PostConstruct和 **@PreDestroy注解的所有行为,正如上文提到的,需要为 Spring 的CommonAnnotationBeanPostProcessor** 提供一个 bean 定义。但另一个更简练的方法则可能是使用 2.5 中的新的 context 命名空间:
<span color="#3f7f7f"><strong><context:annotation-config/></strong></span><br></br>
引入这个单个元素将不单单注册一个CommonAnnotationBeanPostProcessor,也会像下文将叙述的那样激活自动装配(autowire)行为。CommonAnnotationBeanPostProcessor也为@WebServiceRef 和@EJB注解提供支持。这些将在本文系列的第三篇中和 Spring2.5 为企业集成提供的其他新特性一起讨论。
利用注解来优化细粒度自动装配
涵盖 Spring 对自动装配支持的文档中常常会提到由于自动装配机制的粗粒度而伴随有很多限制性。Spring2.5 之前,自动装配可以通过很多不同的方式来配置:构造器,类型 setter,名字 setter,或者自动侦测(在该方式中 Spring 选择自动装配一个构造器或者类型 setter)。这些不同的选择确实提供了很大程度的灵活性,但它们中没有一个方法能够提供细粒度控制。换句话说,Spring2.5 之前还不可能自动装配某个对象 setter 方法的特定子集,或者通过类型或名字来自动装配它的一些属性。结果,许多 Spring 用户意识到将自动装配应用到构建原型和测试中的好处,但当提到在产品中维护和支持系统时,大部分人认为,加入冗长的显式配置对于澄清它所担负的职责是非常值得的。
然而,Spring2.5 大幅度地改变了布局。如上文所述,自动配置选项现在已经被扩展,支持 JSR-250 @Resource注解来激活在每个方法或域基础上被命名资源的自动装配。然而,@Resource注解若单独使用的话有很多限制。因此,Sring2.5 引进了一个名为@Autowired的注解进一步提高控制级别。为激活这里所讲的行为需要注册一个单独的 bean 定义:
<span color="#3f7f7f"><bean</span> <span color="#7f007f">class</span>=<span color="#2a00ff">"org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor"</span><span color="#3f7f7f">/></span><br></br>
另外如上文提到的,context 命名空间提供了一个更简明的方法。它将激活本文所讨论的两个 post-processor(AutowiredAnnotationBeanPostProcessor和CommonAnnotationBeanPostProcessor)和我们在 Spring2.0 中引入的基于注解的 post-processor:RequiredAnnotationBeanPostProcessor和PersistenceAnnotationBeanPostProcessor。
<span color="#3f7f7f"><context:annotation-config/></span><br></br>
利用@Autowired 注解可以对相应类型注入依赖。域、构造器和方法都可以激活此行为。实际上,aotowired 方法并不一定要是 setter 方法,且可以接受多个参数。下面这个例子是完整的可接受的用法:
<strong>@Autowired</strong><br></br><span color="#7f0055"><strong>public void</strong></span> setup(DataSource dataSource, AnotherObject o) { ... }
默认地,标有 **@Autowired注解的依赖被认为是必须的。然而,也可以将required属性值设置为false来声明它们中的任何一个。在下面这个例子中,DefaultStrategy只有在 context 命名空间中没有SomeStrategy** 类型的 Spring 管理对象时才能被使用。
<strong>@Autowired(required=false)</strong><br></br><span color="#7f0055"><strong>private</strong></span> SomeStrategy <span color="#2a00c0">strategy</span> = <span color="#7f0055">new</span> DefaultStrategy();
通过类型进行的自动装配明显地在 Spring context 包含多于一个期望类型的对象的时候造成歧义。默认地,如果一个必须的依赖没不是恰好一个 bean 与之对应的话,自动装配机制就会失败。同样的,对于任何一个可选属性,如果它拥有一个以上的候选,也都会失败(如果属性可选且没有任何候选可用的话,该属性则会被简单地跳过)。有很多不同的配置选项可以避免这些冲突。
若 Context 中拥有一个指定类型的一个主关键实例,对这个类型定义的 bean 定义应该包含‘primary’属性。当 Context 中含有其他可用实例的时候这个方法就很适用,但那些非主关键实例总是显式配置的。
<span color="#3f7f7f"><bean</span> <span color="#7f007f">id</span>=<span color="#2a00ff">"dataSource"</span> <strong>primary="true"</strong> ... <span color="#3f7f7f">/></span> <br></br>
在需要更多控制的时候,任何 autowired 的域、构造参数、或者方法参数可以进一步加注 **@Qualifier注解。qualifier 可以包含一个字符串 ** 值,在这种情况下,Spring 会试图通过名字来找到对应的对象。
@Autowired<p><strong>@Qualifier("primaryDataSource")</strong><span color="#7f0055"><strong>private</strong></span> DataSource dataSource; </p>
@Qualifier作为一个独立注解存在的主要原因是它可以被应用在构造器参数或方法参数上,但上文提到的@Autowired注解只能运用在构造器或方法本身。
@Autowired<br></br><span color="#7f0055"><strong>public void</strong></span> setup(<strong>@Qualifier("primaryDataSource")</strong> DataSource dataSource, AnotherObject o) { ... }
事实上,@Qualifier作为一个单独的注解在定制化方面提供了更多的好处。用户自定义的注解在自动装配过程中也可以起到 qualifier 的作用,最简单的实现方式是在运用自定义注解的同时将@Qualifier作为它的元注解。
@Target({ElementType.<em>FIELD</em>, ElementType.<em>PARAMETER</em>, ElementType.<em>TYPE</em>, ElementType.<em>ANNOTATION_TYPE</em>})<br></br>@Retention(RetentionPolicy.<em>RUNTIME</em>)<p><strong>@Qualifier</strong><span color="#7f0055"><strong>public @interface</strong></span> VetSpecialty { ... } </p>
自定义注解可以选择包含一个值来提供通过名字匹配的功能,但更普遍的用法是将它作为“标记”注解或定义一个对 qualifier 过程提供一些更多含义的值。例如,下面这个摘录则描绘了一个域,它应该和通过名字匹配得到的结果中合格的对象进行自动装配。
@Autowired<p><strong>@VetSpecialty("dentistry")</strong><span color="#7f0055"><strong>private</strong></span> Clinic dentistryClinic; </p>
在使用 XML 配置来达到依赖解析的目标时,‘qualifier’ 子元素可以被加注到 bean 定义中。在下文的组件扫描部分,我们将呈现一个可供选择的非 XML 方法。
<span color="#3f7f7f"><bean</span> <span color="#7f007f">id</span>=<span color="#2a00ff">"dentistryClinic"</span> <span color="#7f007f">class</span>=<span color="#2a00ff">"samples.DentistryClinic"</span><span color="#3f7f7f">></span><br></br><span color="#3f7f7f"><qualifier</span> <span color="#7f007f">type</span>=<span color="#2a00ff">"example.VetSpecialty"</span> <span color="#7f007f">value</span>=<span color="#2a00ff">"dentistry"</span><span color="#3f7f7f">/></span><br></br><span color="#3f7f7f"></bean></span>
为了避免对 **@Qualifier注解的任何依赖性,可以在 Spring context 中提供一个CustomAutowireConfigurer** 的 bean 定义并直接注册所有自定义注解类型:
<span color="#3f7f7f"><bean</span> <span color="#7f007f">class</span>=<span color="#2a00ff">"org.springframework.beans.factory.annotation.CustomAutowireConfigurer"</span><span color="#3f7f7f">></span><br></br><span color="#3f7f7f"><property</span> <span color="#7f007f">name</span>=<span color="#2a00ff">"customQualifierTypes"</span><span color="#3f7f7f">></span><p><span color="#3f7f7f"><set></span><span color="#3f7f7f"><value></span>example.VetSpecialty<span color="#3f7f7f"></value></span></p><p><span color="#3f7f7f"></set></span><span color="#3f7f7f"></property></span><span color="#3f7f7f"></bean></span> </p>
现在,自定义修饰符被显式声明了,就不再需要@Qualifier这个元注解符了。
@Target({ElementType.<em>FIELD</em>, ElementType.<em>PARAMETER</em>, ElementType.<em>TYPE</em>, ElementType.<em>ANNOTATION_TYPE</em>})<br></br>@Retention(RetentionPolicy.<em>RUNTIME</em>)<br></br><span color="#7f0055"><strong>public @interface</strong></span> VetSpecialty { ... }
其实,在配置AutowiredAnnotationBeanPostProcessor的时候,取代@Autowired注解都是有可能的。
<span color="#3f7f7f"><bean</span> <span color="#7f007f">class</span>=<span color="#2a00ff">"org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor"</span><span color="#3f7f7f">></span><br></br><span color="#3f7f7f"><property</span> <span color="#7f007f">name</span>=<span color="#2a00ff">"autowiredAnnotationType"</span> <span color="#7f007f">value</span>=<span color="#2a00ff">"example.Injected"</span><span color="#3f7f7f">/></span><br></br><span color="#3f7f7f"></bean></span>
大部分情况下,定义自定义‘标记’注解的能力结合通过名字或其他文法值进行匹配选项,足以完成自动装配过程的细粒度控制。但 Spring 还支持在 qualifier 注解上任意数目的任意属性。比如,下面是一个极为细粒度修饰的例子。
<strong>@SpecializedClinic(species="dog", breed="poodle")</strong><br></br><span color="#7f0055"><strong>private</strong></span> Clinic poodleClinic;
自定义修饰符的实现应该定义这些属性:
@Target({ElementType.<em>FIELD</em>, ElementType.<em>PARAMETER</em>, ElementType.<em>TYPE</em>, ElementType.<em>ANNOTATION_TYPE</em>})<br></br>@Retention(RetentionPolicy.<em>RUNTIME</em>)<br></br>@Qualifier<br></br><span color="#7f0055"><strong>public @interface</strong></span> SpecializedClinic {<p><strong>String species();</strong><strong>String breed();</strong>} </p>
自定义修饰符属性可以匹配那些 XML 中 bean 定义的qualifier注解的属性子元素。这些元素通常以键/值对方式提供。
<span color="#3f7f7f"><bean</span> <span color="#7f007f">id</span>=<span color="#2a00ff">"poodleClinic"</span> <span color="#7f007f">class</span>=<span color="#2a00ff">"example.PoodleClinic"</span><span color="#3f7f7f">></span><br></br><span color="#3f7f7f"><qualifier</span> <span color="#7f007f">type</span>=<span color="#2a00ff">"example.SpecializedClinic"</span><span color="#3f7f7f">></span><br></br><span color="#3f7f7f"><attribute</span> <span color="#7f007f">key</span>=<span color="#2a00ff">"species"</span> <span color="#7f007f">value</span>=<span color="#2a00ff">"dog"</span><span color="#3f7f7f">/></span><br></br><span color="#3f7f7f"><attribute</span> <span color="#7f007f">key</span>=<span color="#2a00ff">"breed"</span> <span color="#7f007f">value</span>=<span color="#2a00ff">"poodle"</span><span color="#3f7f7f">/></span><p><span color="#3f7f7f"></qualifier></span><span color="#3f7f7f"></bean></span> </p>
目前为止,关于 autowire 的描述都只是针对单独的实例,其实也支持集合。在任何需要得到所有 context 中某种特定类型的 Spring 管理对象的时候,只需要简单地在一个强类型(strongly-typed)集合上加注@Autowired 注解。
@Autowired<br></br><span color="#7f0055"><strong>private</strong></span> List<Clinic> <span color="#2a00c0">allClinics</span>;
本章节最后一个值得指出的特性是自动装配的使用替代了 Spring 的 Aware 接口。在 Spring2.5 之前,如果某个对象需要一个 Spring context 的ResourceLoader的引用,它可以通过实现ResourceLoaderAware的方式使得 Spring 通过setResourceLoader(ResourceLoader resourceLoader) 方法来提供该依赖。借助同样的方法可以得到 Spring 管理的MessageSource的引用,甚至可以得到ApplicationContext本身。对于 Spring2.5 用户而言,这个行为现在通过 autowiring 得到全面支持(需要指出的是包含这些 Spring 特定依赖的时候应该考虑周到,特别是它们只能用于从业务逻辑清楚地分割出来的基础构架代码中)。
@Autowired<br></br><span color="#7f0055"><strong>private</strong></span> MessageSource messageSource;<p>@Autowired</p><br></br><span color="#7f0055"><strong>private</strong></span> ResourceLoader resourceLoader;<p>@Autowired</p><br></br><span color="#7f0055"><strong>private</strong></span> ApplicationContext applicationContext;<br></br>
## 自动侦测 Spring 组件
从 2.0 版本开始,Spring 引入了构造型(stereotype)注解的概念以及将 **@Repository注解作为数据访问代码的标记的方法。在此基础上,Spring2.5 又加入了两个新的注解 —— @Service和@Controller 来完成为通常的三层架构(数据访问对象、服务、web 控制器)角色委任。Spring2.5 也引入了泛型@Component注解,其他构造型可从逻辑上对其进行扩展。通过清晰地指明应用程序的角色,这些构造型方便了 Spring AOP 和 post-processor 的使用,这些 post-processor 给基于这些角色的加了注解的对象提供了附加行为。比如,Spring2.0 引入了PersistenceExceptionTranslationPostProcessor对任何带有@Repository** 注解的对象自动激活其数据访问异常转换。
这些注解同样可以结合 Spring2.5 其他一些新性能来使用:自动侦测 classpath 上的组件。尽管 XML 已经成为最常见的 Spring 元数据的格式,但它决不是唯一选择。实际上,Spring 容器内的元数据是由纯 Java 来表示的,当 XML 被用来定义 Spring 管理对象时,在实例化过程之前,那些定义会被解析并转化成 Java 对象。Spring2.5 的一个巨大的新功能是支持从源码层注解读取元数据。因而,上文描述的自动装配机制使用注解的元数据来注入依赖,但它仍然需要注册至少一个 bean 定义以便提供每个 Spring 管理对象的实现类。组件扫描功能则使得这个 XML 中最起码的 bean 定义都不再存在需求性。
正如上面所示,Spring 注解驱动的自动装配可以在不牺牲细粒度控制的前提下极大程度地减少 XML 的使用。组件侦测机制将这个优点更发扬光大。全面替代 XML 中的配置不再必要,组件扫描反而可以处理 XML 元数据来简化整体配置。结合 XML 和注解驱动技术可以得到一个平衡优化的方法,这在 2.5 版本的 PetClinic 范例中有详细阐述。在该范例中,基础构架组件(数据源、事务管理等)结合上文提到的外化属性在 XML 中定义。数据访问层对象也有部分在 XML 中定义,它们的配置也都利用了@Autowired注解来简化依赖注入。最后,web 层控制器完全不在 XML 中显式定义,相反,下面提供的这段配置被用来触发所有 web 控制器的自动侦测:
<strong><span color="#3f7f7f"><context:component-scan</span> <span color="#7f007f">base-package</span>=<span color="#2a00ff">"org.springframework.samples.petclinic.web"</span><span color="#3f7f7f">/></span></strong><br></br>
需要注意到的是这段示例中使用到了 base-package 属性。组件扫描的默认匹配规则会递归侦测该包(多个包可以以逗号分隔的 list 方式提供)内的所有类的所有 Spring 构造型注解。正因为如此,PetClinic 应用程序范例中的各类控制器的实现都采用了 **@Controller注解(Spring 的内置 ** 构造型之一)。请看下面这个例子:
<strong>@Controller</strong><br></br><span color="#7f0055"><strong>public class</strong></span> ClinicController {<p><span color="#7f0055"><strong>private final</strong></span> Clinic <span color="#2a00c0">clinic</span>;</p><p> @Autowired</p><br></br><span color="#7f0055"><strong>public</strong></span> ClinicController(Clinic clinic) {<br></br><span color="#7f0055"><strong>this</strong></span>.<span color="#2a00c0">clinic</span> = clinic;<br></br> }<br></br> ...
自动侦测组件在 Spring 容器中注册,就像它们在 XML 中被定义一样。如上所示,那些对象可以轮流利用注解驱动的自动装配。
组件扫描的匹配规则可以通过过滤器(filter)来自定义,以根据类型、AspectJ 表达式、或针对命名模式的正则表达式来决定包含或不包含哪些组件。默认的构造型也可以被禁用。比如这里有一个配置的例子,这个配置会忽略默认的构造型,但会自动侦测名字以 Stub 打头或者包含@Mock注解的所有类:
<span color="#3f7f7f"><context:component-scan</span> <span color="#7f007f">base-package</span>=<span color="#2a00ff">"example"</span> <span color="#7f007f">use-default-filters</span>=<span color="#2a00ff">"false"</span><span color="#3f7f7f">></span><br></br><span color="#3f7f7f"><context:include-filter</span> <span color="#7f007f">type</span>=<span color="#2a00ff">"aspectj"</span> <span color="#7f007f">expression</span>=<span color="#2a00ff">"example..Stub*"</span><span color="#3f7f7f">/></span><br></br><span color="#3f7f7f"><context:include-filter</span> <span color="#7f007f">type</span>=<span color="#2a00ff">"annotation"</span> <span color="#7f007f">expression</span>=<span color="#2a00ff">"example.Mock"</span><span color="#3f7f7f">/></span><br></br><span color="#3f7f7f"></context:component-scan></span>
类型匹配的限制性也可以用排他的过滤器控制。例如,除了 @Repository注解外其他都依赖于默认过滤器,那么就需要加入一个排他过滤器(exclude-filter)。
<span color="#3f7f7f"><context:component-scan</span> <span color="#7f007f">base-package</span>=<span color="#2a00ff">"example"</span><span color="#3f7f7f">></span><br></br><span color="#3f7f7f"><context:exclude-filter</span> <span color="#7f007f">type</span>=<span color="#2a00ff">"annotation"</span> <span color="#7f007f">expression</span>=<span color="#2a00ff">"org.springframework.stereotype.Repository"</span><span color="#3f7f7f">/></span><br></br><span color="#3f7f7f"></context:component-scan></span>
很明显,有很多方法可以扩展组件扫描来注册自定义的类型。构造型注解是最简单的选择,所以构造型概念本身也是可扩展的。像先前提到的,@Component是泛型模型,@Repository、@Service, 和 **@Controller注解都从该构造型逻辑扩展而得。正因为如此,@Component可被用来作为元注解(也就是说,在另外的注解上声明的注解),所有具有@Component** 元注解的自定义注解都会被默认扫描匹配规则自动侦测到。一个例子就有希望让你领会到其实它根本没有听起来那么难。
让我们回想一下在讲 **@PostConstruct和@PreDestroy生命周期注解的时候的假想的后台任务。也许一个应用程序有很多很多这样的后台任务,这些任务实例需要 XML bean 定义以便在 Spring context 里注册并使它们自己的生命周期方法在正确时候被调用。利用组件扫描就不再需要这些显式的 XML bean 定义。如果这些后台任务都实现一个相同的接口或者都沿用同样的命名惯例,那么可以用include-filters。然而,更简单的方法是为这些任务对象创建一个注解并提供@Component** 元注解。
@Target({ElementType.<em>TYPE</em>})<br></br>@Retention(RetentionPolicy.<em>RUNTIME</em>)<br></br>@Documented<p><strong>@Component</strong><span color="#7f0055"><strong>public @interface</strong></span> BackgroundTask {</p><br></br> String value() <span color="#7f0055"><strong>default</strong></span> <span color="#0000c0">""</span>;<br></br>}
然后在所有后台任务的类定义中提供自定义构造型注解。
<strong>@BackgroundTask</strong><br></br><span color="#7f0055"><strong>public class</strong></span> FilePoller {<p> @PostConstruct</p><br></br><span color="#7f0055"><strong>public void</strong></span> startPolling() {<br></br> ...<br></br> }<p> @PreDestroy</p><br></br><span color="#7f0055"><strong>public void</strong></span> stopPolling() {<br></br> ...<br></br> }<br></br> ...<br></br>}
泛型@Component注解可以像例子中提供的那样简单使用,自定义注解技术则提供了一个使用更具涵义的、领域特定的名字的机会。这些领域特定注解提供更深入的机会,比如使用 AspectJ 切点表达式来识别所有后台任务,以便增加 advice 来监控这些任务的活动性。
默认的,组件被侦测到的时候,Spring 会自动生成一个没有修饰符的类名作为 bean 名字。上一个例子中,生成的 bean 名字会是 filePoller。但是,任何加注了 Spring 构造型注解(@Component、@Repository、@Service或 @Controller)或是加注了其他的以 @Component 作为元注解的注解(比如上面例子中的@BackgroundTask )的类,构造型注解的value属性可以被显式指定,实例将该值作为它的 bean 名字注册到 context 中。接下来的例子里,实例名应该是 petClinic 而不是默认生成的名字 simpleJdbcClinic。
<strong>@Service("petClinic")</strong><br></br><span color="#7f0055"><strong>public class</strong></span> SimpleJdbcClinic {<br></br> ...<br></br>}
同样的,在下面修正版的 FilePoller 例子里,生成的 bean 名字应该是 poller 而不是 filePoller。
<strong>@BackgroundTask("poller")</strong><br></br><span color="#7f0055"><strong>public class</strong></span> FilePoller {<br></br> ...<br></br>}
虽然所有 Spring 管理对象都被默认地当作单例实例来处理,但有些时候还是有必要为某个对象指明一个备用的范围(scope)。举个例子来说,在 web 层,一个 Spring 管理对象可能捆绑到 request 或 session 的范围。对于 2.0 版本,Spring 的 scope 机制更具延展性,这样一来,自定义 scope 可以被注册到应用程序上下文(application context)。在 XML 配置中,仅仅是简单地包含进scope属性及该 scope 的名字就可以了。
<span color="#3f7f7f"><bean</span> <span color="#7f007f">id</span>=<span color="#2a00ff">"shoppingCart"</span> <span color="#7f007f">class</span>=<span color="#2a00ff">"example.ShoppingCart"</span> <strong>scope=<span color="#2a00ff">"session"</span></strong><span color="#3f7f7f">></span><br></br> ...<br></br><span color="#3f7f7f"></bean></span>
Spring2.5 中,为被扫描的组件提供@Scope注解可以起到同样的作用。
@Component<p><strong>@Scope("session")</strong><span color="#7f0055"><strong>public class</strong></span> ShoppingCart {</p><br></br> ...<br></br>}
这里要指出的最后一点是使用组件扫描时 qualifier 注解应用是多么的简单。在上一节,下面这个对象曾被作为使用自定义 qualifier 注解进行自动装配的例子:
<strong>@VetSpecialty("dentistry")</strong><br></br><span color="#7f0055"><strong>private</strong></span> Clinic <span color="#2a00c0">dentistryClinic</span>;
同样的例子接着展现了在 XML 内使用‘qualifier’元素为依赖提供指定目标 bean 定义。在使用组件扫描时,XML 元数据不是必须的。但自定义修饰符也许在目标类定义中被作为类型层注解而引入。另一个将被扫描的@Repository实例作为依赖的例子如下:
@Repository<p><strong>@VetSpecialty("dentistry")</strong><span color="#7f0055"><strong>public class</strong></span> DentistryClinic <span color="#7f0055"><strong>implements</strong></span> Clinic {</p><br></br> ...<br></br>}
最终,因为前面的例子展现了自定义注解及其属性的例子,相等同的非 XML 表示依赖目标的方法如下:
@Repository<p><strong>@SpecializedClinic(species="dog", breed="poodle")</strong><span color="#7f0055"><strong>public class</strong></span> PoodleClinic <span color="#7f0055"><strong>implements</strong></span> Clinic {</p><br></br> ...<br></br>}
## 小结
Spring2.5 在很多方面都提供了很有意义的新功能。本文主要关注于怎样通过掌控 Java 注解的力量将配置简化。就如在 JSR-250 中定义的那样,Spring 支持公共注解(Common Annotations),同时为自动装配过程的更细粒度的控制提供了额外注解。Spring2.5 也扩展了从 Spring2.0 的@Repository就开始的构造型(stereotype)注解,并且所有这些构造型注解都可以和新的组件扫描功能结合使用。Spring2.5 仍然全面支持基于 XML 的配置,同时它又引进了一个新的 context 命名空间对常见配置场景提供更精要的文法。实际上,支持 XML 和基于注解配置的无缝结合最终产生一个更为平衡的全面的方法。基本构架的复杂配置可以在模块 XML 文件中定义,而应用程序栈日益增多地更高层配置可以更多的从基于注解的技术中获益——前提是都在同一个 Spring2.5 应用程序 context 内。
在接下来的文章中,我们将讨论到在 Spring web 层强大的基于注解的新功能项。敬请关注该系列的下一篇文章。
查看英文原文: What’s New in Spring 2.5: Part 1
评论