Spring实战(3):装配Bean——自动化、JavaConfig、XML

装配是什么?

创建应用对象之间协作关系的行为通常称为装配(wiring),这也是DI的本质。
我们必须要告诉Spring要创建哪些bean并且如何将其装配在一起。

Spring提供了三种主要的装配机制:

  • 在XML中进行显式配置
  • 在Java中进行显式配置
  • 隐式的bean发现机制和自动装配

Spring的配置风格是可以互相搭配的,但应尽可能地使用自动配置的机制,因其使用起来要方便的多,显式配置越少越好。显式配置越少,代码就越容易维护。而基于Java和XML相比,JavaConfig的安全性比XML要好,并且功能更强大。

所以我们先来看Spring的自动化配置。


自动化装配Bean——便利

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

  • 组件扫描(component scanning):Spring会自动发现应用上下文中所创建的Bean。
  • 自动装配 (autowiring):Spring自动满足Bean之间的依赖。

组件扫描和自动装配组合在一起就能使我们的显式配置降低到最少。

为了阐述组件扫描和装配,我们来看这个CD和CD播放器的例子~

1)组件扫描 : 创建可被发现的Bean

首先建立CD的概念,我们先定义CD的一个接口,它定义了CD播放器对一盘CD所能进行的操作。它将CD播放器的任意实现与CD本身的耦合降低到了最低。

2.1 : CD接口

package soundsystem;

public interface CompactDisc {
    void play();
}

现在,我们创建一个CD的实现,一盘霉霉的《TaylorSwift》CD。

2.2:实现一个《TaylorSwift》的CD

@Component
public class TaylorSwift implements CompactDisc {
    private String title = "TaylorSwift";
    private String artist = "MeiMei";

    public void play() {
        System.out.print("正在播放" + artist + "的" + title);
    }
}

这个TaylorSwift使用了@Component注解,这个注解表明该类会作为组件类,并告知Spring要为这个TaylorSwift类创建Bean。
我们不必显式配置Bean,因为该类使用了@Component注解,Spring会将事情处理妥当。

但是组件扫描默认是不开启的,想要让Spring命令去寻找带有@Component注解的类,必须先对Spring进行显式配置。
同样的,显式配置有两种方式,XML和基于Java。

2.3:在Java代码中定义Spring的装配规则启用组件扫描

@Configuration
@ComponentScan
public class CDPlayerConfig {
}

@ComponentScan注解能够在Spring中启用组件扫描,并且,@ComponentScan默认会扫描与配置类相同的包。因为CDPlayerConfig类位于soundsystem包,因此Spring将会扫描这个包以及这个包下的所有子包,查找带有@Component注解的类。这样就实现了组件扫描,就能发现TaylorSwift类,并且会在Spring中自动为其创建一个Bean。

2.4:使用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/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 http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <context:component-scan base-package="soundsystem" />

</beans>

在XML中,< context: component-scan>元素会有与@ComponentScan注解相对应的属性和子元素。

可是,真的扫描成功了吗?Bean到底创建成功了吗?接下来测试一下~
为了测试组件扫描的功能,创建一个简单的JUnit测试,它会创建Spring上下文,并判断TaylorSwift Bean是不是真的创建出来了。

2.5:测试组件扫描能够发现TaylorSwift

@RunWith(SpringJUnit4ClassRunner.class)
//@ContextConfiguration("classpath*:CDPlayer.xml") //使用XML启用组件扫描
@ContextConfiguration(classes = CDPlayerConfig.class) //使用基于Java配置启用组件扫描
public class CDPlayerTest {
    @Autowired
    private CompactDisc cd;

    @Test
    public void cdShouldNotBeNull() {
        //若程序运行没有出现异常, 说明断言正确
        assertNotNull(cd);
    }
}

CDPlayerTest使用了Spring的SpringJUnit4ClassRunner,以便在测试开始的时候自动创建Spring的应用上下文。注解@ContextConfiguration会告诉它需要在CDPlayerConfig或CDPlayer.xml中加载配置文件。因为配置文件中都包含了ComponentScan,所以最终的应用上下文中都包含TaylorSwift Bean。

下面我们会更加深入地探讨@ComponentScan和@Component,看看它们还有哪些作用。

2)为组件扫描的Bean命名

