《Spring3实战》摘要(3-1)--最小化Spring XML配置之自动装配Bean属性

第三章 最小化Spring XML配置

随着应用的不断发展,我们将不得不编写越来越复杂的XML配置。幸运的是,Spring提供了几种技巧,可以帮助我们减少XML的配置数量。

  • 自动装配(autowiring):有助于减少甚至消除配置<property>元素和<constructor-arg>元素,让Spring自动识别如何装配Bean的依赖关系。
  • 自动检测(autodiscovery):比自动装配更进一步,让Spring能够自动识别哪些类需要被配置成Spring Bean,从而减少对<bean>元素的使用。

3.1 自动装配Bean属性

3.1.1 4种类型的自动装配

当涉及自动装配Bean的依赖关系时,Spring有多种处理方式。因此,Spring提供了4种各具特色的自动装配策略。

  • byName——把与Bean的属性具有相同名字(或者ID)的其他Bean自动装配到Bean的对应属性中。如果没有跟属性的名字相匹配的Bean,则该属性不进行装配。

  • byType——-把与Bean的属性具有相同类型的其他Bean自动装配到Bean的对应属性中。如果没有跟属性的类型相匹配的Bean,则该属性不被装配。如果Spring寻找到多个符合要求的Bean,Spring会抛出异常。为了避免因为使用byType自动装配而带来的歧义,Spring为我们提供了另外两种选择:可以为自动装配标识一个首选Bean,或者可以取消某个Bean自动装配的候选资格。

    • 为自动装配标识一个首选Bean,可以使用<bean>元素的primary属性。如果只有一个自动装配的候选Bean的primary属性设置为true,name该Bean将比其他候选Bean优先被选择。但是primary属性默认设置为true,所以为了使用primary属性,需要将所有非首选Bean的primary属性设置为false。
    • 取消某个Bean的候选资格,可以设置这些Bean的autowire-candidate属性为false。
  • constructor——把与Bean的构造器入参具有相同类型的其他Bean自动装配到Bean构造器的对应入参中。constructor自动装配具有和byType自动装配相同的局限性。当发现多个Bean匹配某个构造器入参时,Spring会报错。如果一个类有多个构造器,它们都满足自动装配的条件时,spring也会报错。

  • autodetect——-首先尝试使用constructor进行自动装配。如果失败,再尝试使用byType进行自动装配。

<!-- 通过设置autowire属性为byName,Spring将为kenny的所有属性寻找与其名字相同的Spring Bean。在这里,Spring会发现instrument属性可以通过setter注入来进行自动装配。 -->
<bean id="instrument" class="com.springinaction.springidol.Saxophone" />

<bean id="kenny" class="com.springinaction.springidol.Instrumentalist" autowire="byName">
    <property name="song" value="Jingle Bells" />
</bean>

3.1.2 默认自动装配

如果需要为Spring应用上下文中的每一个Bean(或者其中的大多数)配置相同的autowire属性。我们可以在根元素<bean>上增加一个default-autowire属性。默认情况下,default-autowire属性为none。该属性配置仅对该配置文件下的Bean有效,我们也可以使用<bean>元素的autowire属性来覆盖默认自动装配策略。
这里写图片描述

3.1.3 混合使用自动装配和显式装配

我们对某个Bean选择了自动装配策略时,也可以同时对该Bean的某些属性进行显式装配。我们仍然可以为任意一个属性配置<property>元素。
这里写图片描述

3.2 使用注解装配

从Spring 2.5 开始,Spring开始支持使用注解自动装配Bean的属性。使用注解自动装配与在XML中使用autowire属性自动装配并没有太大的差别。但是使用注解方式允许更细粒度的自动装配。
Spring容器默认禁用注解装配。所以,在使用基于注解的自动装配前,我们需要在Spring配置中启用它。最简单的启用方式是使用Spring的context命名空间配置中的<context:annotation-config />元素。

这里写图片描述

Spring 3 支持几种不同的用于自动装配的注解:

  • Spring自带的@Autowired注解;
  • JSR-330 (Java依赖注入标准)的 @Inject 注解;
  • JSR-250 的 @Resource 注解。

3.2.1 使用 @Autowired

Spring的@Autowired注解是减少Spring XML配置的一种方式。但使用它的类会引入对Spring的特定依赖。

(1)Autowired注解标注setter方法,Spring 会尝试对该方法执行byType自动装配。

@Autowired
public void setInstrument(Instrument instrument){
    this.instrument = instrument;
}

(2)Autowired注解可以标注需要自动装配Bean引用的任意方法。

@Autowired
public void heresYourInstrument(Instrument instrument){
    this.instrument = instrument;
}

(3)Autowired注解标注构造器

@Autowired
public Instrumentalist(Instrument instrument){
    this.instrument = instrument;
}

(4)Autowired注解可直接标注属性,不需要setter方法。

@Autowired
private Instrument instrument;

