为了简化XML配置,Spring提供了自动装配(autowiring)。
四种类型自动装配
byName自动装配
byName自动装配遵循约定:为属性自动装配ID与该属性的名字相同的Bean。例如,将先前的kenny例子:
1 | <bean id="kenny" class="com.spring.entity.Instrumentalist"> |
改为:
1 | <bean id="kenny" class="com.spring.entity.Instrumentalist" autowire="byName"> |
通过autowire
属性,Spring就可以利用此信息自动装配kenny的剩下的instrument属性了。
配置一个id为”instrument”的Bean:
1 | <bean id="instrument" class="com.spring.entity.Saxophone"/> |
这样,saxophone就自动装配给kenny的instrument属性了。当找不到时,则该属性不进行装配。
如果多个Instrumentalist Bean都被配置为byName自动装配,那他们将会演奏同一个乐器。
byType自动装配
byType自动装配通过寻找哪一个Bean的类型与属性的类型相匹配。如果找到多个与需要装配的属性类型相匹配的Bean,Spring会直接抛出异常。所以,应用只允许存在一个类型相匹配的Bean。但在实际中,XML中可能存在多个类型一样的Bean,为了解决这种情况,Spring提供了两种解决方法:
① 为自动装配标识一个首选Bean
使用<bean>
元素的primary属性。如果只有一个自动装配的候选Bean的primary属性设置为true,那么其将被优先选择。比如设置saxophone为首选Bean:
1 | <bean id="saxophone" class="com.spring.entity.Saxophone" primary="true"/> |
也可以使用@Primary
注解。 ② 取消某个Bean自动装配的候选资格
使用方法为设置Bean的autowire-candidate
属性为false即可:
1 | <bean id="saxophone" class="com.spring.entity.Saxophone" autowire-candidate="false"/> |
constructor自动装配
如果通过构造器注入配置Bean,那么可以移除<constructor-arg>
元素。如将先前的duke配置:
1 | <bean id="duke" class="com.spring.entity.Juggler" autowire="constructor"/> |
通过autowire="constructor"
声明,Spring会去获取Juggler某个构造器的所有参数类型,然后再XML中寻找与其类型匹配的Bean。so,constructor自动装配与byType自动装配具有相同的局限性。
autodetect自动装配
detect 英[dɪˈtekt] 美[dɪˈtɛkt] vt. 查明,发现; 洞察; 侦察,侦查; [电子学] 检波
1 | <bean id="duke" class="com.spring.entity.Juggler" autowire="autodetect"/> |
通过该声明,Spring首次尝试使用constructor自动装配,失败的话再次尝试使用byType自动装配。
默认自动装配
通过配置<beans>
元素上增加一个default-autowire
属性:
1 |
|
这样的话,Spring配置文件里的所有Bean都将使用byType自动装配,除非Bean自己配置了autowire属性。
混合装配
一个Bean可以同时使用自动装配和显示装配,如:
1 | <bean id="kenny" |
混合使用可以避免当自动装配失败的时候,使用显示装配覆盖自动装配。
使用constructor自动装配时,不能混合使用constructor自动装配策略和
使用注解装配
1 | <context:annotation-config/> |
annotation 英[ˌænə’teɪʃn] 美[ˌænə’teɪʃn] n. 注释;
<context:annotation-config/>
可以消除Spring中的<property>
和<constructor-arg>
元素。注意,我们还是得使用<bean>
元素显示定义Bean。
@Autowired
例如,在Instrumentalist的setInstrument()
方法进行标注:
1 | //注入乐器 |
这样,我们可以移除用来定义Instrumentalist的instrument属性所对应的<property>
元素了。通过@Autowired
注解,Spring会通过byType自动装配。
假如@Autowired
注解标注于属性,我们可以删除setter方法:
1 |
|
@Autowired
也可以标注于构造器,这样XML文件中可以省去<constructor-arg>
元素配置Bean:
1 |
|
当多个构造器都通过@Autowired注解的时候,Spring就会从所有满足装配条件的构造器中选择出参数最多的那个。
可选的自动装配:
通过@Autowired
标注的属性或者参数必须是可装配的。假如匹配失败,则抛出NoSuchBeanDefinitionException
异常。假如属性不一定要装配,null
值也可以接收的话,我们可以设置required=fasle
来让自动装配变为可选:
1 | false) (required= |
当使用构造器装配时,只有一个构造器可以将@Autowired
的required属性设置为true,其余的只能设置为false。
限定歧义性依赖:
如果通过@Autowired
注解匹配到好几个Bean,为了鉴别哪一个是我们需要的,我们可以在@Autowired
注解下添加@Qualifier
注解:
qualifier 英[ˈkwɒlɪfaɪə(r)] 美[ˈkwɑ:lɪfaɪə(r)] n. 合格者,已取得资格的人; [语] 修饰语; 预选赛,资格赛
1 |
|
如上所示,@Qualifier
注解将尝试注入ID为”guitar”的Bean。
除了通过Bean的ID来匹配,我们还可以修改guitar Bean配置:
1 | <bean class="com.spring.entity.Guitar"> |
<qualifier>
元素限定了guitar Bean是一个弦乐器(stringed)。这时候可以将上面的注解修改为:
1 |
|
创建自定义的限定器:
首先,通过如下代码创建自定义的限定器:
1 | import java.lang.annotation.ElementType; |
这样,我们在guitar类上添加此限定器:
1 |
|
在自动装配的instrument属性进行限定:
1 |
|
这样guitar就会被装配的instrument属性里!
假如匹配到多个Bean,需要进一步的缩小范围,继续定义自定义限定器即可!
@Inject
Maven依赖:
1 | <!-- https://mvnrepository.com/artifact/javax.inject/javax.inject --> |
为了规范各种依赖注入框架的编程模型,JCP(Java Community Process)发布了Java依赖注入规范,简称为JCR-330。Spring 3.0开始兼容该依赖注入模型。@Inject
注解几乎可以完全替代@Autowired
。如:
1 |
|
@Inject
也可以标注于属性,方法和构造器,不过@Inject
没有required属性,所以@Inject
注解所标注的依赖关系必须存在,否则抛出异常。
限定@Inject所标注的属性
类似于前面的@Qualifier
,@Inject
所对应的是@Named
注解:
1 |
|
创建自定义的JSR-330 Qualifier
通过下面的代码创建一个新的@NewStringedInstrument
注解:
1 | import java.lang.annotation.ElementType; |
和前面的自定义限定器唯一的区别就是@Qualifier注解的导入声明。
在注解中使用SpEL表达式
Spring3.0 引入了@Value
,其可以注解装配String类型和基本类型的值。比如,我们通过@Value
注解传入一个String类型的值:
1 | "May Rain") ( |
这样”May Rain”就被注入到song属性中了。
我们还可以结合SpEL表达式:
1 | "#{systemProperties.myFavoriteSong}") ( |
@Resource
Maven依赖:
1 | <!-- https://mvnrepository.com/artifact/javax.annotation/jsr250-api --> |
@Resource
注解是JSR-250发布的注解。与@Autowired
和@Inject
不同的是,@Resource
注解不能用于构造器!@Resource
注解默认使用byName自动装配!
@Resource
有两个中重要的属性:name和type ,而Spring将@Resource
注解的name属性解析为bean的 名字,而type属性则解析为bean的类型。所以如果使用name属性,则使用byName的自动注入策略,而使用type属性时则使用 byType自动注入策略。如果既不指定name也不指定type属性,这时将通过反射机制使用byName自动注入策略。
byName:
1 | "piano") (name= |
byType:
1 | (type=Instrument.class) |