本节书摘来自异步社区《Spring攻略(第2版)》一书中的第1章,第1.12节,作者: 【美】Gary Mak , Josh Long , Daniel Rubio著,更多章节内容可以访问云栖社区“异步社区”公众号查看
1.12 用@Autowired和@Resource自动装配Bean
1.12.1 问题
在Bean配置文件中设置autowire属性进行的自动装配将装配一个Bean的所有属性。这样的灵活性不足以仅仅装配特定的属性。而且,你只能通过类型或者名称自动装配Bean。如果这两种策略都不能满足你的需求,就必须明确地装配Bean。
1.12.2 解决方案
从Spring 2.5起,自动装配功能进行了多处改进。你可以通过用@Autowired或者@Resource(在JSR-250:Java平台常见注解中定义)注解一个设值方法、构造程序、字段甚至任意方法自动装配特定的属性。这意味着你除了设置autowire属性之外,还有一个能够满足需求的选择。但是,这种基于注解的选项要求你使用Java 1.5或者更高版本。
1.12.3 工作原理
为了要求Spring自动装配具有@Autowired或者@Resource注解的属性,你必须在IoC容器中注册一个AutowiredAnnotationBeanPostProcessor实例。如果你使用一个Bean工厂,就必须通过API注册这个Bean后处理器,否则,你只能在你的应用上下文里声明一个实例。
<bean class="org.springframework.beans.factory.annotation.
AutowiredAnnotationBeanPostProcessor" />
你也可以简单地在Bean配置文件中包含元素,这将自动注册一个AutowiredAnnotationBeanPostProcessor实例。
<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-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<context:annotation-config />
...
</beans>
自动装配一个兼容类型的Bean
@Autowired可以应用到一个特定的需要Spring自动装配的属性。例如,你可以用@Autowired注解prefixGenerator属性的设值方法。然后,Spring将试图装配一个类型与prefixGenerator兼容的Bean。
package com.apress.springrecipes.sequence;
import org.springframework.beans.factory.annotation.Autowired;
public class SequenceGenerator {
...
@Autowired
public void setPrefixGenerator(PrefixGenerator prefixGenerator) {
this.prefixGenerator = prefixGenerator;
}
}
如果你在IoC容器中定义了一个与PrefixGenerator类型兼容的Bean,它将自动地设置到PrefixGenerator属性上。
<beans ...>
...
<bean id="sequenceGenerator"
class="com.apress.springrecipes.sequence.SequenceGenerator">
<property name="initial" value="100000" />
<property name="suffix" value="A" />
</bean>
<bean id="datePrefixGenerator"
class="com.apress.springrecipes.sequence.DatePrefixGenerator">
<property name="pattern" value="yyyyMMdd" />
</bean>
</beans>
默认情况下,所有带有@Autowired的属性都是必需的。当Spring不能找到匹配的Bean进行装配时,将会抛出一个异常。如果你希望某个属性是可选的,将@Autowired的required属性设置为false。之后,当Spring找不到匹配的Bean,将不设置该属性。
package com.apress.springrecipes.sequence;
import org.springframework.beans.factory.annotation.Autowired;
public class SequenceGenerator {
...
@Autowired(required = false)
public void setPrefixGenerator(PrefixGenerator prefixGenerator) {
this.prefixGenerator = prefixGenerator;
}
}
除了设值方法之外,@Autowired注解还可以应用到构造程序,Spring将为每个构造程序参数寻找一个具有兼容类型的Bean。
package com.apress.springrecipes.sequence;
import org.springframework.beans.factory.annotation.Autowired;
public class SequenceGenerator {
...
@Autowired
public SequenceGenerator(PrefixGenerator prefixGenerator) {
this.prefixGenerator = prefixGenerator;
}
}
@Autowired注解还可以应用到一个字段,即使这个字段没有声明为Public。这样,你不能省略这个字段的设值方法或者构造程序的声明。Spring将通过反射把匹配的Bean注入这个字段。但是,用@Autowired注解非公开的字段将降低代码的可测试性,因为代码将很难进行单元测试(黑盒测试法无法使用模拟对象之类的方式操纵这一状态)。
package com.apress.springrecipes.sequence;
import org.springframework.beans.factory.annotation.Autowired;
public class SequenceGenerator {
@Autowired
private PrefixGenerator prefixGenerator;
...
}
你甚至可以将@Autowired注解应用到具有任意名称和任意数量参数的方法上,在这种情况下,Spring将试图为每个方法参数装配一个类型兼容的Bean。
package com.apress.springrecipes.sequence;
import org.springframework.beans.factory.annotation.Autowired;
public class SequenceGenerator {
...
@Autowired
public void inject(PrefixGenerator prefixGenerator) {
this.prefixGenerator = prefixGenerator;
}
}
自动装配所有兼容类型的Bean
@Autowired注解还可以应用到一个数组类型的属性上,让Spring自动装配所有匹配的Bean。例如,你可以用@Autowired注解一个PrefixGenerator[]属性。然后,Spring将一次性自动装配所有类型与PrefixGenerator兼容的Bean。
package com.apress.springrecipes.sequence;
import org.springframework.beans.factory.annotation.Autowired;
public class SequenceGenerator {
@Autowired
private PrefixGenerator[] prefixGenerators;
...
}
如果你在IoC容器中有多个类型与PrefixGenerator兼容类型的Bean,它们将自动被添加到PrefixGenerators数组中。
<beans ...>
...
<bean id="datePrefixGenerator"
class="com.apress.springrecipes.sequence.DatePrefixGenerator">
<property name="pattern" value="yyyyMMdd" />
</bean>
<bean id="yearPrefixGenerator"
class="com.apress.springrecipes.sequence.DatePrefixGenerator">
<property name="pattern" value="yyyy" />
</bean>
</beans>
相似地,你可以将@Autowired注解应用类型安全的集合。Spring能够读取这个集合的类型信息,自动装配所有类型兼容的Bean。
package com.apress.springrecipes.sequence;
import org.springframework.beans.factory.annotation.Autowired;
public class SequenceGenerator {
@Autowired
private List<PrefixGenerator> prefixGenerators;
...
}
如果Spring注意到@Autowired注解应用到了一个关键字为字符串的类型安全java.util.Map,它将在这个Map中添加所有Bean名称与关键字相同的兼容类型Bean。
package com.apress.springrecipes.sequence;
import org.springframework.beans.factory.annotation.Autowired;
public class SequenceGenerator {
@Autowired
private Map<String, PrefixGenerator> prefixGenerators;
...
}
使用限定符的按类型自动装配
默认情况下,按照类型的自动装配在IoC容器中有超过一个类型兼容的Bean时无效。但是,Spring允许你指定一个候选Bean,这个Bean的名称在@Qualifier注解中提供。
package com.apress.springrecipes.sequence;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
public class SequenceGenerator {
@Autowired
@Qualifier("datePrefixGenerator")
private PrefixGenerator prefixGenerator;
...
}
完成了这项工作,Spring将会试图在IoC容器中查找一个具有这个名称的Bean,将其装配到这个属性中。
<bean id="datePrefixGenerator"
class="com.apress.springrecipes.sequence.DatePrefixGenerator">
<property name="pattern" value="yyyyMMdd" />
</bean>
@Qualifier注解也可以应用到方法参数中进行自动装配。
package com.apress.springrecipes.sequence;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
public class SequenceGenerator {
...
@Autowired
public void inject(
@Qualifier("datePrefixGenerator") PrefixGenerator prefixGenerator) {
this.prefixGenerator = prefixGenerator;
}
}
你可以为自动装配创建一个自定义的限定符注解类型。这种注解类型必须用@Qualifier注解。如果你希望一种特殊的Bean和配置在注解装饰字段或者设值方法时注入,那么就可以使用这种注解类型。
package com.apress.springrecipes.sequence;
import java.lang.annotation.Target;
import java.lang.annotation.Retention;
import java.lang.annotation.ElementType;
import java.lang.annotation.RetentionPolicy;import
org.springframework.beans.factory.annotation.Qualifier;
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.PARAMETER })
@Qualifier
public @interface Generator {
String value();
}
之后,你可以将这个注解应用到@Autowired bean属性。这将要求Spring自动装配带有这个限定符注解和特定值的Bean。
package com.apress.springrecipes.sequence;
import org.springframework.beans.factory.annotation.Autowired;
public class SequenceGenerator {
@Autowired
@Generator("prefix")
private PrefixGenerator prefixGenerator;
...
}
你必须向希望自动装配到前述的属性中的目标Bean提供这个限定符。限定符由带有type属性的元素添加。限定符值在value属性中指定。Value属性映射到注解的String value()属性。
<bean id="datePrefixGenerator"
class="com.apress.springrecipes.sequence.DatePrefixGenerator">
<qualifier type="Generator" value="prefix" />
<property name="pattern" value="yyyyMMdd" />
</bean>
按照名称自动装配
如果你希望按照名称自动装配Bean属性,可以用JSR-250 @Resource注解为一个设值方法、构造程序或者字段加上注解。默认情况下,Spring将试图找到一个与属性同名的Bean。但是你可以显式地在name属性中指定Bean名称。
注:
为了使用JSR-250注解,你必须包含JSR 250依赖。如果你使用Maven,添加以下内容:
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>jsr250-api</artifactId>
<version>1.0</version>
</dependency>
package com.apress.springrecipes.sequence;
import javax.annotation.Resource;
public class SequenceGenerator {
@Resource(name = "datePrefixGenerator")
private PrefixGenerator prefixGenerator;
...
}