Spring应用上下文中所有的Bean都会给定一个ID,Spring应用上下文自动创建的Bean的ID为将该类的名字的第一个字母小写,也就是说,TaylorSwift的Bean的ID为taylorSwift。

我们有两种方法为Bean设置不同的ID:

  • 通过值传递给@Component注解
  • 使用Java依赖注入规范中提供的@Named注解
@Component("lovetaylor") //第一种
//@Named("lovetaylor")   //第二种
public class TaylorSwift implements CompactDisc {
    ...
}

但是我们更倾向于使用@Component,@Component更能表明它是做什么的。

3)设置组件扫描的基础包

上面我们说过,@ComponentScan默认会扫描配置类所在的包,那如果我们想扫描不同的包,扫描多个包该怎么办呢?
我们可以在@ComponentScan的value属性中指明包的名称

  • 一次指定一个包:
@Configuration
//@ComponentScan("soundsystem")
@ComponentScan(basePackages="soundsystem")
public class CDPlayerConfig{}
  • 同时指定多个包:
@Configuration
@ComponentScan(basePackages={"soundsystem","video"})
public class CDPlayerConfig{}

除了将包设置为简单的String类型之外,@ComponentScan还提供了另一种方法,那就是将其指定为包中所包含的类或接口,这些类所在的包将会作为组件扫描的基础包:

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

在我们的应哟呵那个程序中,很多对象会依赖其他的对象才能完成任务,所以就需要一种方法能够将组件扫描的Bean和它们的依赖装配在一起。
下面我们就来看看自动化装配的另一方面内容——自动装配。

4)通过为Bean添加注解实现自动装配

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

通过程序来解释自动装配@Autowired是怎么回事。
现在有了CD,要将CD放入播放器里面播放啦~
同样的,先抽象出媒体播放器的接口,然后再实现CD播放器。

2.6:媒体播放器接口

public interface MediaPlayer {
    public void play();
}

2.7:实现CD播放器

@Component
public class CDPlayer implements MediaPlayer {
    private CompactDisc cd;

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

    public void play() {
        cd.play();
    }
}

CDPlayer的构造方法处使用了@Autiwired注解,这表明Spring创建CDPlayer Bean的时候,会通过这个构造器来进行实例化并且会传入一个可设置给CompactDisc类型的Bean。

换种说法,就是Spring在初始化CDPlayer这个Bean的时候,发现其构造方法需要依赖CompactDisc Bean,所以Spring就会去满足这种依赖关系,也就是将CompactDisc这个Bean自动装配进来。

不管是构造器、set方法还是其他的方法,Spring都会尝试满足方法参数上所声明的依赖。
如果没有匹配的Bean,那么应用上下文创建的时候,Spring就会抛出一个异常。可以将@Autowired的required属性设置为false,但是需谨慎,因为这样如果没有匹配的Bean, Spring就会让当前Bean处于未装配的状态。
如果有多个Bean都能满足依赖关系的话,Spring将会抛出一个异常,表明没有明确指定要选择哪个Bean进行自动装配。

像@Component和@Named一样,@Inject(来源于Java依赖注入规范)也可以用来替换@Autowired(Spring特有)。

5)验证自动装配

现在,我们已经将自动化装配的两件事组件扫描和自动装配做好了,那接下来就来验证一下装配有没有成功。我们对之前的CDPlayerTest进行修改~

2.8:验证自动装配

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = CDPlayerConfig.class)
public class CDPlayerTest {

    @Rule
    public final StandardOutputStreamLog log = new StandardOutputStreamLog();

    @Autowired
    private MediaPlayer player;

    @Autowired
    private CompactDisc cd;

    @Test
    public void cdShouldNotBeNull() {
        assertNotNull(cd);
    }

    @Test
    public void play() {
        player.play();
        assertEquals("正在播放MeiMei的TaylorSwift", log.getLog());
    }
}

代码中使用了StandardOutputStreamLog(已过时),这是来源于System Rules库的一个JUnit规则,这个规则能够基于控制台的输出编写断言。在这里,我们断言TaylorSwift.play()方法的输出被发送到了控制台上 。

现在我们已经了解了自动化装配,再来看看Spring中如何显式地装配Bean~


通过Java代码装配Bean

