Spring的@Autowired
注解版的自动绑定(@Autowired)
在之前的学习中,为了减少配置量,我们可以采用Spring的IoC容器提供的自动绑定功能:
1 | <beans default-autowire="byType"> |
可以通过default-autowired或者autowired来指定每个bean定义的自动绑定方式,Spring2.5之后提供了一个更加方便的方式:@Autowired注解,它可以让容器知道需要为当前类注入哪些依赖。例如:
1 | public class FXNewsProvider { |
与原有的byType类型的自动绑定方式类似,@Autowired也是按照类型匹配进行依赖注入的,当按照类型找不到时,则会使用标注的属性名称,按byName的方式查找。但是@Autowired也更加灵活更加强大,它可以标注于类定义的多个位置。包括如下几个:
- 域或者说属性(Property):不管它们声明的访问限制符是private还是public等等,只要标注@Autowired了,它们所需要的依赖注入需求就都能够被满足。
- 构造方法定义:标注于类的构造方法之上的@Autowired,相当于抢夺了原有自动绑定功能中“constructor”方式的权力,它将更具构造方法参数类型,来决定将什么样的依赖对象注入给当前对象,就如上面的代码演示一样。
- 方法定义:@Autowired不光可以标注于传统的setter方法之上,而且还可以标注于任意名称的方法定义之上,只要该方法定义了需要被注入的参数。
接下来只需要在容器的配置文件中定义bean就行了:
1 | <beans> |
为了给容器中定义的每个bean对应的实例注入依赖,可以遍历它们,然后通过反射,检查每个bean定义对应的类上各种可能位置上的@Autowired。如果存在的话,就可以从当前容器管理的对象中获取符合条件的对象,设置给@Autowired所标注的对象。
那么如果仅仅只是一个注解,在Spring容器中并不能起作用,联想到之前的BeanPostProcessor自定义实现,让这个BeanPostProcessor在实例化bean定义的过程中,来检查当前对象是否有@Autowired标注的依赖需要注入,AutowiredAnnotationBeanPostProcessor就是Spring提供用于这一目的的BeanPostProcesser实现,所以只需要在配置文件中注册一下它就可以了:
1 | <beans> |
@Qualifier的陪伴
@Autowired是按照类型进行匹配,如果当前@Autowired标注的依赖在容器中只能找到一个实例与之对应的话还好,可以是若能够同时找到多个同一类型的实例对象又该怎么办?这时候就可以使用@Qualifier对依赖注入的条件做进一步限定。
@Qualifier实际上是byName自动绑定的注解版,既然IoC容器无法自己从多个同一类型的实例中选取我们真正想要的那个,那么我们就可以使用@Qualifier直接点名需要哪个。假设FXNewsProvider使用的IFNewsListener有两个实现类DowJonesNewsListener和ReutersNewsListener:
1 | <beans> |
如果我们想让FXNewsProvider使用ReutersNewsListener,那么就可以使用@Qulifier指定选择:
1 | public class FXNewsProvider { |
JavaEE的@Resource
除了使用Spring提供的@Autowired和@Qulifier之外,还可以使用JavaEE提供的@Resource。@Resource与@Autowired不同,它遵循的是byName自动绑定的行为准则,也就是说,IoC容器将更具@Resource所指定的名称,到容器中查找beanName与之对应的实例,然后将查找到的对象实例注入给@Resource所标注的对象。如下:
1 | public class FXNewsProvider { |
除了标注于属性之上外,还可以与@Autowired一样标注于方法或者构造方法之上,此处与@Autowired大致一样。
@PostConstruct和@PreDestroy
确切的说,@PostConstruct和@PreDestroy不是服务于依赖注入的,他们主要用于标注对象生命周期管理相关方法,这与Spring的InitializingBean和DisposableBean接口,以及配置项中的init-method和destroy-method起到类似作用。
如果想某个方法在对象实例化之后被调用,以做某些准备工作,或者想在对象销毁之前调用某个方法做清理工作,可以像如下这样:
1 | public class LifecycleEnabledClass { |
annotation-config
就像@AutoWired需要AutowiredAnnotationBeanPostProcessor为它与IoC容器牵线搭桥一样,JavaEE同样有这样的配置方法,可以帮助这些注解发挥它们的作用,那就是annotation-config,如下:
1 | <beans xmlns="http://www.springframework.org/schema/beans" |
<context:annotation-config/>不但帮我们把AutowiredAnnotationBeanPostProcessor和 CommonAnnotationBeanPostProcessor注册到容器,还把PersistenceAnnotationBeanPostProcessor和RequiredAnnotationBeanPostProcessor一并注册,可谓一举四得。
classpath-scanning介绍
到现在为止,我们还是需要将相应对象的bean定义,一个个的添加到IoC容器的配置文件中,唯一与之前的区别就是,不用在配置文件中明确指定依赖关系了,那么既然注解做都做了,为了不做添加bean的注解呢》Spring的classpath-scanning就是因此而生的。
使用相应的注解对组成应用程序的相关类进行标注后,classpath-scanning功能可以从某一顶层包开始扫描,当扫描到某个类标注了相应的注解之后,就会提取该类的相关信息,构建对应的BeanDefinition,然后把构建完成的BeanDefinition注册到容器,这之后的事情就不用多说了。
classpath-scanning功能的触发是由<context:component-scan>决定的,只需要配置如下代码即可生效:
1 | <beans xmlns="http://www.springframework.org/schema/beans" |
现在<context:component-scan>将扫描org.spring21路径下的所有类型定义,寻找标注了相应注解的类,并添加到IoC容器。
<context:component-scan>默认扫描的注解类型是@Component,不过在@Component语义基础上细化后的@Repository、@Service和@Controller也同样可以获得<context:component-scan>的青睐。
<context:component-scan>在扫描相关类定义并将它们添加到容器的时候,会使用一种默认的命名规则,来生成那些添加到容器的bean的名称(beanName)。比如DowJonesNewsPersister通过默认命名规则将获得dowJonesNewsPersister。如果想改变这种命名规则,就可以在@Component中制定一个自定义的名称。
当在配置文件中添加<context:component-scan>之后,<context:component-scan>将多管闲事,同时会将AutowiredAnnotationBeanPostProcessor和CommonAnnotationBeanPostProcessor一并注入到容器中。
<context:component-scan>还会进一步定制扫描范围,默认情况下他只关系@Component、@Repository、@Service和@Controller四位大员。但是我们可以通过include-filtering和exclude-filtering定制扫描范围,例子如下:
1 | <beans xmlns="http://www.springframework.org/schema/beans" |
include-filtering和exclude-filtering可以使用的type类型由annotation、assignable、regex和aspectj四种。