Spring学习笔记(四) --- 装配Bean之通过XML装配

来源:互联网 发布:淘宝药品现在要怎么买 编辑:程序博客网 时间:2024/05/17 22:37

本系列博客为spring In Action 这本书的学习笔记

在之前的两篇博客里我们说完了自动装配和通过Java代码装配Bean, 这篇博文里我们将介绍最后一种装配Bean的方式 — 通过XML装配.


1. 创建一个XML配置文件

和上一篇通过Java装配Bean的博文里面一样, 我们先来看一下在自动装配中出现过的XML文件.

程序1: 在CD播放器里面出现过的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="com.SoundSystem_Auto" /></beans>

这个程序1与上一篇通过Java代码装配Bean的博客里面的那个程序1都取自Spring学习笔记(二): 装配Bean之自动化装配那篇博客里. 其意义都是通过显式配置启用组件扫描.
<context:component-scan base-package=”com.SoundSystem_Auto” />这句代码等同于JavaConfig里面的@ComponentScan, 意为在Spring中启用组件扫描.

这是在前面出现过的XML配置文件, 那么我们现在来看一下一个普通的, 没有进行任何配置的XML配置文件是什么样的.

程序2: 创建一个普通的规范的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">  <!--Configuration details go here-->  <!--在这里写配置的细节--></beans>

这就是一个最简单的XML配置文件, 我们可以看出, 它跟同样功能的JavaConfig相比, 复杂了很多. 在JavaConfig中只需要一个@Congfiguration标注就可以声明一个Java配置文件, 但是在XML配置文件中, 需要在顶部声明多个XML模式(即XSD文件), 这些文件定义了配置Spring的XML元素.
用来装配Bean的最基本的XML元素包含在spring-beans模式之中, 在上面这个XML文件中, 它被命名为根空间. <beans>是该模式中的一个元素, 它是所有Spring配置的根元素.

2. 声明一个简单的Bean

上面介绍了怎样创建一个规范的XML配置文件, 那么现在就来看看怎样在XML配置文件中声明Bean.

要在XML中声明一个Bean, 要用到spring-beans模式中的另一个元素<bean>. <bean>元素类似于JavaConfig中的@Bean注解.

现在我们想要声明一个CompactDisc Bean, 我们可以在XML文件的<beans>标签下这样写:

程序3: 在XML中通过<bean>声明一个Bean

<bean class = "com.SoundSystem_XML.Jay" />

这样就声明了一个简单的Bean, 创佳那这个Bean是通过class属性的全限定类名来指定的. 在这个Bean中, 并没有给出明确的Bean的ID, 所以Spring将会根据全限定的类名来给这个Bean命名, 比如这个Bean的ID为:”com.SoundSystem_XML#0”. 如果使用这个全限定类名再声明一个Bean, 那么, 它的ID将为:”com.SoundSystem_XML#1”.

但是这样虽然在创建Bean的时候很方便, 但是在使用这个Bean的时候又会回很麻烦, 所以一般情况下, 我们将采用给Bean自定义ID, 比如下面这样:

程序4: 给Bean自定义ID

<bean id="jay" class = "com.SoundSystem_XML.Jay" />

这样在稍后将这个Bean装配到CDPlayer Bean中的时候, 我们就可以直接使用这个具体的名字.

现在已经知道了如何在XML配置文件中声明一个Bean, 那么, 相比于JavaConfig, Spring到底是如何通过XML配置文件来创建一个Bean的.

在JavaConfig中, 我们需要编写带有@Beam标注的方法, 这些方法回返回一个new的对象, 然后Spring将这个对象包装成Bean.
在XML中, 我们使用全限定的类名来为Spring创建Bean指定路径. 也就是说, Spring会根据这个全限定的类名找到这个类, 并调用该类的默认构造器来创建Bean.

看起来XML好像更省事一些, 但是它并没有JavaConfig那么强大, JavaConfig可以使用任何你能用Java代码实现的方式来创建一个Bean.
而且, 通过XML的全限定类名来创建Bean, 如果突然改变了类名, 那又将造成很多麻烦.