通过Java代码装配Bean,就是通过编写JavaConfig进行配置,更简单地来讲,就是在JavaConfig中声明Bean。具有强大、类型安全并且对重构友好的特点。

我们说JavaConfig就是带有@Configuration注解的Java代码,但是它和其他Java代码又有什么区别呢?
在概念上,JavaConfig与应用程序中的业务逻辑和领域代码是不同的,JavaConfig是配置代码,不包含任何业务逻辑,也不侵入到业务逻辑代码之中。所以通常将其放到单独的包中,使它与其他的应用程序逻辑分离开。

接下来我们看一下如何通过JavaConfig显式配置Spring~

1)创建配置类

前面的例子中我们已经认识了JavaConfig

2.9:在CD播放器例子中出现过的JavaConfig

@Configuration
@ComponentScan
public class CDPlayerConfig {
}

这个JavaConfig是启用组件扫描的一个配置文件。@ComponentScan注解就是在Spring中启用组件扫描。

那么问题来了,我们说自动化装配是隐式配置,JavaConfig和XML是显式配置,那为什么JavaConfig还可以用到自动化装配里呢?
其实,自动装配Bean机制是由组件扫描和自动装配两部分组成, 组件扫描仍需要显式配置之后才能发挥它的作用。

现在理清了这些关系,那么我们来看一下通过Java代码来装配Bean的JavaConfig应该怎样写。

2.10:创建一个普通的JavaConfig

@Configuration
public class CDPlayerConfig {
}

创建好了,我们看一下如何使用JavaConfig装配CDPlayer和CompactDisc。

2)声明简单的Bean

要在JavaConfig中声明Bean,我们需要编写一个方法,这个方法会创建所需类型的实例,然后给这个方法添加@Bean注解。

2.11: 声明一个TaylorSwift类的Bean

@Bean
public CompactDisc TaylorSwift() {
    return new TaylorSwift();
}

@Bean注解会告诉Spring,这个方法会返回一个对象,该对象要注册为Spring应用上下文中的Bean。
Bean就是包装后的Object,那也就是说,Spring最终会将new TaylorSwift()产生的实例包裹起来,形成Bean。
也可以通过Bean的name属性为Bean命名。

2.12:给Bean重命名

@Bean(name="lovetaylor")
public CompactDisc TaylorSwift() {
    return new TaylorSwift();
}

我们发现Bean的声明是很简单的,但是我们可能希望充分使用Java的功能来发挥其功能。
比如,我们希望随机选择一个CD来播放~

2.13: 随机播放一盘CD

@Bean
public CompactDisc randomCD(){
    int choice = (int)Math.floor(Math.random() * 4);

    if(choice == 0){
        return new TaylorSwift();
    } else if(choice ==1) {
        return new bala1();
    } else if(choice ==2) {
        return new bala2();
    } else{
        return new bala();
    }
}

3)借助JavaConfig实现注入

在自动装配中,Spring帮我们来完成了Bean的注入,那么现在我们要通过Java代码来手动完成装配。
既然我们已经有了CD的Bean,现在要做的是播放CD,也就是要将CD注入到CDPlayer中。
最简单的方式就是引用创建Bean的方法:

2.14: 创建CDPlayer Bean时直接调用CD Bean

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

这个cdPlayer()和TaylorSwift()方法一样,同样使用了@Bean注解,表明这个方法会创建一个Bean实例并将其注册到Spring应用上下文中。但是在cdPlayer()方法体里并没有使用默认的构造器构建实例,而是调用了需要传入CompactDisc对象的构造器来创建CDPlayer实例。
所以看起来就像是在创建CDPlayer Bean的时候会调用创建TaylorSwift Bean的方法,但实际上并非如此,cdPlayer()方法调用TaylorSwift()这个方法的时候,直接返回TaylorSwift Bean, 而非每次调用都重新new一个TaylorSwift对象再交给Spring注册成一个Bean。
这也就是说,TaylorSwift Bean在Spring中只存在一份,如果在这个JavaConfig中还有其它Bean需要用到TaylorSwift Bean的时候,将与CDPlayer Bean共用一份TaylorSwift Bean实例,因为默认情况下,Spring中的Bean都是单例的。

可以看到,这种通过调用的方法来引用Bean的方式有点令人困惑。
那么还有一种理解起来更为简单的方式:

2.15: 自动匹配所需的Bean进行装配

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

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

