3

Spring配置的可选方案

Spring容器负责创建应用程序中的bean并通过DI来协调这些对象之间的关系。当描述bean如何进行装配时,Spring具有非常大的灵活性,它提供了三种主要的装配机制:

  • 在XML中进行显式配置

  • 在Java中进行显式配置

  • 隐式的bean发现机制和自动装配

尽可能地使用自动配置的机制。显式配置越少越好。当你必须要显式配置bean的时候(比如,有些源码不是由你来
维护的,而当你需要为这些代码配置bean的时候),推荐使用类型安全并且比XML更加强大的JavaConfig。最后,只有想要使用便利的XML命名空间,并且在JavaConfig中没有同样的实现时,才应该使用XML

自动化装配bean

Spring从两个角度来实现自动化装配:

  • 组件扫描(component scanning):Spring会自动发现应用上下文中所创建的bean

  • 自动装配(autowiring):Spring自动满足bean之间的依赖

组件扫描和自动装配组合在一起就能发挥出强大的威力,它们能够将显式配置降低到最少

创建可被发现的bean

CompactDisc接口在Java中定义了CD的概念

package soundsystem;

public interface CompactDisc
{
    void play();
}

CompactDisc的具体内容并不重要,重要的是你将其定义为一个接口。作为接口,它定义了CD播放器对一盘CD所能进行的操作。它将CD播放器的任意实现与CD本身的耦合降低到了最小的程度

带有@Component注解的CompactDisc实现类SgtPeppers

package soundsystem;
import org.springframework.stereotype.Component;

@Component
public class SgtPeppers implements CompactDisc
{
    private String title = "Sgt. Pepper's Lonely Hearts Club Band";
    private String artist = "The Beatles";
    
    public void play()
    {
        System.out.println("Playing" + title + "by" + artist);
    }
}

SgtPeppers类上使用了@Component注解。这个简单的注解表明SgtPeppers类会作为组件类,并告知Spring要为这个类创建bean。没有必要显式配置SgtPeppersbean,因为这个类使用了@Component注解,所以Spring会为你把事情处理妥当

不过,组件扫描默认是不启用的。我们还需要显式配置一下Spring,从而命令它去寻找带有@Component注解的类,并为其创建bean

@ComponentScan注解启用了组件扫描

以下程序的配置类展现了完成这项任务的最简洁配:

package soundsystem;
import org.springframework.context.annotation.componentScan;
import org.springframework.context.annotation.Con;
    
@Component
@ComponentScan
public class CDPlayerConfig{}

类CDPlayerConfig通过Java代码定义了Spring的装配规则。观察可知,CDPlayerConfig类并没有显式地声明任何bean,只不过它使用了@ComponentScan注解,这个注解能够在Spring中启用组件扫描

如果没有其他配置的话,@ComponentScan默认会扫描与配置类相同的包。因为CDPlayerConfig类位于soundsystem包中,因此Spring将会扫描这个包以及这个包下的所有子包,查找带有@Component注解的类。这样的话,就能发现CompactDisc,并且会在Spring中自动为其创建一个bean

通过XML启用组件扫描

如更倾向于使用XML来启用组件扫描的话,那么可以使用Spring context命名空间的<context:component-scan>元素。以下程序展示了启用组件扫描的最简洁XML配置:

<?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/comtext"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/beans/context
        http://www.springframework.org/schema/beans/context/spring-context.xsd">
        
    <context:component-scan base-package="soundsystem"/>
    
</beans>

测试组件扫描能够发现CompactDisc

package soundsystem;

import static org.junit.Assert.*;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes=CDPlayerConfig.class)
public class CDPlayerTest
{
    @Autowired
    private CompactDisc cd;
    
    @test_139772[test] 
    public void cdShouldNotBeNull()
    {
        assertNotNull(cd);
    }
}

CDPlayerTest使用了Spring的SpringJUnit4ClassRunner,以便在测试开始的时候自动创建Spring的应用上下文。注解@ContextConfiguration会告诉它需要在CDPlayerConfig中加载配置。因为CDPlayerConfig类中包含了@ComponentScan,因此最终的应用上下文中应该包含CompactDiscbean

