《Spring实战》学习笔记-第三章:最小化SpringXML配置

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。使用方法:

1
2
3
<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,那么它将会得到优选被选择权:

1
2
3
4
5
6
7
<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”

1
2
3
4
5
6
<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属性来设置该配置文件中的自动装配方式。

混用自动装配和显示装配

显示装配会覆盖掉自动装配

1
2
3
4
5
6
7
<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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 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来配置可选。

1
2
@Autowired(required=false)
private Instrument instrument;

这时,若没有找到匹配到的instrument Bean,应用也不会出现异常,instrument会被设置为null。

注意
当使用构造器装配时,只有一个构造器可以将@Autowired的required属性设置为true,其他使用@Autowired注解的required属性必须设置为false。此外,当使用@Autowired标注多个构造器时,Spring会从所有满足装配条件的构造器中选择入参最多的那个。

限制歧义性的依赖

当有多个Bean满足装配条件时,可以配合使用@Qualifier注解。

1
2
3
@Autowired
@Qualifier("guitar")
private Instrument instrument;

@Qualifier注解缩小了自动装配候选Bean的范围。

@Inject

和@Autowired一样,@Inject可以装配 属性、方法和构造器;但是@Inject没有required属性,因此@Inject注解所依赖的bean是必须存在的,如果不存在就会抛出异常。

使用@Inject注入一个Provider,从而可以实现Bean引用的延迟注入以及注入多个Bean实例的功能。

1
2
3
4
5
6
7
8
9
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

1
2
3
@Inject
@Named("guitar")
private Instrument instrument;

在注解中使用表达式:@Value

可以使用@Value装配简单值:String类型和基本类型,如:

1
2
@Value("Happy")
private String song;

@Value可以配合SpEL使用:

1
2
@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:标注为自定义注解。
1
2
3
4
5
6
7
8
9
10
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注解的类。

1
2
3
4
5
6
<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>
hoxis wechat
一个脱离了高级趣味的程序员,关注回复1024有惊喜~
赞赏一杯咖啡
0%