Spring提供了几种技巧,可以帮助我们减少XML的配置数量。
- 自动装配(autowiring)有助于减少甚至消除配置<property>元素和<constructor-arg>元素,让Spring自动识别如何装配Bean的依赖关系。
- 自动检测(autodiscovery)比自动装配更进了一步,让Spring能够自动识别哪些类需要被装配成Spring Bean,从而减少对<bean>元素的使用。
当自动装配和自动检测一起使用时,它们可以显著减少Spring的XML配置数量。通常只需要配置少量的几行XML代码,而无需知道在Spring的应用上下文中究竟有多少Bean。
自动装配Bean属性
自动装配(autowiring)
4种类型的自动装配
当涉及自动装配Bean的依赖关系时,Spring有多种处理方式。因此,Spring提供了4种各具特色的自动装配策略。
- byName——把与Bean的属性具有相同名字(或者ID)的其他Bean自动装配到Bean的对应属性中。如果没有跟属性的名字相匹配的Bean,则该属性不进行装配。
- byType——把与Bean的属性具有相同类型的其他Bean自动装配到Bean的对应属性中。如果没有跟属性的类型相匹配的Bean,则该属性不被装配。
- constructor——把与Bean的构造器入参具有相同类型的其他Bean自动装配到Bean构造器的对应入参中。
- autodetect——首先尝试使用constructor进行自动装配。如果失败,再尝试使用byType进行自动装配。
byName自动装配
举个栗子:
<bean id="kenny2"
class="com.springinaction.springidol.Instrumentalist">
<property name="song" value="Jingle Bells" />
<property name="instrument" ref="saxophone" />
</bean>
在这里,我们使用<property>元素显式装配了Kenny的instrument属性。假设使用<bean>元素在定义萨克斯(saxophone)时,把Bean的id属性设置为instrument:
<bean id="instrument"
class="com.springinaction.springIdol.Saxphone" />
在本示例中,萨克斯(saxophone)Bean的id属性与Keeny Bean的instrument属性的名字是一样的。通过配置autowire属性,Spring就可以利用此信息自动装配kenny的instrument属性
<bena id="kenny"
class="com.springinaction.springidol.Instrumentlist"
autowire="byName">
<property name="song" value="Jingle Bells"/>
</bena>
byName自动装配遵循一项约定:为属性自动装配ID与该属性的名字相同的Bena。通过设置autowire属性为byName,Spring将特殊对待kenny的所有属性,为这些属性寻找与其名字相同的Spring Bean。在这里,Spring会发现instrument属性可以通过setter注入来进行自动装配。
使用byName自动装配的缺点是需要假设Bean的名字与其他Bean的属性的名字一样。
byType自动装配
byType自动装配的工作方式类似于byName自动装配,只不过不再是匹配属性的名字而是检查属性的类型。当我们尝试使用byType自动装配时,Spring会寻找哪一个Bean的类型与属性的类型相匹配。
constructor自动装配
如果要通过构造器注入来配置Bean,那我们可以移除<constructor-arg>元素,由Spring在应用上下文中自动选择Bean注入到构造器入参中。
例如:
<bean id="duke"
class="com.springinaction.springidol.PoeticJuggler"
autowire="constructor"
/>
上述声明告诉Spring去审视PoeticJuggler的构造器,并尝试在Spring配置中寻找匹配PoeticJuggler某一个构造器所有入参的Bean。
最佳自动装配
如果想自动装配Bean,但是又不能决定该使用那一种类型的自动装配。现在不必担心了,我们可以设置autuwire属性为autodetect,由Spring来决定。例如
<bean id="duke"
class="com.springinaction.springidol.PoeticJuggle"
autowire="autodetect"/>
当配置一个Bean的autowire属性为autodetect时,Spring将首先尝试使用constructor自动装配,如果没有发现与构造器相匹配的Bean时,Spring将尝试使用byType自动装配。
默认自动装配
default-autowire
混合使用自动装配和显式装配
使用注解装配
从Spring2.5开始,最有趣的一种装配Spring Bean的方式是使用注解自动装配Bean的属性。使用注解自动装配与在XML中使用autowire属性自动装配并没有太大差别。但是使用注解方式允许更细粒度的自动装配,我们可以选择性地标注某一属性来对应其应用自动装配。
Spring容器默认禁止注解装配。所以,在使用基于注解的自动装配前,我们需要在Spring配置中启用它。最简单的启用方式是使用Spring的context命名空间配置中的<context:annotation-config />元素。
<context:annotation-config />元素告诉Spring我们打算使用基于注解的自动装配。一旦配置完成,我们就可以对代码添加注解,标识Spring应该为属性、方法和构造器进行自动装配。
Spring3支持几种不同的用于自动装配的注解:
- Spring自带的@Autowired注解
- JSR-330的@Inject注解
- JSR-250的@Resource注解
使用@Autowired
假设我们希望使用@Autowired让Spring自动装配乐器演奏家(Instrumentalist)Bean的instrument属性。则可以对setInstrument()方法进行标注,如下
@Autowired
public void setInstrument(Instrument instrument){
this.instrument = instrument;
}
当Spring发现我们对setInstrument()方法使用了@Autowired注解时,Spring就会尝试对该方法执行byType自动装配。@Autowired也可以用于装配Bean的引用和标注构造器。
可选的自动装配
默认情况下,@Autowired具有强契约特征,其所标注的属性或参数必须是可装配的。如果没有Bean可以装配到@Autowired所标注的属性或参数中,自动装配就会失败(抛出令人讨厌的NoSuchBeanDefinitionException)。这可能是我们所期望的处理方式——当自动装配无法完成时,让Spring尽早失败,远胜于以后抛出异常。
属性不一定非要装配,null值也是可以接受的。在这种场景下,可以通过设置@Autowired的required属性为false来配置自动装配是可选的。
@Autowired(required=false)
private Instrument instrument;
在这里,Spring将尝试装配instrument属性,但是如果没有查找到与之匹配的类型为Instrument的Bean,应用就不会发生任何问题,而instrument属性的值会设置为null。
限定歧义性的依赖
为了帮助@Autowired鉴别出哪一个Bean才是我们所需要的,我们可以配合使用Spring的@Qualifier注解。
例如,为了确保Spirng为eddie Bean选择吉他(guitar)来演奏,即使有其他Bean也可以装配到instrument属性中,但我们可以使用@Qualifier来明确指定名为guitar的Bean:
@Autowired
@Qualifier("guitar")
private Instrument instrument;
如上所示,@Qualifier注解将尝试注入ID为guitar的Bean。
借助@Inject实现基于标准的自动装配
和@Autowired一样,@Inject可以用来自动装配属性、方法和构造器;与@Autowired不同的是,@Injet没有required属性。因此,@Inject注解所标注的依赖关系必须存在,如果不存在,则会抛出异常。
例如,我们有一个KnifeJuggler类需要注入一个或多个Knife的实例。假设Knife Bean的作用域声明为prototype,下面的KnifeJuggler的构造器将获得5个Knife 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将获得一个Provider<Knife>,而不是在构造器中获得一个Knife实例。这个时候,只有provider被注入进去;在调用provider的get()方法之前,实际的Knife对象并没有被注入。在这个示例中,get()方法被调用了5次。因为Knife Bean的作用域为prototype,所以knife的Set集合将被赋予5个不同的Knife对象。
限定@Inject所标注的属性
@Named注解的工作方式非常类似于Spring的@Qualifier,正如我们在这里所看到的:
@Inject
@Named("guitar")
private Instrument instrument;
在注解注入中使用表达式
@Value注解尽管易于使用,但我们很快就会发现,它同样具有威力。我们可以通过@Value直接标注某个属性、方法或者方法参数,并传入一个String类型的表达式来装配属性。例如:
@Value("Eruption")
private String song;
在这里,我们为String类型的属性装配了一个String类型的值。但是传入@Value的String类型的参数只是一个表达式——它的结算结果可以是任意类型,因此@Value能够标注任意类型的属性。
自动检测Bean
<context:component-scan>元素除了完成了与<context:annotation-config>一样的工作,还允许Spring自动检测Bean和定义Bean。
<context:component-scan>元素会扫描指定的包及其所有子包,并查找能够自动注册为Spring Bean的类。base-package属性标识了<context:component-scan>元素所扫描的包。
为自动检测标注Bean
默认情况下,<context:component-scan >查找使用构造器(stereotype)注解所有标注的类,这些特殊的注解如下:
- @Component ——通用的构造型注解,标识该类为Spring组件。
- @Controller——标识将该类定义为SpringMVC controller。
- @Repository——标识将该类定义为数据仓库
- @Service——标识将该类定义为服务。
- 使用@Component标注的任意自定义注解。
过滤组件扫描
通过为<context:component-scan>配置<context:include-filter>和/或者<context:exclude-filter>子元素,我们可以随意调整扫描行为。
过滤器类型 | 描述 |
---|---|
annotation | 过滤器扫描使用指定注解所标注的那些类,通过expression属性指定要扫描的注解 |
assignable | 过滤器扫描派生于expression属性所指定类型的那些类 |
aspectj | 过滤器扫描与expression属性所指定的AspectJ表达式所匹配的那些类 |
custom | 使用自定义的org.springframework.core.type.TypeFilter实现类,该类由expression |
regex | 过滤器扫描类的名称与expression属性所指定的正则表达式匹配的那些类 |
网友评论