@Autowired和@Resource

Spring的@Autowired

注解版的自动绑定(@Autowired)

在之前的学习中,为了减少配置量,我们可以采用Spring的IoC容器提供的自动绑定功能:

1
2
3
4
5
<beans default-autowire="byType">  
<bean id="newsProvider" class="..FXNewsProvider" autowire="byType"/>
<bean id="djNewsListener" class="..DowJonesNewsListener"/>
<bean id="djNewsPersister" class="..DowJonesNewsPersister"/>
</beans>

可以通过default-autowired或者autowired来指定每个bean定义的自动绑定方式,Spring2.5之后提供了一个更加方便的方式:@Autowired注解,它可以让容器知道需要为当前类注入哪些依赖。例如:

1
2
3
4
5
6
7
8
9
10
11
public class FXNewsProvider  {  
private IFXNewsListener newsListener;
private IFXNewsPersister newPersistener;

@Autowired
public FXNewsProvider(IFXNewsListener newsListner,IFXNewsPersister newsPersister) {
this.newsListener = newsListner;
this.newPersistener = newsPersister;
}
...
}

与原有的byType类型的自动绑定方式类似,@Autowired也是按照类型匹配进行依赖注入的,当按照类型找不到时,则会使用标注的属性名称,按byName的方式查找。但是@Autowired也更加灵活更加强大,它可以标注于类定义的多个位置。包括如下几个:

  • 域或者说属性(Property)不管它们声明的访问限制符是private还是public等等,只要标注@Autowired了,它们所需要的依赖注入需求就都能够被满足。
  • 构造方法定义标注于类的构造方法之上的@Autowired,相当于抢夺了原有自动绑定功能中“constructor”方式的权力,它将更具构造方法参数类型,来决定将什么样的依赖对象注入给当前对象,就如上面的代码演示一样。
  • 方法定义@Autowired不光可以标注于传统的setter方法之上,而且还可以标注于任意名称的方法定义之上,只要该方法定义了需要被注入的参数。

接下来只需要在容器的配置文件中定义bean就行了:

1
2
3
4
5
<beans>   
<bean id="newsProvider" class="..FXNewsProvider"/>
<bean id="djNewsListener" class="..DowJonesNewsListener"/>
<bean id="djNewsPersister" class="..DowJonesNewsPersister"/>
< /beans>

为了给容器中定义的每个bean对应的实例注入依赖,可以遍历它们,然后通过反射,检查每个bean定义对应的类上各种可能位置上的@Autowired。如果存在的话,就可以从当前容器管理的对象中获取符合条件的对象,设置给@Autowired所标注的对象。

那么如果仅仅只是一个注解,在Spring容器中并不能起作用,联想到之前的BeanPostProcessor自定义实现,让这个BeanPostProcessor在实例化bean定义的过程中,来检查当前对象是否有@Autowired标注的依赖需要注入,AutowiredAnnotationBeanPostProcessor就是Spring提供用于这一目的的BeanPostProcesser实现,所以只需要在配置文件中注册一下它就可以了:

1
2
3
4
5
6
7
<beans>  
<bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor"/>

<bean id="newsProvider" class="..FXNewsProvider"/>
<bean id="djNewsListener" class="..DowJonesNewsListener"/>
<bean id="djNewsPersister" class="..DowJonesNewsPersister"/>
< /beans>

@Qualifier的陪伴

@Autowired是按照类型进行匹配,如果当前@Autowired标注的依赖在容器中只能找到一个实例与之对应的话还好,可以是若能够同时找到多个同一类型的实例对象又该怎么办?这时候就可以使用@Qualifier对依赖注入的条件做进一步限定。

@Qualifier实际上是byName自动绑定的注解版,既然IoC容器无法自己从多个同一类型的实例中选取我们真正想要的那个,那么我们就可以使用@Qualifier直接点名需要哪个。假设FXNewsProvider使用的IFNewsListener有两个实现类DowJonesNewsListener和ReutersNewsListener:

1
2
3
4
5
6
7
8
<beans>  
<bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor"/>

<bean id="newsProvider" class="..FXNewsProvider"/>
<bean id="djNewsListener" class="..DowJonesNewsListener"/>
<bean id="reutersNewsListner" class="..ReutersNewsListener"/>
<bean id="djNewsPersister" class="..DowJonesNewsPersister"/>
</beans>

如果我们想让FXNewsProvider使用ReutersNewsListener,那么就可以使用@Qulifier指定选择:

1
2
3
4
5
6
7
8
9
10
public class FXNewsProvider  {  
@Autowired
@Qualifier("reutersNewsListner")
private IFXNewsListener newsListener;

@Autowired
private IFXNewsPersister newPersistener;

...
}

JavaEE的@Resource

除了使用Spring提供的@Autowired和@Qulifier之外,还可以使用JavaEE提供的@Resource。@Resource与@Autowired不同,它遵循的是byName自动绑定的行为准则,也就是说,IoC容器将更具@Resource所指定的名称,到容器中查找beanName与之对应的实例,然后将查找到的对象实例注入给@Resource所标注的对象。如下:

1
2
3
4
5
6
7
8
public class FXNewsProvider  {   
@Resource(name="djNewsListener")
private IFXNewsListener newsListener;

@Resource(name="djNewsPersister")
private IFXNewsPersister newPersistener;
...
}

除了标注于属性之上外,还可以与@Autowired一样标注于方法或者构造方法之上,此处与@Autowired大致一样。

@PostConstruct和@PreDestroy

确切的说,@PostConstruct和@PreDestroy不是服务于依赖注入的,他们主要用于标注对象生命周期管理相关方法,这与Spring的InitializingBean和DisposableBean接口,以及配置项中的init-method和destroy-method起到类似作用。

如果想某个方法在对象实例化之后被调用,以做某些准备工作,或者想在对象销毁之前调用某个方法做清理工作,可以像如下这样:

1
2
3
4
5
6
7
8
9
10
11
public class LifecycleEnabledClass { 
@PostConstruct
public void setUp() {
...
}

@PreDestroy
public void destroy() {
...
}
}

annotation-config

就像@AutoWired需要AutowiredAnnotationBeanPostProcessor为它与IoC容器牵线搭桥一样,JavaEE同样有这样的配置方法,可以帮助这些注解发挥它们的作用,那就是annotation-config,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<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"
xmlns:p="http://www.springframework.org/schema/p"
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="newsProvider" class="..FXNewsProvider"/>
<!--其他bean定义-->
...
</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
2
3
4
5
6
7
8
9
10
<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="org.spring21"/>
< /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
2
3
4
5
6
7
8
9
10
11
12
13
<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="org.spring21">
<context:include-filter type="annotation" expression="cn.spring21.annotation.FXService"/>
<context:exclude-filter type="aspectj" expression=".."/>
</context:component-scan>
< /beans>

include-filtering和exclude-filtering可以使用的type类型由annotation、assignable、regex和aspectj四种。