第三章 最小化Spring XML配置(Spring in action,3th)

4 篇文章 0 订阅
3 篇文章 0 订阅

                         第三章 最小Spring XML配置

到目前为止,知道如何使用<bean>元素定义Bean以及使用<constructor-arg>或<property>元素装配Bean。对于含有少量的Bean应用,工作量不大,但对于有成千上万的Bean应用来说,大量在XML上装配Bean,无疑是一件崩溃的事情。

Spring对此提供了几种方式,帮组减少XML的配置数量。

自动装配(autowiring)有助于减少甚至消除配置<property>元素和<constructor-arg>元素,让Spring自动识别如何装配Bean的依赖关系。

自动检查(autodiscovery)比自动装配更进一步,让Spring自动识别哪些类需要被配置为Spring Bean,而减少对<bean>元素的使用。

本章讨论如何利用Spring的自动装配和自动检查来减少配置Spring应用所需要的XML。还会探讨Spring基于Java的配置(如何使用Java代码而不是XML来配置Spring应用)。

自动装配Bean属性

4种类型的自动装配

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

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

byType——把与Bean的属性具有相同类型的其他Bean自动装配到Bean的对应属性中,如果没有跟属性的类型相匹配的Bean,则该属性不被装配。

constructor——把与Bean的构造器入参具有相同类型的其他Ban自动装配到Bean构造器的对应入参中。

autodetect——首先尝试使用constructor进行自动装配。如果失败,在尝试使用byType进行自动装配。注意:将查看源码发现Spring3.0.0.RELEASE版本后已经取消该属性值

byName 自动装配

Spring中,所有的东西都会赋予一个名字。Bean的属性也会有一个名字,若属性的名字恰好与要被装配到改属性的Bean的名字匹配,则Spring将这个Bean自动装配到高属性中。

<!-- 使用对象引用属性注入 -->
<bean class="cn.song9989.Instrumentalist" id="kenny">
    <property name="song" value="Tomorrow Is Anthor Day"/>
    <property name="instrument" ref="saxophone"/>
</bean>
<bean id="saxophone" class="cn.song9989.Saxophone"/>

上面<property>元素显式配置了kenny的instrument属性通过对应引用注入了另一个Bean saxophone。将上面改为:

<!-- 使用对象引用属性注入 -->
<bean class="cn.song9989.Instrumentalist" id="kenny" autowire="byName">
    <property name="song" value="Tomorrow Is Anthor Day"/>
</bean>
<bean id="instrument" class="cn.song9989.Saxophone"/>

这里Saxophone Bean的id属性与kenny Bean的instrument属性名字一样,通过配置autowire属相,spring可利用此信息自动装配kenny的instrument属性了。

byName自动装配遵循一项约定:为属性自动装配ID与改属性的名字相同的Bean。通过设置autowire属性为byName,Spring将特殊对待kenny的所有属性,为这些属性寻找与其名字相同的Spring Bean。Spring会发现instrument属性可以通过setter注入来进行自动装配。如下图所示,若应用上下文中存在id为instrument的Bean,该Bean会自动被装配到instrument属性中。

byName自动装配的缺点是需要假设Bean名字与其他Bean属性名字一样。

byType 自动装配

byType自动装配的工作方式和byName类似,只不过不在匹配属性名,而是检查属性的类型。

<!-- 使用对象引用属性注入 -->
<bean class="cn.song9989.Instrumentalist" id="kenny" autowire="byType">
    <property name="song" value="Tomorrow Is Anthor Day"/>
</bean>

若kenny Bean的autowire属性设置为byType,而不是byName,Spring容器会查找哪一个Bean的类型与Instrument类型匹配。若匹配则把该Bean装配导kenny的instrument属性中。

byType自动装配有一个局限性:当Spring容器中选找到多个Bean,Spring容器会直接抛出异常。所以,应用值允许存在一个Bean与需要自动装配的属性类型匹配。

为自动装配标识一个首选Bean,可以使用<bean>元素的primary属性。如果只有一个自动装配的候选Bean的primary属性设置为true,那么该Bean将比其他候选Bean优先被选择。

primary属性默认为true,意味着说有候选的Bean都将变成首选Bean(即,不存在首选Bean)。为使用primary属性,则得将所有非首选Bean的primary属性设置为false。经测试,两个类型相同的Bean,将非首选的Bean的primary属性改为false,运行时也会报错。得将首选的设置为true,其他非首选的都设置为false才可以。