在测试代码中有一个CompactDisc类型的属性,并且这个属性带有@Autowired注解,以便于将CompactDiscbean
注入到测试代码之中。最后,会有一个简单的测试方法断言cd属性不为null。如果它不为null的话,就意味着Spring能够发现CompactDisc类,自动在Spring上下文中将其创建为bean并将其注入到测试代码之中

为组件扫描的bean命名

Spring应用上下文中所有的bean都会给定一个ID。前面的例子中,尽管没有明确地为SgtPeppersbean设置ID,但Spring会根据类名为其指定一个ID。具体来讲,这个bean所给定的ID为sgtPeppers,也就是将类名的第一个字母变为小写

若想为这个bean设置不同的ID,则需将期望的ID作为值传递给@Component注解:

@Component("lonelyHeartsClub")
public class SgtPeppers implements CompactDisc
{
    ...
}

另外一种为bean命名的方式,使用Java依赖注入规范(Java Dependency Injection)中所提供的@Named注解来为bean设置ID:

package soundsystem;
import javax.inject.Named;
@Named("lonelyHeartsClub")
public class SgtPeppers implements CompactDisc
{
    ...
}

设置组件扫描的基础包

为了指定不同的基础包,所需要在@ComponentScan的value属性中指明包的名称:

@Configuration
@ComponentScan("soundsystem")
public class CDPlayerConfig{}

若需更加清晰地表明你所设置的是基础包,通过basePackages属性进行配置:

@Configuration
@ComponentScan(basePackages = "soundsystem")
public class CDPlayerConfig{}

如果需要可以设置多个基础包,只需要将basePackages属性设置为要扫描包的一个数组即可:

@Configuration
@ComponentScan(basePackages = {"soundsystem", “videos”})
public class CDPlayerConfig{}

上面例子中,所设置的基础包是以String类型表示的。这是可行的,但这种方法是类型不安全(not type-safe)的。如进行代码重构,那么所指定的基础包可能会出现错误

除了将包设置为简单的String类型之外,@ComponentScan提供的另外一种方法,将其指定为包中所包含的类或接口:

@Configuration
@ComponentScan(basePackageClasses = {CDPlayer.class, DVDPlayer.class})
public class CDPlayerConfig{}

通过为bean添加注解实现自动装配

自动装配就是让Spring自动满足bean依赖的一种方法,在满足依赖的过程中,会在Spring应用上下文中寻找匹配某个bean需求的其他bean。为了声明要进行自动装配,我们可以借助Spring的@Autowired注解

下述程序CDPlayer类。它的构造器上添加了@Autowired注解,这表明当Spring创建CDPlayerbean的时候,会通过这个构造器来进行实例化并且会传入一个可设置给CompactDisc类型的bean

通过自动装配,将一个CompactDisc注入到CDPlayer之中:

package soundsystem;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class CDPlayer implement MediaPlayer
{
    private CompactDisc cd;
    
    @Autowired
    public CDPlayer(CompactDisc cd)
    {
        this.cd = cd;
    }
    
    public void play
    {
        cd.play();
    }
}

@Autowired注解不仅能够用在构造器上,还能用在属性的Setter方法上。比如说,如果CDPlayer有一个setCompactDisc()方法,那么可以采用如下的注解形式进行自动装配:

@Autowired
public void setCompactDisc(CompactDisc cd)

@Autowired注解可以用在类的任何方法上。假设CDPlayer类有一个insertDisc()方法,那么@Autowired能够像在setCompactDisc()上那样,发挥完全相同的作用:

@Autowired
public void insertDisc(CompactDisc cd)
{
    this.cd = cd;
}

不管是构造器、Setter方法还是其他的方法,Spring都会尝试满足方法参数上所声明的依赖。假如有且只有一个bean匹配依赖需求的话,那么这个bean将会被装配进来

如果没有匹配的bean,那么在应用上下文创建的时候,Spring会抛出一个异常。为了避免异常的出现,你可以将@Autowired的required属性设置为false:

@Autowired
public CDPlayer(CompactDisc cd)
{
    this.cd = cd;
}

