Spring 2.5 的基于注解的依赖注入
@Autowired之外的选择——使用JSR250标注依赖注入关系
Spring2.5提供的基于注解的依赖注入,除了可以使用 Spring 提供的 @Autowired
和 @Qualifier
来标注相应类定义之外,还可以使用 JSR250的 @Resource
和 @PostConstruct
以及 @PreDestroy
对相应类进行标注,这同样可以达到依赖注入的目的。
@Resource
与@Autowired
不同,它遵循的是byName自动绑定形式的行为准则,也就是说,IoC容器将根据@Resource
所指定的名称,到容器中查找beanName与之对应的实例,然后将查找到的对象实例注入给@Resource
所标注的对象。
同样的FXNewsProvider
,如若使用@Resource
进行标注以获取依赖注入的话,类似如下的样子:
JSR250规定,如果@Resource
标注于属性域或者方法之上的话,相应的容器将负责把指定的资源注入给当前对象,所以,除了像我们这样直接在属性域上标注@Resource
,还可以在构造方法或者普通方法定义上标注@Resource
,这与@Autowired
能够存在的地方大致相同。
确切地说,@PostConstruct
和@PreDestroy
不是服务于依赖注入的,它们主要用于 标注对象生命周期管理相关方法,这与Spring的InitializingBean
和DisposableBean
接口,以及配置项中的init-method
和destroy-method
起到类似的作用。
下方代码给出了可能使用这两个注解的示例代码。
如果想某个方法在对象实例化之后被调用,以做某些准备工作,或者想在对象销毁之前调用某个方法清理某些资源,那么就可以像我们这样,使用@PostConstruct
和@PreDestroy
来标注这些方法。
我们只是使用@Resource
或者@PostConstruct
和@PreDestroy
标注了相应对象,并不能给该对象带来想要的东西。所以,就像@Autowired
需要AutowiredAnnotationBeanPostProcessor
为它与IoC容器牵线搭桥一样,JSR250的这些注解也同样需要一个BeanPostProcessor
帮助它们实现自身的价值。这个BeanPostProcessor
就是org.springframework.context.annotation.CommonAnnotationBeanPostProcessor
,只有将CommonAnnotationBeanPostProcessor
添加到容器,JSR250的相关注解才能发挥作用,通常如下添加相关配置即可:
既然不管是@Autowired
还是@Resource
都需要添加相应的BeanPostProcessor
到容器,那么我们就可以在基于XSD的配置文件中使用一个<context:annotation-config>
配置搞定以上所有的BeanPostProcessor
配置,如下方代码所示。
<context:annotation-config>
不但帮我们把AutowiredAnnotationBeanPostProcessor
和CommonAnnotationBeanPostProcessor
注册到容器,同时还会把PersistenceAnnotationBeanPostProcessor
和RequiredAnnotationBeanPostProcessor
一并进行注册,可谓一举四得啊!
classpath-scanning 功能介绍
好了,该来解决让我们不爽的那个问题了。到目前为止,我们还是需要将相应对象的bean定义,一个个地添加到IoC容器的配置文件中。与之前唯一的区别就是,不用在配置文件中明确指定依赖关系了(改用注解来表达了嘛)。既然使用注解来表达对象之间的依赖注入关系,那为什么不搞的彻底一点儿,将那些几乎“光秃秃”的bean定义从配置文件中彻底消灭呢?
OK,我们想到了,Spring开发团队也想到了,classpath-scanning
的功能正是因此而诞生的!
使用相应的注解对组成应用程序的相关类进行标注之后,classpath-scanning
功能可以从某一顶层包(basepackage)开始扫描。当扫描到某个类标注了相应的注解之后,就会提取该类的相关信息,构建对应的BeanDefinition
,然后把构建完的BeanDefinition
注册到容器。这之后所发生的事情就不用我说了,既然相关的类已经添加到了容器,那么后面BeanPostProcessor
为@Autowired
或者@Resource
所提供的注入肯定是有东西拿咯!
classpath-scanning
功能的触发是由 <context:component-scan>
决定的。按照如下代码,在 XSD 形式(也只能是 XSD 形式)的配置文件中添加该项配置之后,classpath-scanning
功能立即开启。
现在<context:component-scan>
将遍历扫描org.spring21
路径下的所有类型定义,寻找标注了相应注解的类,并添加到IoC容器。如果要扫描的类定义存在于不同的源码包下面,也可以为base-package
指定多个以逗号分隔的扫描路径。
<context:component-scan>
默认扫描的注解类型是@Component
。不过,在@Component
语义基础上细化后的@Repository
、@Service
和@Controller
也同样可以获得<context:component-scan>
的青睐。
同样对于服务层的类定义来说,使用@Service
标注它,要比使用@Component
更为确切。对于其他两种注解也是同样道理。
我们暂且使用语义更广的@Component
来标注FXNews相关类,以便摆脱每次都要向IoC容器配置添加bean定义的苦恼。
<context:component-scan>
在扫描相关类定义并将它们添加到容器的时候,会使用一种默认的命名规则,来生成那些添加到容器的bean定义的名称(beanName)。
比如DowJonesNewsPersister
通过默认命名规则将获得dowJonesNewsPersister
作为bean定义名称。如果想改变这一默认行为,就可以像以上DowJonesNewsListener
所对应的@Component
那样,指定一个自定义的名称。
现在,除了<context:component-scan>
是唯一需要添加到IoC容器的配置内容,所有的工作都可以围绕着使用注解的Java源代码来完成了。如果现在加载配置文件,启动FXNewProvider
来处理外汇新闻的话,我们可以得到预期的运行效果,运行的代码如下所示:
你或许会觉得有些诧异,因为我们并没有使用<context:annotation-config>
甚至直接将相应的BeanPostProcessor添加到容器中,而FXNewsProvider怎么会获得相应的依赖注入呢?这个得怪<context:component-scan>
“多管闲事”,它同时将AutowiredAnnotationBeanPostProcessor
和CommonAnnotationBeanPostProcessor
一并注册到了容器中,所以,依赖注入的需求得以满足。
如果你不喜欢,非要自己通过<context:annotation-config>
或者直接添加相关BeanPostProcessor
的方式来满足@Autowired
或者@Resource
的需求,可以将<context:component-scan>
的annotation-config
属性值从默认的true改为false。不过,我想没有太好的理由非要这么做吧?
<context:component-scan>
的扫描行为可以进一步定制,默认情况下它只关心@Component
、@Repository
、@Service
和@Controller
四位大员,但我们可以丰富这一范围,或者对默认的扫描结果进行过滤以排除某些类,<context:component-scan>
的嵌套配置项可以帮我们达到这一目的。下方代码演示了<context:component-scan>
部分嵌套配置项的使用。
include-filter
和exclude-filter
可以使用的type类型有annotation
、assignable
、regex
和aspectj
四种。它们的更多信息可以参考最新的Spring2.5参考文档。上例中,我们增加了@FXService
作为新的被扫描注解对象,并使用aspectj表达式排除某些扫描结果。
Spring3.0 展望
(这里不做介绍,有兴趣可以看原文)
本章小结
Spring最初并不支持基于注解的依赖注入方式。所以,在Spring2.5中引入这一依赖注入方式的时候,肯定要在维护整个框架设计与实现的一致性和引入这种依赖注入方式对整个框架的冲击之间做出权衡。最终的结果我们已经看到了,Spring2.5中引入的基于注解的依赖注入从整体上保持了框架内的一致性,同时又提供了足够的基于注解的依赖注入表达能力。我想,最初的决定和最终的效果都是令人满意的。虽然我们还会部分地依赖于容器的配置文件,但通过20%的工作却可以带来80%的效果,这本身已经是最好的结果了。
不过,从实际开发角度看,如果非要使用完全基于注解的依赖注入的话,或许会遇到一些过不去的坎儿。比如,对于第三方提供的类库,肯定没法给其中的相关类标注@Component
之类的注解。这时,我们可以结合使用基于配置文件的依赖注入方式。毕竟,基于XML的依赖注入方式是Spring提供的最基本、也最为强大的表达方式了!
到目前为止,我们已经结束了SpringIoC容器的旅程。接下来的第三部分将带领读者探索SpringAOP框架的精彩世界。