<bean id="saxophone" class="cn.song9989.Saxophone" primary="false"/>

Primary属性进队标识首选Bean有意义,如果自动装配时,希望排除某些Bean,可以设置这些Bean的autowire-candidate属性为false。如下:

<bean id="saxophone" class="cn.song9989.Saxophone" autowire-candidate="false"/>

这里要求Spring在自动装配时忽略(saxophone)Bean作为候选Bean。

constructor 自动装配

如果要通过构造器注入来配置Bean,可以移除<constructor-arg>元素,由Spring在应用上下文中自动选择Bean注入到构造器入参中。

    例如,重新声明poeticJuggler Bean:

<!-- 构造器自动装配 -->
<bean class="cn.song9989.PoeticJuggler" id="poeticJuggler" autowire="constructor"/>

在poeticJuggler 的新声明中,<constructor-arg>元素取消,使用autowire属性并设置为constructor。Spring会尝试在容器中寻找一个PoticJuggler某一个构造器所有入参的Bean,将其注入到构造器中。

constructor自动装配和byType自动装配有同样的局限性。当发现多个Bean匹配某个构造器的入参时,Spring不会尝试猜测哪一个Bean更适合自动装配。此外,如果一个类有多个构造器,它们都满足自动装配条件时,Spring也不会尝试猜测哪一个构造器更适合。

最佳自动装配

若想自动装配Bean,但又不能决定使用哪一种类型的自动装配。可以设置autowire属性为autodetect,由Spring来决定。Spring3.0.0版本后已经不支持此属性值

<bean class="cn.song9989.PoeticJuggler" id="poeticJuggler3" autowire="autodetect"/>

默认自动装配

若需要为Spring应用上下文中的每一个Bean(或者大多数)配置相同的autowire属性,可以要求Spring为它所创建的所有Bean应用相同的自动装配策略来简化配置。需要做的是在<beans>元素上面增加一个default-autowire属性。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
       xmlns:util="http://www.springframework.org/schema/util"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd 
       http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"
       default-autowire="byType">
...
</beans>

默认情况下,default-autowire属性被设置为none,标记所有Bean都不适用自动装配。我们可以再default-autowire属性值设置为任意一种有效的自动装配策略,并将其应用于Spring配置文件(当前配置文件,非应用上下文)中的所有Bean。

混合使用自动装配

对某个Bean选择了自动装配策略,我们也可对该Bean的某些属性进行显示装配。为其任意属性配置<property>元素,就像<bean>上没有设置autowire一样。对于既在<bean>上这只autowire属性,又在其中配置<property>元素显示装配,有些多此一举。

<!-- 使用对象引用属性注入 -->
<bean class="cn.song9989.Instrumentalist" id="kenny" autowire="byType">
    <property name="song" value="Tomorrow Is Anthor Day"/>
    <property name="instrument" ref="saxophone"/>
</bean>

如在Spring上下文中有多个Bean实现Instrument接口,为规避Spring无法从这几个实现了Instrument接口的Bean中进行明确挑选而抛出的异常,可显式地装配instrument属性来覆盖自动装配。

注意事项:当使用constructor自动装配策略时,不能混合使用constructor自动装配和<constructor-arg>元素。