3. 注入(装配)Bean

我们可以回想一下, 在JavaConfig中有两种注入Bean方式, 那么在XML配置文件中也有两种注入Bean的方式. 分别是通过构造器注入Bean通过setter设置属性所注入Bean.

(1) 通过构造器注入Bean

什么叫通过构造器注入Bean呢? 前面讲过了, 在XML中声明Bean时, 使用全限定的类名来指定一个类, 然后Spring调用这个类的默认构造方法new出一个对象, 然后将其包裹成Bean.
那么, 如果这个类我们给其自定义了构造方法, 我们就可以在XML中对其进行注入, 完成Bean的初始化工作. 可能你还有些不明白, 先看一个例子吧.

仍然是CD播放器的例子, 先给出一个CD播放器的类.

程序5: CD播放器的类

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

可以看到, 在CDPlayer类的构造方法需要一个CompactDisc对象, 如果我们要初始化这个Bean, 就要完成CompactDisc Bean的注入.

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

  • <constructor-arg>元素
  • 使用Spring的c-命名空间

我们先来看一下<constructor-arg>元素配置方式

1. <constructor-arg>元素配置方式

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

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

<constructor-arg>元素会告知Spring要将一个ID为jay的Bean引用传递到CDPlayer的构造器中.

同样的, 还有c-命名空间配置注入Bean

2. c-命名空间配置方式

程序7: c-命名配置方式注入Bean

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

c-命名空间这种[配置方式看起来有点怪异, 我们来分析一下c:cd-ref=”jay”这句代码里面的各个参数.

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

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

<bean id="cdPlayer2" class="com.SoundSystem_XML.CDPlayer"          c:_0-ref="jay"/>

(2) 通过构造器注入常量

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

假设现在有一个周杰伦的CD如下:

程序8: JayZhou CD

public class JayZhou implements CompactDisc {    private String title;    private String artist;    public JayZhou(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-命名空间

1. <constructor-arg>元素配置方式

程序9: 将常量注入到构造器中

<bean id="jayZhou1" class="com.SoundSystem_XML.JayZhou" >    <constructor-arg value="魔杰座" />    <constructor-arg value="周杰伦" /></bean>

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

<bean id="jayZhou" class="com.SoundSystem_XML.JayZhou" >    <constructor-arg index="0" value="魔杰座"/>    <constructor-arg index="1" value="周杰伦"/></bean>

究竟使用哪种, 全凭个人喜好.

2. c-命名空间配置方式

用c-命名空间配置方式可以像下面这样注入:

程序10: 使用c-命名空间注入

<bean id="jayZhou2" class="com.SoundSystem_XML.JayZhou"          c:title="魔杰座" c:artist="周杰伦" />

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

<bean id="jayZhou3" class="com.SoundSystem_XML.JayZhou"          c:_0="魔杰座" c:_1="周杰伦" />

3. 装配Bean时注入列表/集合/数组

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

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

程序11: 五月天的新专辑

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.SoundSystem_XML.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>元素, 注入到电台类中.
程序12: 电台

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.SoundSystem_XML.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) 通过设置属性注入Bean

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

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

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

1. 通过<property>元素装配

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

程序13: CDPLyer

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.SoundSystem_XML.CDPlayer" />

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

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

<bean id="cdPlayer3" class="com.SoundSystem_XML.CDPlayer">    <!--这里的property的name属性要与setXXX()方法里面的XXX保持一致(XXX首字母应该小写)-->    <property name="cd" ref="jay"/></bean>

2. 通过p-命名空间装配

程序14: 通过p-命名空间来装配Bean

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

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

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

(4) 通过设置属性注入常量

有了前面通过构造器注入常量的例子, 这里的通过设置属性注入常量应该也不难理解.

与前面一样, 通过设置属性注入常量也有两种装配方式:

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

1. 通过<property>元素装配

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

程序15: 五月天的新专辑类

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.SoundSystem_XML.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>

2. 通过p-命名空间装配

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

<bean id="mayDay4" class="com.SoundSystem_XML.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.SoundSystem_XML.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 0
原创粉丝点击