Spring提供了几种技巧,可以减少XML的配置数量:
- 自动装配(autowiring):可以减少
<property>
和<constructor-arg>
元素,让Spring自动识别如何装配Bean的依赖关系; - 自动检测(autodiscovery):Spring能够自动识别哪些类需要被装配成Spring Bean,从而减少对
<bean>
的使用。
自动装配Bean属性
4种自动装配
- byName:把与Bean属性具有相同名字(或id)的其他Bean自动装配到Bean的对应属性中;
- byType:把与Bean属性具有相同类型的其他Bean自动装配到Bean的对应属性中;
- constructor:把与Bean的构造函数的入参具有相同类型的其他Bean自动装配到Bean的构造函数对应的入参中;
- autodetect:先尝试使用constructor,失败后再使用byType。
byName
为属性自动装配id与该属性的名字相同的Bean。使用方法:
<bean id="kenny" class="com.springinaction.springidol.Instrumentalist" autowire="byName">
<property name="song" value="Happy" />
</bean>
通过配置Bean Kenny的autowire="byName"
属性,Spring就可以利用此信息自动装配Kenny的instrument属性了。
缺点:需要假设Bean的名字(如instrument)与其他Bean的属性的名字一样,若其他多个Bean的属性都是instrument,那么让他们将使用同一个instrument。
byType
当Spring根据类型匹配到多个Bean时,会抛出异常,形如:
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'kenny' defined in class path resource [spring-idol.xml]: Unsatisfied dependency expressed through bean property 'instrument': : No qualifying bean of type [com.springinaction.springidol.Instrument] is defined: expected single matching bean but found 2: saxphone,guitar;
为了避免这种异常(expected single matching bean but found 2)的出现,Spring提供了两种方案:可以自动装配标识一个首选Bean,或者可以取消某个Bean的自动装配的候选资格。
标识首选Bean:primary=”true”
可以使用primary属性将Bean设置为首选Bean,那么它将会得到优选被选择权:
<bean id="saxphone" class="com.springinaction.springidol.Saxophone" />
<bean id="guitar" class="com.springinaction.springidol.Guitar" primary="true"/>
<bean id="kenny" class="com.springinaction.springidol.Instrumentalist"
autowire="byType">
<property name="song" value="Happy" />
</bean>
有两个Bean类型满足kenny的instrument属性,但是guitar设置了primary="true"
,因此会注入guitar。
排除其他Bean: autowire-candidate=”false”
<bean id="saxphone" class="com.springinaction.springidol.Saxophone" autowire-candidate="false"/>
<bean id="guitar" class="com.springinaction.springidol.Guitar"/>
<bean id="kenny" class="com.springinaction.springidol.Instrumentalist"
autowire="byType">
<property name="song" value="Happy" />
</bean>
通过排除其他Bean的候选资格来达到和上面设置primary同样的效果。
constructor
当有多个Bean匹配某个构造函数的入参时,Spring同样会抛出异常。
默认自动装配方式
可以在根元素<beans>
上添加default-autowire
属性来设置该配置文件中的的自动装配方式。
混用自动装配和显示装配
显示装配会覆盖掉自动装配:
<bean id="saxphone" class="com.springinaction.springidol.Saxophone" autowire-candidate="false"/>
<bean id="guitar" class="com.springinaction.springidol.Guitar"/>
<bean id="kenny" class="com.springinaction.springidol.Instrumentalist"
autowire="byType">
<property name="song" value="Happy" />
<property name="instrument" ref="saxphone"></property>
</bean>
虽然取消了saxphone的候选资格,但最终仍是saxphone注入到了kenny的属性中。
注意
当使用constructor自动装配时,就不能混合使用constructor自动装配和标签了。
使用注解装配
启用注解装配:<context:annotation-config />
。
- Spring自带的@Autowired注解
- JSR-330的@Inject注解
- JSR-250的@Resource注解
@Autowired
使用方法:
// 1、可以标注setter
@Autowired
public void setInstrument(Instrument instrument) {
this.instrument = instrument;
}
// 2、标注其他方法
@Autowired
public void heresYourInstrument(Instrument instrument) {
this.instrument = instrument;
}
// 3、标注构造器
@Autowired
public Instrumentalist(Instrument instrument) {
this.instrument = instrument;
}
// 4、直接标注属性
@Autowired
private Instrument instrument;
使用@Autowired进行自动装配时,在遇到多个匹配的Bean或者没有匹配的Bean也会出现问题。
可选的自动装配
通过设置@Autowired的required属性为false来配置可选。
@Autowired(required=false)
private Instrument instrument;
这时,若没有找到匹配到的instrument Bean,应用也不会出现异常,instrument会被设置为null。
注意:
当使用构造器装配时,只有一个构造器可以将@Autowired的required属性设置为true,其他使用@Autowired注解的required属性必须设置为false。此外,当使用@Autowired标注多个构造器时,Spring会从所有满足装配条件的构造器中选择入参最多的那个。
限制歧义性的依赖
当有多个Bean满足装配条件时,可以配合使用@Qualifier
注解。
@Autowired
@Qualifier("guitar")
private Instrument instrument;
@Qualifier
注解缩小了自动装配候选Bean的范围。
@Inject
和@Autowired一样,@Inject可以装配 属性、方法和构造器;但是@Inject没有required属性,因此@Inject注解所依赖的bean是必须存在的,如果不存在就会抛出异常。
使用@Inject注入一个Provider,从而可以实现Bean引用的延迟注入以及注入多个Bean实例的功能。
private Set<Knife> knives;
@Inject
public KnifeJuggler(Provider<Knife> knifeProvider) {
knives = new HashSet<Knife>();
for (int i = 0; i < 5; i++) {
knives.add(knifeProvider.get());
}
}
KnifeJuggler类需要注入多个Knife实例,假设Knife Bean的作用域是prototype的,那么KnifeJuggler将获得一个Provider,这时只有provider被注入;在调用provider的get()方法之前,实际的Knife对象没有被注入。
限定@Inject所注入的属性:@Named
@Inject
@Named("guitar")
private Instrument instrument;
在注解中使用表达式:@Value
可以使用@Value装配简单值:String类型和基本类型,如:
@Value("Happy")
private String song;
@Value可以配合SpEL使用:
@Value("#{systemProperties.myFavoriteSong}")
private String song;
自动检测Bean:
可以减少和 的使用,但仍需配置。
使用 除了可以完成上述工作,还可以自动检测Bean和定义Bean,它会扫描指定的包及其所有子包,并查找出能够自动注册为Spring Bean的类,base-package标识了所要扫描的包:
<context:component-scan base-package="com.springinaction.springidol"/>
标注Bean
- @Component:通用的构造型注解,标识该类为Spring组件;
- @Controller:标识该类为Spring MVC controller;
- @Repository:标识为数据仓库;
- @Service:标识为服务;
- @Component:标注为自定义注解。
package com.springinaction.springidol;
import org.springframework.stereotype.Component;
@Component
public class Guitar implements Instrument {
public void play() {
System.out.println("Strum strum strum");
}
}
Spring扫描com.springinaction.springidol包时,会发现使用@Component注解所标注的Guitar,会自动将它注册为Spring Bean,其id会是guitar。
定义组件扫描策略
过滤器类型 | 描述 |
---|---|
annotation | 扫描使用指定注解所标注的类,通过expression属性指定要扫描的注解 |
assignable | 扫描派生于expression属性所指定类型的那些类 |
aspectj | 扫描与expression属性所指定的AspectJ表达式多匹配的那些类 |
custom | 使用自定义的org.springframework.core.type.TypeFilter实现类,该类由expression属性指定 |
regex | 扫描类名称与expression属性所指定的正则表达式所匹配的类 |
以下配置实现了:自动注册所有实现了Instrument接口的类,并且排除使用自定义@SkipIt注解的类。
<context:component-scan base-package="com.springinaction.springidol">
<context:include-filter type="assignable"
expression="com.springinaction.springidol.Instrument" />
<context:exclude-filter type="annotation"
expression="com.springinaction.springidol.SkipIt" />
</context:component-scan>