使用注解装配(推荐

Spring2.5后,Spring Bean的方式可以使用注解进行自动装配Bean的属性。使用注解自动装配与在XML中使用autowire属性自动装配并没有太大却别。但使用注解方式允许耕细粒度的自动装配。

Spring容器默认禁用注解装配,在使用基于注解的自动装配前,需要在Spring配置中启用它。最简单的启用方式是使用Spring的context命名空间配置中的<context:annotation-config/>元素。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
       xmlns:util="http://www.springframework.org/schema/util"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd
       http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
    <context:annotation-config/>
...
</beans>

<context:annotation-config>元素告诉Spring我们打算使用基于注解的自动装配。配置完成,我们可对代码添加注解,标识Spirng应该为属性、方法和构造器进行自动装配。

Spring3支持集中不同的自动装配注解:
    Spring自带的@Autowired注解;

    JSR-330的@Inject注解;

    JSR-250的@Resource注解。

使用@Autowired注解

    setter方法上面使用@Autowired注解

public class Instrumentalist implements Performer {
    // 歌曲
    private String song;
//年龄
    private Integer age;
    //乐器
    private Instrument instrument;
    @Autowired
    public void setInstrument(Instrument instrument) {
        this.instrument = instrument;
    }
    @Override
    public void permform() throws PerformanceException {
        System.out.println("Playing "+song+" : ");
        instrument.paly();
    }
// 省略其他的getter/setter方法
...
}
<!--注解使用-->
<context:annotation-config/>
<bean class="cn.song9989.annotation.Saxophone" id="saxophone"/>
<bean class="cn.song9989.annotation.Instrumentalist" id="tom">
    <property name="song" value="My Heart Will Go ON"/>
    <property name="age" value="28"/>
</bean>

对此可以移除用来定义Instrumentalist的instrument属性所对应的<property>元素了。当Spring发现我们队setInstrument()方法使用了@Autowired注解时,Spring会尝试执行byType自动装配。

    构造器上使用@Autowired注解

public class Instrumentalist implements Performer {
    // 歌曲
    private String song;
    //年龄
    private Integer age;
    //乐器
    private Instrument instrument;
    @Autowired
    public Instrumentalist(Instrument instrument) {
        this.instrument = instrument;
    }
// 省略其他的getter/setter方法
...
}

    在其他地方使用@Autowired注解

public class Instrumentalist implements Performer {
    // 歌曲
    private String song;
    //年龄
    private Integer age;
    //乐器
    private Instrument instrument;

    @Autowired
    public void heresYourInstrument(Instrument instrument) {
        this.instrument = instrument;
        // 可以做其他的业务操作
    }
// 省略其他的getter/setter方法
...
}
<!--注解使用-->
<context:annotation-config/>
<bean class="cn.song9989.annotation.Saxophone" id="saxophone"/>
<bean class="cn.song9989.annotation.Instrumentalist" id="tom">
    <property name="song" value="My Heart Will Go ON"/>
    <property name="age" value="28"/>
</bean>
public static void main(String[] args) throws PerformanceException {
    ApplicationContext context = new ClassPathXmlApplicationContext(
            "classpath:applicationContext-annotation.xml");
    Instrumentalist tom = context.getBean("tom", Instrumentalist.class);
    Instrument instrument = tom.getInstrument();
    instrument.paly();
}

    在属性上使用@Autowired注解(最常用)

public class Instrumentalist implements Performer {
    // 歌曲
    private String song;
    //年龄
    private Integer age;
    //乐器
    @Autowired
    private Instrument instrument;
// 省略其他的getter/setter方法
...
}

@Autowired的局限:应用中必须只能有一个Bean适合装配到@Autowired注解所标注的属性或参数中,如果没有匹配的Bean,或者存在多个匹配的Bean,@Autowired注解会报错。

可选的自动装配

默认情况下,@Autowired具有强契约特征,其所标注的属性或者参数必须是可装配的。若没有Bean可以装配到@Autowired所标注的属性或者参数中,自动装配就会失败(抛出NoSuchBeanDefinitionException)。属性不一定非要装配,null值也是可以接受的。在此场景下,可以通过设置@Autowired的required属性为false来配置自动装配时可选的。

@Autowired(required = false)
private Instrument instrument;

在这里,Spring将尝试装配instrument属性,但是如果没有查找到与之匹配的类型为Instrument的Bean,应用就不会发生任何问题,而instrument属性的值会设置为null。

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

 

限定歧义性的依赖

当在Spring容器中存在多个适合自动装配的Bean时,这些Bean都可以被装配到属性或者参数中,@Autowired注解没办法选择哪一个Bean才是它真正需要的。这是会抛出NoSuchBeanDefinitionException异常,明确表明装配失败。

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

@Autowired
@Qualifier("guitar")
private Instrument instrument;

如上所示,@Qualifier注解将尝试注入ID为guitar的Bean。

表面上看起来使用@Qualifier意味着把@Autowired的byType自动装配转换为显式的byName装配。@Qualifier注解缩小了自动装配挑选候选Bean的范围。

也可以在xml的上下文中配置限定配置,这个得配合@Qualigier注解使用。通过String类型的标识类型限定自动装配Bean。

<bean class="cn.song9989.annotation.Musician" id="musician" autowire="constructor">
    <qualifier value="guiter"/>
</bean>
@Qualifier("guitar")
public class Guitar implements Instrument {
    @Override
    public void paly() {}
}

public class Musician implements Performer {
    private Instrument instrument;
    public Musician(Instrument instrument) {
        this.instrument = instrument;
    }
    @Override
    public void permform() throws PerformanceException {
        System.out.println("Musician...");
        instrument.paly();
    }
}

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

创建自定义的限定注解,需要做的是定义一个注解,并使用@Qualifier注解作为它的元注解。例如,我们要创建字节的@StringedInstrument注解来充当限定器。

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

通过自定义注解@StringedInstrument注解,可以代替@Qualifier来标注Guitar。

@StringedInstrument
public class Saxophone implements Instrument {
    ...
}

public class Musician implements Performer {
    @Autowired
    @StringedInstrument
    private Instrument instrument;
    public Musician(Instrument instrument) {
        this.instrument = instrument;
    }
    @Override
    public void permform() throws PerformanceException {
        System.out.println("Musician...");
        instrument.paly();
    }
}
<bean class="cn.song9989.annotation.Musician" id="musician" />
public class AppCxt {
    public static void main(String[] args) throws PerformanceException {
        ApplicationContext context = new ClassPathXmlApplicationContext(
                "classpath:applicationContext-annotation.xml");
        Musician musician = context.getBean("musician", Musician.class);
        musician.permform();
    }
}

如果使用功能@StringedInstrument注解的乐器Bean有多个,我们还需进一步限定来缩小范围。那我们可以再声明一个注解(如@Strummed),通过新增注解方式缩小选择范围。

@Autowired
@StringedInstrument
@Strummed
private Instrument instrument;

Spring的@Autowired注解时减少Spring XML配置的一种方式。但是使用它的类会引入对Spring的特定依赖(即使这种依赖是只是一个注解)。为此,Spring还提供了标准的Java注解来替代@Autowired。

 

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

为统一各类依赖注入框架的编程模型,JCP(Java Community Process)发布了Java依赖注入规范。从Spring 3开始,Spring兼容JSR-330规范的依赖注入模型。[JSR-250注解为@Resource,@Resource可配合其属性name和type使用]

@Inject注解时JSR-330的核心部件。我们可使用@Inject注解来替代@Autowired注解。使用@Inject注解需要引入如下依赖包:

<dependency>
    <groupId>javax.inject</groupId>
    <artifactId>javax.inject</artifactId>
    <version>1</version>
</dependency>
@Inject
private Instrument instrument;

@Inject可以用来自动装配属性、方法和构造器。与@Autowired不同的是,@Inject没有required属性。故@Inject注解所标注的依赖关系必须存在,若不存在则抛出异常。

除@Inject注解,JSR-330提供一种技巧:与其直接注入一个引用,不如要求@Inject注入一个Provider。Provider接口可以实现Bean引用的延迟注入以及注入Bean的多个实例等功能。

如:有一个KnifeJuggler类需要注入一个或者多个Knife的实例。假设Knife Bean的作用域声明为prototype,下面KnifeJuggler的构造器将获得5个Knife Bean:

private Set<Knife> knives;

@Inject
public KnifeJuggler(Provider(Knife) knifeProvider){
    knives = new HashSet<>();
    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所标注的属性

和@Autowired一样,@Inject注解易导致歧义性的Bean定义。@Autowired有@Qualifier,@Inject则有@Named注解对应。

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

@Named("guitar")
public class Guitar implements Instrument {
    @Override
    public void paly() {
        System.out.println("Guitar playing...");
    }
}

Spring的@Qualifier与JSR-330的@Named的关键区别在于语义层面。@Qualifier注解帮助我们缩小所匹配的Bean的选择范围(默认使用Bean的ID),而@Named通过Bean的ID来标识可选择的Bean。

 

创建自定义的JSR-330 Qualifier

JSR-330在javax.inject包里面有自己的@Qualifier注解。此注解只能作用在注解上(如下源码)。咱们不能使用@Qualifier作用于属性,类,参数等上,但我们可使用该注解来创建自定义的限定器注解,就想使用Spring的@Qualifier来创建自定义注解一样。

@Target({ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Qualifier {
}

在注解注入中使用表达式

在Spring3.0引入了@Value注解,此为装配注解,可使用注解装配String类型的值和基本类型的值。通过@Value直接标注某个属性、方法或者方法参数,并传入一个String类型的表达式来装配属性。

@Value("Tomorrow Is Another Day")
private String song;

@Value装配硬编码并不实用,但可借助SpEL表达式,让@Value注解发挥真正的魔力。它一种基于注解驱动的装配方式,可根据SpEL表达式来进行的动态的求值计算。

@Value("#{guitar.name}")
private String song;

自动检测Bean

当在Spring配置中增加<context:annotation-config/>时,我们希望Spring特殊对待我们所定义的Bean里的某一组注解,并使用这些注解指导Bean的装配。使用<context:annotation-config/>有有助于完全消除Spring配置中的<property>和<constructor-arg>元素,但然需要使用<bean>元素显式定义Bean。<context:annotation-config/>只能用于激活那些已经在spring容器里注册过的bean,即Bean需要在容器中注册后才会自动注入

当在Spring配置中增加<context:component-scan/>元素除了完成<context:annotation-config/>一样的工作,还允许Spring自动检测Bean和定义Bean。这意味着不适用<bean>元素,但需要将需要注入到Spring容器中的Bean添加的注解(@Service/@Controller/@Repository/@Component)。配置使用Spring自动检测如下:

<context:component-scan base-package="cn.song9989"/>

<context:component-scan>元素会扫描指定的包以及所有子包,并查找出能够自动注册为Spring Bean的类。base-package属性标识了<context:component-scan>元素所扫描的包。

<context:component-scan>包含<context:annotation-config/>的功能,配置文件中可只配置<context:component-scan>。若两者都配置了,Spring也不会重复加载。

 

为自动检测标注Bean

假设应用上下文中仅仅有eddie和guitar两个Bean。使用<context:component-scan>元素并使用@Component注解标注Instrumentalist和Guitar类,可显式消除<bean>定义。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd">
    <context:component-scan base-package="cn.song9989"/>
</beans>
//  Spring 扫描会发现使用@Component注解所标注的Guitar,并自动将其注册为Spring Bean。Bean的ID默认为无限定类型,默认ID为类名且首字母小写。
@Component
public class Guitar implements Instrument {
    @Override
    public void paly() {
        System.out.println("Strum Strum Strum ...");
    }
}
// 指定了Bean ID作为@Component注解的参数,此时不会默认Instrumentalist的名称为instrumentliast,而是显式的命名为eddie。
@Component("eddie")
public class Instrumentalist implements Performer {
    // 歌曲
    @Value("My Heart Will Go On")
    private String song;
    //年龄
    private Integer age;
    //乐器
    @Autowired
    private Instrument instrument;
//省略getter/setter/toString方法
... 
@Override
    public void permform() throws PerformanceException {
        System.out.println("Playing " + song + " : ");
        instrument.paly();
    }
}

public static void main(String[] args) throws PerformanceException {
    ApplicationContext context = new ClassPathXmlApplicationContext(
            "classpath:applicationContext-annotation.xml");
    Instrumentalist eddie = (Instrumentalist) context.getBean("eddie");
    eddie.permform();
}

当使用<context:component-scan>时,基于注解地自动检测只是一种扫描策略。下面介绍配置<context:component-scan>的其他扫描策略。

 

过滤组件扫描

在扫描获得候选Bean方面,<context:component-scan>非常灵活。通过为<context:component-scan>配置<context:include-filter>或者<context:exclude-filter>子元素,可随意调整扫描行为。

为演示组件扫描的过滤机制,考虑下如何基于注解让<context:component-scan>自动注册所有实现Instrument接口的类。我们必须先看每一个Instrument实现的源码,并使用@Component(或者其他构造型注解)标注他们。这样很不方便,如果使用了第三方的Instrument实现,或许都没有源码访问权限来添加注解。故,替换掉基于注解的组件扫描策略,在增加一个包含过滤器来要求<context:component-scan>自动注册所有的Instrument实现类

<context:component-scan base-package="cn.song9989.annotation">
    <context:include-filter type="assignable" expression="cn.song9989.annotation.Instrument"/>
</context:component-scan>
// Instrument的实现类上没有添加@Component(或其他构造型注解),通过配置过滤器,Spring容器将其子类实现类实例化为了Bean。
public class Saxophone implements Instrument {
    @Override
    public void paly() {
        System.out.println("TOOT TOOT TOOT");
    }
}

public class Guitar implements Instrument {
    @Override
    public void paly() {
        System.out.println("Strum Strum Strum ...");
    }
}

@Component
public class Musician implements Performer {
    @Autowired
    @Qualifier("saxophone")
    private Instrument instrument;
    @Override
    public void permform() throws PerformanceException {
        System.out.println("Musician...");
        instrument.paly();
    }
}

@Component("eddie")
public class Instrumentalist implements Performer {
    // 歌曲
    @Value("My Heart Will Go On")
    private String song;
    //年龄
    private Integer age;
    //乐器
    @Autowired
    @Qualifier("guitar")
    private Instrument instrument;
//省略getter/setter/toString方法
    ... 
    @Override
    public void permform() throws PerformanceException {
        System.out.println("Playing " + song + " : ");
        instrument.paly();
    }
}

public class AppCxt {
    public static void main(String[] args) throws PerformanceException {
        ApplicationContext context = new ClassPathXmlApplicationContext(
                "classpath:applicationContext-annotation.xml");
        Performer performer = (Performer) context.getBean("eddie");
        performer.permform();

        performer = (Performer) context.getBean("musician");
        performer.permform();
    }
}

<context:include-filter>的type和expression属性一起协作来定义组件扫描策略。在这种情况下,我们要求派生于Instrument的所有类自动注册为Spring Bean。我们还可选择如下任意一种过滤器,如下:

过滤器类型

描述

annotation

过滤器扫描使用指定注解所标注的那些类,通过expression属性指定要扫描的注解

assignable

过滤器扫描派生于expression属性所指定类型的那些类

aspectj

过滤器扫描与expression属性所指定的AspectJ表达式所匹配的那些类

custom

使用自定义的org.springframework.core.type.TypeFilter实现类,该类由expression属性指定

regex

过滤器扫描类的名称与expression属性所指定的正则表达式所匹配的那些类

除了使用<context:include-filter>,还可使用<context:exclude-filter>来告知<context:component-scan>哪些类不需要注册为Spring Bean。例如除了使用自定义@SkipIt注解的类,其他所有的Instrument实现都需要注册为Spring Bean。

<context:component-scan base-package="cn.song9989.annotation">
    <context:include-filter type="assignable" expression="cn.song9989.annotation.Instrument"/>
    <context:exclude-filter type="annotation" expression="cn.song9989.annotation.SkipIt"/>
</context:component-scan>

使用Spring基于Java的配置

Spring的Java配置可以让我们不使用XML就可以编写大多数的Spring配置,但任需要少量的XML来启动Java配置。<context:component-scan>可自动注册那些使用某种构造器型注解所标注的Bean,它也会自动加载@Configuration注解所标注的类。在下面实例中,base-package属性告知Spring在cn.song9989.config下查找使用@Configuration注解所标注的所有类。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
      http://www.springframework.org/schema/context/spring-context.xsd">
    <context:component-scan base-package="cn.song9989.config"/>
</beans>

声明一个简单的Bean

使用注解@Bean标注在一个方法上来定义guitar Bean:

@Configuration
public class BaseConfig {
    @Bean
    public Instrument guitar(){
        return new Guitar();
    }
}

此方式等价在XML中所配置的<bean>元素,@Bean告知Spring这个方法返回一个对象,且该对象应该注册为Spring应用上下文中的一个Bean。方法签名为该Bean的ID。方法中实现的所有逻辑本质上都是为了创建Bean。

Spring的Java配置相对XML配置的优点。XML配置中,Bean蕾西选哪个和ID由String属性来标志,String标识符缺点是无法在编译期检查,若重名了Guitar类,或许忘记修改对应的XML配置,导致加载Bean时无法找到此类而失败。而Java配置方式,Bean的ID和类型都被视为方法签名的一部分。Bean的实际创建实在方法体中定义,我们可在编译期检查来确保Bean的类型是合法类型,且Bean的ID是唯一的。

使用Spring的基于Java的配置进行注入

构造器注入:

public class Instrumentalist implements Performer {
    // 歌曲
    private String song;
    //年龄
    private Integer age;
    //乐器
    private Instrument instrument;

    public Instrumentalist() {
    }

    public Instrumentalist(String song, Instrument instrument) {
        this.song = song;
        this.instrument = instrument;
    }
    public Instrumentalist(String song, Integer age,Instrument instrument){
        this.song = song;
        this.age = age;
        this.instrument = instrument;
    }

    @Override
    public void permform() throws PerformanceException {
        System.out.println("Playing " + song + " : ");
        instrument.paly();
    }
    //省略getter/setter/toString方法
    ...
}

@Configuration
public class BaseConfig {
    @Bean
    public Performer eddie() {
        return new Instrumentalist("My Heart Will Go On",guitar());
    }
    @Bean
    public Instrument guitar() {
        return new Guitar();
    }
}

属性注入:

@Bean
public Performer jackson() {
    Instrumentalist instrumentalist = new Instrumentalist();
    instrumentalist.setAge(19);
    instrumentalist.setSong("My Heart Will Go On");
    instrumentalist.setInstrument(guitar());
    return instrumentalist;
}

《Spring in action,3th》阅读笔记

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值