这就是关于使用Java代码来装配Bean的方式。


通过XML装配Bean

由于XML装配Bean并不作为我们的首选方案,所以这部分内容只是用来帮助我们维护已有的XML配置。

1)创建XML配置规范

在使用XML为Spring装配Bean之前,我们需要创建一个新的配置规范,即创建一个XML文件,并且以< beans>元素为根。
我们还要在XML文件顶部声明多个XML模式文件(.xsd),这些文件定义了配置Spring的XML元素。

2.16:没有声明任何Bean的最简单的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/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 http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">

</beans>

这就是一个合法的SpringXML配置。不过,它还没有声明任何Bean。现在,我们要给予它生命力,这次我们用XML配置重新创建CD。

2)声明一个简单的< bean>

我们要使用spring-beans模式中的另外一个元素:< bean>,它类似于JavaConfig中的@Bean注解。
我们可以这样声明CompactDisc Bean:

<bean class="soundsystemXML.TaylorSwift" />

这个Bean的类通过class属性来指定,并且要使用全限定的类名。因为没有明确的指出ID,所以该Bean的ID将会是”soundsystemXML.TaylorSwift#0”。这个#0表示计数,如果我们也声明了不指定ID的另外一个TaylorSwift,那么它的ID将会是”soungsystemXML.TaylorSwift#1”。

但通常我们都会为它们指定ID,因为我们在装配Bean的时候会用到这个具体的名字,否则自动产生的名字也没有多大用处。

<bean id="taylor" class="soundsystemXML.TaylorSwift" />

那么当Spring发现这个< bean>元素时,它将会调用TaylorSwift的默认构造器来创建Bean。
与JavaConfig的方式比较,我们不再需要直接创建TaylorSwift实例,这样也显得更加被动,但是它也没有JavaConfig强大,不能通过任何我们可以想象到的方法来创建Bean实例,而且,如果突然改变了类名,那又将造成很多麻烦。

3)注入Bean(装配Bean)

我们可以回想一下,在JavaConfig中有两种注入Bean方式。那么在XML配置文件中也有两种注入Bean的方式:

  • 通过构造器注入Bean
  • 通过setter设置属性注入Bean
3.1)通过构造器注入Bean

什么叫通过构造器注入Bean呢? 前面讲过了,在XML中声明Bean时,使用全限定的类名来指定一个类,然后Spring调用这个类的默认构造方法new出一个对象,然后将其包裹成Bean。那么,如果这个类我们给其自定义了构造方法,我们就可以在XML中对其进行注入,完成Bean的初始化工作。
前面2.7的CD播放器的程序中,CDPlayer类的构造方法需要一个CompactDisc对象,如果我们要初始化这个Bean,就要完成CompactDisc Bean的注入。

在XML中,通过构造器注入Bean有两种配置方案:

  • < constructor-arg>元素
  • 使用Spring的c-命名空间
3.1.1)< constructor-arg>元素配置方式

2.17: < constructor-arg>元素配置方式注入初始化CDPlayer Bean

<bean id="cdPlayer" class="com.soundsystemXML.CDPlayer">
    <constructor-arg ref="taylor" />
</bean>

< constructor-arg>元素会告知Spring要将一个ID为taylor的Bean引用传递到CDPlayer的构造器中。
同样的,还有c-命名空间配置注入Bean。

3.1.2)c-命名空间配置方式

2.18: c-命名配置方式注入Bean

<bean id="cdPlayer1" class="com.soundsystemXML.CDPlayer"
        c:cd-ref="taylor" />

c-命名空间这种配置方式看起来有点怪异,我们来分析一下:

  • c:是c-命名空间的前缀
  • cd是构造器的参数名,也就是CDPlyer构造方法的参数列表里面的参数名
  • -ref是注入Bean引用,“taylor”是要注入的Bean的ID

关于c-命名空间,还有一些别的使用规则。比如,当构造器中需要多个Bean的时候,我们可以使用参数索引来表示参数列表里所需要的多个Bean。比如这样:

<bean id="cdPlayer2" class="com.soundsystemXML.CDPlayer"
          c:_0-ref="taylor" />
3.2)通过构造器注入常量

上面介绍的是通过构造器注入Bean,但是有的时候,构造器的参数并不是一个类变量,而是一个常量。那么就来看一下如何通过构造器注入常量~