将required属性设置为false时,Spring会尝试执行自动装配,若没有匹配的bean,Spring将会让这个bean处于未装配的状态。但把required属性设置为false时,需要谨慎对待。如果代码中没有进行null检查,处于未装配状态的属性可能出现NullPointerException

@Autowired是Spring特有的注解。如果不愿代码中到处使用
Spring的特定注解来完成自动装配任务,可考虑替换为@Inject:

package soundsystem;
import javax.inject.Inject;
import javax.inject.Named;

@Named
public class CDPlayer
{
    ...
    @Inject
    public CDPlayer(CompactDisc cd)
    {
        this.cd = cd;
    }
    ...
}

@Inject注解来源于Java依赖注入规范,该规范同时还定义了@Named注解。在自动装配中,Spring同时支持@Inject和
@Autowired。尽管@Inject和@Autowired之间有着一些细微的差别,但是在大多数场景下,是可以互相替换的

验证自动装配

图片描述

除了注入CompactDisc,还将CDPlayerbean注入到测试代码的player成员变量之中(它是更为通用的MediaPlayer类型)。在play()测试方法中,我们可以调用CDPlayer的play()方法,并断言它的行为与预期一致

在测试代码中使用System.out.println()。因此,该样例中使用了StandardOutputStreamLog,是来源于System Rules库(http://stefanbirkner.github.i...)的一个JUnit规则,该规则能够基于控制台的输出编写断言。SgtPeppers.play()方法的输出将被发送到控制台上

通过Java代码装配bean

如想将第三方库中的组件装配到你的应用中,在这种情况下无法通过在它的类上添加@Component和@Autowired注解的方法实现自动化装配

JavaConfig与其他的Java代码存在区别,在概念上,它与应用程序中的业务逻辑和领域代码不同的。尽管都使用相同的语言进行表述,但JavaConfig是配置代码。这意味着它不应该包含任何业务逻辑,JavaConfig也不应该侵入到业务逻辑代码之中。尽管不是必须的,但通常会将JavaConfig放到单独的包中,使它与其他的应用程序逻辑分离开来,这样对于它的意图就不会产生困惑了

创建配置类

创建JavaConfig类的关键在于为其添加@Configuration注解,@Configuration注解表明这个类是一个配置类,该类应该包含在Spring应用上下文中如何创建bean的细节

package soundsystem;
import org.springframework.context.annotation.Configuration;

@Configuration
public class CDPlayerConfig
{
    ...
}

到此为止,都是依赖组件扫描来发现Spring应该创建的bean。尽管可以同时使用组件扫描和显式配置,但在本节更加关注于显式配置,因此将CDPlayerConfig的@ComponentScan注解移除掉了。移除了@ComponentScan注解,此时的CDPlayerConfig类就没有任何作用了。如现在运行CDPlayerTest,测试失败,并出现BeanCreation-Exception异常。测试期望被注入CDPlayer和CompactDisc,但这些bean根本就没有创建,因为组件扫描不会发现它们。下一节,将展现如何使用JavaConfig装配CDPlayer和CompactDisc

声明简单的bean

要在JavaConfig中声明bean,需编写一个创建所需类型实例的方法,然后给这个方法添加@Bean注解。如下代码声明了CompactDisc bean:

@Bean
public CompactDisc sgtPeppers()
{
    return new SgtPeppers();
}

@Bean注解会告诉Spring这个方法将会返回一个对象,该对象要注册为Spring应用上下文中的bean。方法体中包含了最终产生bean实例的逻辑

默认情况下,bean的ID与带有@Bean注解的方法名是一样的。在本例中,bean的名字将会是sgtPeppers。如果你想为其设置成一个不同的名字的话,那么可以重命名该方法,也可以通过name属性指定一个不同的名字:

@Bean(name= "lonelyHeartsClubBand")
public CompactDisc sgtPeppers()
{
    return new SgtPeppers();
}

借助JavaConfig实现注入

前面所声明的CompactDisc bean非常简单,自身没有其他依赖。现在,需要声明依赖于CompactDisc的CDPlayer bean。在JavaConfig中装配bean的最简单方式就是引用创建bean的方法。如下就是一种声明CDPlayer的可行方案:

@Bean
public CDPlayer cdPlayer()
{
    return new CDPlayer(sgtPeppers());
}

这个方法会创建一个bean实例并将其注册到Spring应用上下文中。所创建的bean ID为cdPlayer,与方法的名字相同。cdPlayer()的方法体与sgtPeppers()稍微有些区别。在这里并没有使用默认的构造器构建实例,而是调用了需要传入CompactDisc对象的构造器来创建CDPlayer实例。看起来,CompactDisc是通过调用sgtPeppers()得到的,但情况并非完全如此。因为sgtPeppers()方法上添加了@Bean注解,Spring将会拦截所有对它的调用,并确保直接返回该方法所创建的bean,而不是每次都对其进行实际的调用

@Bean
public CDPlayer cdPlayer()
{
    return new CDPlayer(sgtPeppers());
}
@Bean
public CDPlayer anotherCDPlayer()
{
    return new CDPlayer(sgtPeppers());
}

默认情况下,Spring中的bean都是单例的,并没必要为第二个CDPlayer bean创建完全相同的SgtPeppers实例。所以,Spring会拦截对sgtPeppers()的调用并确保返回的是Spring所创建的bean,也就是Spring本身在调用sgtPeppers()时所创建的CompactDiscbean。因此,两个CDPlayer bean会得到相同的SgtPeppers实例

@Bean
public CDPlayer cdPlayer(CompactDisc compactDisc)
{
    return new CDPlayer(sgtPeppers());
}

在这里,cdPlayer()方法请求一个CompactDisc作为参数。当Spring调用cdPlayer()创建CDPlayerbean的时候,它会自动装配一个CompactDisc到配置方法之中。然后,方法体就可以按照合适的方式来使用它。借助这种技术,cdPlayer()方法也能够将CompactDisc注入到CDPlayer的构造器中,而且不用明确引用CompactDisc的@Bean方法

通过这种方式引用其他的bean通常是最佳的选择,因为它不会要求将CompactDisc声明到同一个配置类之中。在这里甚至没有要求CompactDisc必须要在JavaConfig中声明,实际上它可以通过组件扫描功能自动发现或者通过XML来进行配置

可以将配置分散到多个配置类、XML文件以及自动扫描和装配bean之中,只要功能完整健全即可。不管CompactDisc是采用什么方式创建出来的,Spring都会将其传入到配置方法中,并用来创建CDPlayer bean

通过XML装配bean

创建XML配置规范

在使用XML为Spring装配bean之前,你需要创建一个新的配置规范。在使用JavaConfig的时候,这意味着要创建一个带有@Configuration注解的类,而在XML配置中,这意味着要创建一个XML文件,并且要以<beans>元素为根

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/beans/context">
        
    <!-- configuration details go here -->
    
</beans>

如上基本的XML配置已比同等功能的JavaConfig类复杂。在JavaConfig中只需要@Configuration,但在XML中,需要在配置文件的顶部声明多个XML模式(XSD)文件,这些文件定义了配置Spring的XML元素

借助Spring Tool Suite创建XML配置文件创建和管理Spring XML配置文件的一种简便方式是使用Spring Tool Suite(https://spring.io/tools/sts)。在Spring Tool Suite的菜单中,选择File>New>Spring Bean Configuration File,能够创建Spring XML配置文件,并且可以选择可用的配置命名空间

用来装配bean的最基本的XML元素包含在spring-beans模式之中,在上面这个XML文件中,它被定义为根命名空间。<beans>是该模式中的一个元素,它是所有Spring配置文件的根元素

声明一个简单的<bean>

要在基于XML的Spring配置中声明一个bean,我们要使用springbeans模式中的另外一个元素:<bean>。<bean>元素类似于JavaConfig中的@Bean注解。可以如下的方式声明CompactDiscbean:

<bean class="soundsystem.SgtPeppers" />

这里声明了一个简单的bean,创建这个bean的类通过class属性来指定的,并且要使用全限定的类名

因没明确给定ID,所以这个bean将会根据全限定类名来进行命名。在本例中,bean的ID将会是“soundsystem.SgtPeppers#0”。其中,“#0”是一个计数的形式,用来区分相同类型的其他bean。如果你声明了另外一个SgtPeppers,并且没有明确进行标识,那么它自动得到的ID将会是“soundsystem.SgtPeppers#1”

通常来讲,更好的办法是借助id属性,为每个bean设置一个你自己选择的名字:

<bean id = "compactDisc" class="soundsystem.SgtPeppers" />    

减少繁琐为了减少XML中繁琐的配置,只对那些需要按名字引用的bean(比如,你需要将对它的引用注入到另外一个bean中)进行明确地命名

借助IDE检查XML的合法性使用能够感知Spring功能的IDE,如Spring Tool Suite,能够在很大程度上帮助你确保Spring XML配置的合法性

借助构造器注入初始化bean

在Spring XML配置中,只有一种声明bean的方式:使用<bean>元素并指定class属性。Spring会从这里获取必要的信息来创建bean。但是,在XML中声明DI时,会有多种可选的配置方案和风格。具体到构造器注入,有两种基本的配置方案可供选择:

  • <constructor-arg>元素

  • 使用Spring 3.0所引入的c-命名空间

构造器注入bean引用

1.<constructor-arg>元素方案:
现已声明SgtPeppers bean,并且SgtPeppers类实现了CompactDisc接口,所以实际上已经一个可以注入到CDPlayerbean中的bean。所需做的是在XML中声明CDPlayer并通过ID引用SgtPeppers:

<bean id="cdplayer" class="soundsystem.CDPlayer">
    <constructor-arg ref="compactDisc" />
</bean>

当Spring遇到这个<bean>元素时,会创建一个CDPlayer实例。<constructor-arg>元素会告知Spring要将一个ID
为compactDisc的bean引用传递到CDPlayer的构造器中

2.c-命名空间方案:
c-命名空间是在XML中更为简洁地描述构造器参数的方式。要使用它的话,必须要在XML的顶部声明其模式,如下所示:

<?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:c="http://www.springframework.org/schema/c"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="bar" class="x.y.Bar"/>
    <bean id="baz" class="x.y.Baz"/>

    <!-- 标准XML格式 -->
    <bean id="foo" class="x.y.Foo">
        <constructor-arg name="bar" ref="bar"/>
        <constructor-arg name="baz" ref="baz"/>
        <constructor-arg name="email" value="foo@bar.com"/>
    </bean>

    <!-- c命名空间格式 -->
    <bean id="foo" class="x.y.Foo" c:bar-ref="bar" c:baz-ref="baz" c:email="foo@bar.com"/>

    <!-- 还可以使用c命名空间的参数索引格式 -->
    <bean id="foo" class="x.y.Foo" c:_0-ref="bar" c:_1-ref="baz" c:_2="foo@bar.com"/>

</beans>

在c-命名空间和模式声明之后,就可以使用它来声明构造器参数了,如下所示:

<bean id="cdplayer" class="soundsystem.CDPlayer"
    c:cd-ref="compactDisc" />
    

属性名以“c:”开头,也就是命名空间的前缀。接下来就是要装配的构造器参数名,在此之后是“-ref”,这是一个命名的约定,它会告诉Spring,正在装配的是一个bean的引用,这个bean的名字是compactDisc,而不是字面量“compactDisc”
图片描述

引用参数的名称有些怪异,因为需要编译代码时,将调试标志(debug symbol)保存在类代码中。如果优化构建过程,将调试标志移除掉,那么这种方式可能就无法正常执行。根本不用去标示参数的方案:

<bean id="cdplayer" class="soundsystem.CDPlayer"
    c:_-ref="compactDisc" />

将字面量(literal value)注入到构造器中

迄今为止,所做的DI通常指的都是类型的装配——也就是将对象的引用装配到依赖于它们的其他对象之中——而有时需要的只是用一个字面量值来配置对象

1.<constructor-arg>元素方案:

package soundsystem;
public class BlankDisc implements CompactDisc
{
    private String title;
    private String artist;
    
    public BlankDisc(String title, String artist)
    {
        this.title = title;
        this.artist = artist;
    }
    
    public void play()
    {
        System.out.println("Playing" + title + "by" + artist);
    }
}
<bean id="compactDisc" class="soundsystem.BlankDisc">
    <constructor-arg value = "Sgt. Pepper's Lonely Hearts Club Band" />
    <constructor-arg value = "The Beatles" />
</bean>

上述程序使用<constructor-arg>元素进行构造器参数的注入。没有使用“ref”属性来引用其他的bean,而使用了
value属性,通过该属性表明给定的值要以字面量的形式注入到构造器之中

2.c-命名空间方案:

引用构造器参数名:

<bean id="compactDisc" class="soundsystem.BlankDisc"
    c:_title="Sgt. Pepper's Lonely Hearts Club Band" 
    c:_artist="The Beatles" />

通过参数索引装配相同的字面量值:

<bean id="compactDisc" class="soundsystem.BlankDisc"
    c:_0="Sgt. Pepper's Lonely Hearts Club Band"
    c:_1="The Beatles" />               

装配集合

含有磁道列表概念的BLankDisc

package soundsystem.collections;

import java.ytil.List;
import soundsystem.CompactDisc;

public class BlankDisc implements CompactDisc
{
    private String title;
    private String artist;
    private List<String> tracks;
    
    public BlankDisc(String title, String artist, List<String> tracks)
    {
        this.title = title;
        this.artist = artist;
        this.tracks = tracks;
    }
    
    public void play()
    {
        System.out.println("Playing" + title + "by" + artist);
        for(String track : tracks)
        {
            System.out.println("-Track: " + track);
        }
    }
}

这个变更会对Spring如何配置bean产生影响,在声明bean时,必须要提供一个磁道列表。较好的解决方法是提供一个磁道名称的列表。首先,可以使用<list>元素将其声明为一个列表:

<bean id="compactDisc" class="soundsystem.BlankDisc">
    <constructor-arg value = "Sgt. Pepper's Lonely Hearts Club Band" />
    <constructor-arg value = "The Beatles" />
    <constructor-arg>
        <list>
            <value>Sgt. Pepper's Lonely Hearts Club Band</value>
            <value>With a Little Help from My Friend</value>
            <value>Lucy in the Sky with Diamonds</value>
            <value>Getting Better</value>
            <value>Fixing a Hole</value>
            <!-- ...other tracks omiitted for brevity... ->
        </list>
    </constructor-arg>
</bean>

其中,<list>元素是<constructor-arg>的子元素,这表明一个包含值的列表将会传递到构造器中。其中,<value>元素用来指定列表中的每个元素

也可使用<ref>元素替代<value>,实现bean引用列表的装配。假设有一个Discography类,构造器如下所示:

public Discography(String artist, List<CompactDisc> cds)
{
    ...
}

采取如下的方式配置Discography bean:

<bean id="beatlesDiscography" class="soundsystem.Discography">
    <constructor-arg value = "The Beatles" />
    <constructor-arg>
        <list>
            <ref bean="SgtPeppers" />
            <ref bean="whiteAlbum" />
            <ref bean="hardDaysNight" />
            <ref bean="revolver" />
            ...
        </list>
    </constructor-arg>
</bean>

在装配集合方面,<constructor-arg>比c-命名空间的属性更有优势。目前,使用c-命名空间的属性无法实现装配集合的功能。使用<constructor-arg>和c-命名空间实现构造器注入时,它们之间还有一些细微的差别

设置属性

使用Spring XML实现属性注入

设属性注入的CDPlayer如下所示:

package soundsystem;
import org.springframework.beans.factory.annotation.Autowired;
import soundsystem.CompactDisc;
import soundsystem.MediaPlayer;

public class CDPlayer implement MediaPlayer
{
    private CompactDisc compactDisc;
    
    @Autowired
    public CDPlayer(CompactDisc compactDisc)
    {
        this.compactDisc = compactDisc;
    }
    
    public void play
    {
        compactDisc.play();
    }
}

作为一个通用的规则,强依赖使用构造器注入,而对可选性的依赖使用属性注入

p-命名空间:

<?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"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!-- 标准XML格式 -->
    <bean name="john-classic" class="com.example.Person">
        <property name="name" value="John Doe"/>
        <property name="spouse" ref="jane"/>
    </bean>

    <!-- p命名空间格式 -->
    <bean name="john-modern"
        class="com.example.Person"
        p:name="John Doe"
        p:spouse-ref="jane"/>

</beans>

使用p-命名空间装配compactDisc属性:

<bean id = "cdplayer"
      class = "soundsystem.CDPlayer"
      p:compactDisc-ref = "compactDisc" />

图片描述

属性的名字使用了“p:”前缀,表明我们所设置的是一个属性。接下来就是要注入的属性名。最后,属性的名称以“-ref”结尾,这会提示Spring要进行装配的是引用,而不是字面量

将字面量注入到属性中

以下BlankDisc完全通过属性注入进行配置,而不是构造器注入。新的BlankDisc类如下所示:

package soundsystem.collections;

import java.ytil.List;
import soundsystem.CompactDisc;

public class BlankDisc implements CompactDisc
{
    private String title;
    private String artist;
    private List<String> tracks;
    
    public setTitle(String title)
    {
        this.title = title;
    }
    
    public setArtistString artist)
    {
        this.artist = artist;
    }
            
    public setTracks(List<String> tracks)
    {
        this.tracks = tracks;
    }
    
    public void play()
    {
        System.out.println("Playing" + title + "by" + artist);
        for(String track : tracks)
        {
            System.out.println("-Track: " + track);
        }
    }
}

借助<property>元素的value属性装配这些属性,内嵌<list>元素设置tracks属性:

<bean id="compactDisc" class="soundsystem.BlankDisc">
    <property name = "title" value = "Sgt. Pepper's Lonely Hearts Club Band" />
    <property name = "artist" value = "The Beatles" />
    <property name = "tracks">
        <list>
            <value>Sgt. Pepper's Lonely Hearts Club Band</value>
            <value>With a Little Help from My Friend</value>
            <value>Lucy in the Sky with Diamonds</value>
            <value>Getting Better</value>
            <value>Fixing a Hole</value>
            <!-- ...other tracks omiitted for brevity... ->
        </list>
    </property>
</bean>

使用p-命名空间:

<bean id = "compactDisc" 
    class="soundsystem.BlankDisc"
    p:title = "Sgt. Pepper's Lonely Hearts Club Band"
    p:artist = "The Beatles">
    <property name = "tracks">
        <list>
            <value>Sgt. Pepper's Lonely Hearts Club Band</value>
            <value>With a Little Help from My Friend</value>
            <value>Lucy in the Sky with Diamonds</value>
            <value>Getting Better</value>
            <value>Fixing a Hole</value>
            <!-- ...other tracks omiitted for brevity... ->
        </list>
    </property>
</bean>

与c-命名空间一样,装配bean引用与装配字面量的唯一区别在于是否带有“-ref”后缀。如果没有“-ref”后缀的话,所装配的就是字面量

不能使用p-命名空间来装配集合,没有便利的方式使用p-命名空间来指定一个值(或bean引用)的列表。但可以使用Spring util-命名空间中的一些功能来简化BlankDiscbean

首先,需要在XML中声明util-命名空间及其模式:

<?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://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">
    ...
</beans>

util-命名空间所提供的功能之一就是<util:list>元素,它会创建一个列表的bean。<util:list>可将磁道列表转移到BlankDisc bean之外,并将其声明到单独的bean之中,如下所示:

<util:list id = "trackList">
    <value>Sgt. Pepper's Lonely Hearts Club Band</value>
    <value>With a Little Help from My Friend</value>
    <value>Lucy in the Sky with Diamonds</value>
    <value>Getting Better</value>
    <value>Fixing a Hole</value>
    <!-- ...other tracks omiitted for brevity... ->
</util:list>

将磁道列表bean注入到BLANkDisc bean的tracks属性中:

<bean id = "compactDisc" class = "soundsystem.BlankDisc"
    p:title = "Sgt. Pepper's Lonely Hearts Club Band"
    p:artist = "The Beatles"
    p:tracks-ref = "trackList" />
元素 描述
<util:constant> 引用某个类型的public static域,并将其暴露为bean
<util:list> 创建一个java.util.List类型的bean,其中包含值或引用
<util:map> 创建一个java.util.Map类型的bean,其中包含值或引用
<util:properties> 创建一个java.util.properties类型的bean
<util:property-path> 引用一个bean的属性(或内嵌属性),并将其暴露为bean
<util:set> 创建一个java.util.Set类型的bean,其中包含值或引用

导入和混合配置

在JavaConfig中引用XML配置

现在假设CDPlayerConfig已经有些笨重,因此将其进行拆分。虽然目前只定义了两个bean,远远称不上复杂Spring配置。不过,在此假设两个bean已经算多了。一种方案是将BlankDisc从CDPlayerConfig拆分出来,定义到它自己的CDConfig类中,如下所示:

package soundsystem;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class CDConfig
{
    @Bean
    public CompactDisc compactDisc()
    {
        return new SgtPeppers();
    }
}

上述程序的compactDisc()方法已经从CDPlayerConfig中移除掉了,现需要将这两个类组合在一起。一种方法就是
在CDPlayerConfig中使用@Import注解导入CDConfig:

package soundsystem;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;    

@Configuration
@Import(CDConfig.class)
public class CDPlayerConfig
{
    @Bean
    public CDPlayer cdPlayer(CompactDisc compactDisc)
    {
        return new CDPlayer(compactDisc);
    }
}

更好的办法,不在CDPlayerConfig中使用@Import,而是创建一个更高级别的SoundSystemConfig,在
这个类中使用@Import将两个配置类组合在一起:

package soundsystem;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;    

@Configuration
@Import(CDConfig.class)
public class CDPlayerConfig
{
    @Bean
    public CDPlayer cdPlayer(CompactDisc compactDisc)
    {
        return new CDPlayer(compactDisc);
    }
}

更好的办法,不在CDPlayerConfig中使用@Import,而是创建一个更高级别的SoundSystemConfig,在这个类中使用@Import将两个配置类组合在一起:

package soundsystem;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

@Configuration
@Import({CDPlayerConfig.class, CDConfig.class})
public class SoundSystemConfig
{
}

现已将CDPlayer的配置与BlankDisc的配置分开,假设(基于某些原因)希望通过XML来配置BlankDisc,如下所示:

<bean id="compactDisc" class="soundsystem.BlankDisc">
    <c:_0 = "Sgt. Pepper's Lonely Hearts Club Band" />
    <c:_1 = "The Beatles" />
    <constructor-arg>
        <list>
            <value>Sgt. Pepper's Lonely Hearts Club Band</value>
            <value>With a Little Help from My Friend</value>
            <value>Lucy in the Sky with Diamonds</value>
            <value>Getting Better</value>
            <value>Fixing a Hole</value>
            <!-- ...other tracks omiitted for brevity... ->
        </list>
    </constructor-arg>
</bean>

使用@ImportResource注解同时加载XML配置和其他基于Java的配置,假设BlankDisc定义在名为cdconfig.
xml的文件中,该文件位于根类路径下,那么可以修改SoundSystemConfig,让它使用@ImportResource注解,如下所示:

package soundsystem;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

@Configuration
@Import(CDPlayerConfig.class)
@ImportResource("classpath:cd-config.xml")
public class SoundSystemConfig
{
}        

在XML配置中引用JavaConfig

在JavaConfig配置中,使用@Import和@ImportResource来拆分JavaConfig类。在XML中,使用import元素来拆分XML配置

设希望将BlankDisc bean拆分到自己的配置文件中,该文件名为cd-config.xml,这与之前使用@ImportResource是一样的。课在XML配置文件中使用<import>元素来引用该文件:

<?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:c="http://www.springframework.org/schema/c"    
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">
    <import resoune = "cd-config.xml"/>
    <bean id = "cdPlayer" class = "soundsystem.CDPlayer" c:cd-ref = "compactDisc"/>
</beans>

布still
461 声望32 粉丝

数据挖掘、用户行为研究、用户画像