可选的自动装配:配置required属性为false

默认情况下,@Autiwired具有强契约特征,其所标注的属性或参数必须是可以装配的。如果没有Bean可以装配到@Autowire的所标注的属性或参数中,自动装配就会失败(NoSuchBeanDefinitionWxception)。

通过设置@Autowired的required属性为false来配置自动装配是可选的。当Spring没有查找到与之匹配的类型为Instrument的Bean,应用就不会发生任何问题,而装配的属性值会设置为null。

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

@Autowired(required=false)
private Instrument instrument;

限定歧义性的依赖:使用@Qualifier注解

对于具有两个或多个bean都满足装配条件的情况,@Autowired注解没有办法选择哪一个Bean才是它真正需要的。所以会抛出NoUniqueBeanDefinitionException异常,明确表明装配失败了。

我们可以配合使用Spring的@Qualifier注解来帮助@Autowired鉴别出哪一个Bean才是我们所需要的。

<!-- 使用@Qualifier来明确指定装配id为guitar的Bean -->
@Autowired
@Qualifier("guitar")
private Instrument instrument;

<!-- 通过在Bean上直接使用Qualifier来缩小范围 -->
<bean class="com.springinaction.springidol.Guitar">
    <qualifier value="stringed" />
</bean>
<!-- 除了可以在 XML 中指定 qualifier,还可以使用 @Qualifier 注解来标注 Guitar 类-->
@Qualifier("stringed")
public class Guitar implements Instruments{
    ...
}

创建自定义的限定器(Qualifier):

/**
 * 定义一个注解,并使用@Qualifier注解作为它的元注解。例如,让我们创建自己的@StringedInstrument注解来充当限定器。
 */
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.beans.factory.annotation.Qualifier;

@Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface StringedInstrument{
}

/**
 * 通过自定义注解来代替@Qualifier标注Guitar
 */
 @StringedInstrument
 public class Guitar implements Instrument{
 }

/**
 * 使用@StringedInstruement 对自动装配的instrument属性进行限定
 */
 @Autowired
 @StringedInstrument
 private Instrument instrument;

3.2.2 借助@Inject实现基于标准的自动装配

从 Spring 3 开始,Spring 开始兼容 JSR-330 依赖注入规范。

@Inject注解是 JSR-330 的核心部件。该注解几乎可以完全替换 Spring 的 @Autowired 注解。使用 @Inject 注解,需要添加javax.inject包。

和 @Autowired 一样,@Inject 可以用来自动装配属性、方法和构造器;与 @Autowired 不太的是,@Inject 没有 required 属性。因此,@Inject 注解所标注的依赖关系必须存在,如果不存在,则会抛出异常。

/**
 * 除了 @Inject 注解,JSR-330 规范还提供了另一种技巧。Provider 接口可以实现 Bean 引用的延迟注入以及注入 Bean 的多个实例等功能。
 * 我们有一个knifeJuggler类需要注入一个或多个knife的实例。假设 knife bean 的作用域声明为 prototype,下面的 KnifeJuggler 的构造器将获得5个 Knife Bean:
 * KnifeJuggler 将获得一个 Provider<Knife> ,而不是在构造器中获得一个Knife实例。这个时候,只有 provider 被注入进去;在调用provider的 get() 方法前,实际的 Knife 对象并没有被注入。
 */
 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());
    }
 }

限定 @Inject 所标注的属性:@Named注解

@Inject 注解与 @Autowired 注解一样易导致歧义性的Bean定义。相对于 @Autowired 所对应的 @Qualifier,@Inject 所对应的是 @Named 注解。

import javax.inject.Inject;
import javax.inject.Named;
...

@Inject
@Named("guitar")
private Instrument instrument;

创建自定义的 JSR-330 Qualifier

JSR-330 在 javax.inject 包里有自己的 @Qualifier 注解。JSR-330 不建议使用该注解,但 JSR-330 鼓励我们使用该注解来创建自定义的限定器注解,就像我们使用Spring 的 @Qualifier 来创建自定义注解一样。实际上,@Named 注解就是一个使用 @Qualifier 注解所标注的注解。

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Documented;
import javax.inject.Qualifier;

@Retention(RetentionPolicy.RUNTIME)
@Qualifier
@Documented
public @interface StringedInstrument{
}

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

Spring 3.0 引入了 @Value ,它是一个新的装配注解,可以让我们使用注解装配String类型的值和基本类型的值。

我们可以通过 @Value 直接标注某个属性、方法或者方法参数,并传入一个String类型的表达式来装配属性,例如:

@Value("Eruption")
private String song;

装配简单的值并不是 @Value 所擅长的,不过,可以借助 SpEL 表达式,是使@Value 的功能得到强化。它是一种有效的基于注解驱动的装配方式,它可以根据SpEL表达式进行动态的求值计算。例如:

@Value("#{systemProperties.myFavoriteSong}")
private String song;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值