假设现在有一个Taylor的CD如下:

2.19:TaylorDisc CD

public class TaylorDisc implements CompactDisc {

    private String title;
    private String artist;

    public TaylorDisc(String title, String artist){
        this.title = title;
        this.artist = artist;
    }

    public void play() {
        System.out.println("Playing " + title + " by " + artist);
    }
}

就这个类的构造器而言,它的两个参数都是常量,所以要通过XML将常量注入。和前面一样,将常量注入到构造器中,也有两种方式:

  • < constructor-arg>元素
  • 使用Spring的c-命名空间
3.2.1)< constructor-arg>元素配置方式

2.20:将常量注入到构造器中

<bean id="taylorDisc1" class="com.soundsystemXML.TaylorDisc" >
    <constructor-arg value="TSCD" />
    <constructor-arg value="MeiMei" />
</bean>

每种注入方式都是有多种书写格式的, 比如还可以像下面这样:

<bean id="taylorDisc1" class="com.soundsystemXML.TaylorDisc" >
    <constructor-arg index="0" value="TSCD" />
    <constructor-arg index="1" value="MeiMei" />
</bean>
3.2.2)c-命名空间配置方式

2.21:使用c-命名空间注入常量

<bean id="taylorDisc2" class="com.soundsystemXML.TaylorDisc"
          c:title="TSCD" c:artist="MeiMei" />

这种注入方式是通过构造器的参数列表的参数名称来指定,也可以像前面注入Bean那样,使用索引来指定要注入的常量。比如下面这样:

<bean id="bean id="taylorDisc2" class="com.soundsystemXML.TaylorDisc"
          c:_0="TSCD" c:_1="MeiMei" />
3.2.3)装配Bean时注入列表/集合/数组

需要特别说明的是,在装配Bean的时候,如果我们要注入的是一个数组/列表/集合呢?那么我们就需要用到< list>元素。

假设现在有一个五月天的新专辑类,我们要对这个CD进行一些更细致的处理,比如显示出播放这个CD里面的每一首歌:

2.22:五月天的新专辑

public class MayDayDisc implements CompactDisc {
    private String title;
    private String artist;
    private List<String> tracks;
//  private Set<String> tracks;
//  一般来说, List和Set的区别不是很大, 但是当Spring要装配的是集合的时候, 使用Set集合可以保证集合中的元素不会重复.

    public MayDayDisc(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);
        }
    }
}

那么这个时候,XML文件里应该这样配置:

 <bean id="mayDay1" class="com.soundsystemXML.MayDayDisc">
        <constructor-arg value="五月天的新专辑" />
        <constructor-arg value="五月天" />
        <constructor-arg>
            <list>
                <value>歌曲1</value>
                <value>歌曲2</value>
                <value>歌曲3</value>
                <!--等等...-->
            </list>
        </constructor-arg>
    </bean>

在上面的代码中提到了,在类里除了使用List,也可以使用Set,不过就是将相对应的XML配置文件中的< list>元素改为< set>就可以了。

同理,如果现在有一个电台,要播放若干CD,我们也可以将若干CD类使用< list>元素,注入到电台类中。

2.23:电台

public class Discography {
    private String artist;
    private List<CompactDisc> cds;

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

而其相对应的XML应该是这样:

 <bean id="discpgraphy" class="com.soundsystemXML.Discography">
        <constructor-arg index="0" value="今日流行"/>
        <constructor-arg index="1">
            <list>
                <ref bean="jay" />
                <ref bean="jayZhou" />
                <ref bean="mayDay" />
            </list>
        </constructor-arg>
    </bean>

以上就是关于通过构造器装配(注入)Bean的内容,下面我们来看一下怎样通过设置属性来装配(注入)Bean~

3.3)通过setter设置属性注入Bean

在Java中,除了通过构造方法给数据成员赋值以外,我们还可以通过setter方法给数据成员赋值。

与通过构造器注入Bean一样,通过设置属性注入Bean也有两种装配方式:

  • 通过< property>元素装配
  • 通过p-命名空间装配
3.3.1)通过< property>元素装配

我们现在对CDPlyer类进行修改,去掉它的自定义构造方法,加上所有数据成员的setter方法:

2.24:CDPlayer

public class CDPlayer implements MediaPlayer {
    private CompactDisc cd;

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

    public void play() {
        cd.play();
    }
}

如果现在要在XML中重新声明这个类,我们可能会这样声明:

 <bean id="cdPlayer" class = "com.soundsystemXML.CDPlayer" />

这样看起来是没有问题的,毕竟现在这个类没有自定义的构造方法了嘛,默认的构造方法也是不含参数的。可是这样真的对吗? 如果我们测试这个类,会发现它会抛出NullPoiterException异常,因为我们没有注入任何CompactDisc Bean,所以运行play()方法当然就出错了。

要通过设置属性注入Bean,这时候我们就要用到< property>元素了。元素为设置属性的setter方法所提供的功能和< constructor-arg>元素为构造器提供的功能是一样的。比如现在可以在XML中这样写:

<bean id="cdPlayer3" class = "com.soundsystemXML.CDPlayer">
    <!--这里的property的name属性要与setXXX()方法里面的XXX保持一致(XXX首字母应该小写)-->
    <property name="cd" ref="taylor"/>
</bean>
3.3.2)通过p-命名空间装配

2.25:通过p-命名空间来装配Bean

<bean id="cdPlayer4" class="com.soundsystemXML.CDPlayer"
      p:cd-ref="taylor">
</bean>

同样的,p-命名空间的参数也比较难理解。现在我们也来分析一下p:cd-ref=”taylor”里的各项参数:

  • p:是p-命名空间的前缀
  • cd是属性名(即类中的数据成员名)
  • -ref是注入Bean引用,告知Spring这是注入一个Bean而不是常量
  • “taylor”是所注入的Bean的ID
3.4)通过设置属性注入常量

有了前面通过构造器注入常量的例子,这里的通过设置属性注入常量应该也不难理解。
与前面一样,通过设置属性注入常量也有两种装配方式:

  • 通过< property>元素装配
  • 通过p-命名空间装配
3.4.1)通过< property>元素装配

我们先对五月天的新专辑类进行一些修改,去掉它的构造方法,加上所有数据成员的setter方法。

2.26:五月天的新专辑类

public class MayDayDisc implements CompactDisc {
    private String title;
    private String artist;
    private List<String> tracks;

    public void setTitle(String title){
        this.title = title;
    }

    public void setArtist(String artist){
        this.artist = artist;
    }

    public void 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);
        }
    }
}

在XML中应该这样配置:

    <bean id="mayDay3" class="com.soundsystemXML.MayDayDisc">
        <property name="title" value="五月天的新专辑" />
        <property name="artist" value="五月天" />
        <property name="tracks">
            <list>
                <value>歌曲1</value>
                <value>歌曲2</value>
                <value>歌曲3</value>
                <value>歌曲4</value>
                <!--等等...-->
            </list>
        </property>
    </bean>
3.4.2)通过p-命名空间装配

使用p-命名空间装配的时候还有一丢丢小麻烦,不过我们先把装配的代码贴出来,再对它进行解释:

<bean id="mayDay4" class="com.soundsystemXML.MayDayDisc"
          p:title="五月天的新专辑"
          p:artist="五月天">
        <property name="tracks">
            <list>
                <value>歌曲1</value>
                <value>歌曲2</value>
                <value>歌曲3</value>
                <value>歌曲4</value>
                <!--等等...-->
            </list>
        </property>
    </bean>

可以看出,再注入列表常量的时候,没有使用p-命名空间,而是使用了< property>元素。事实上,p-命名空间是不能装配集合的。
但是,我们仍然有简化书写的办法,就是使用util-命名空间。

我们可以使用util-命名空间的< util:list>元素来创建一个Bean,然后再通过p-命名空间的ref来引用这个Bean。具体代码如下:

<util:list id="trackList">
        <value>歌曲1</value>
        <value>歌曲2</value>
        <value>歌曲3</value>
        <value>歌曲4</value>
        <!--等等...-->
    </util:list>

    <bean id="mayDay5" class="com.soundsystemXML.MayDayDisc"
          p:title="五月天的新专辑"
          p:artist="五月天"
          p:tracks-ref="trackList">
    </bean>

< util:list>元素只是util-命名空间所提供的功能之一,它还有许多其它的元素,比如:

<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, 其中包含值或引用.
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值