Spring in Action 中文版(第二版)读书笔记(2-2)

来源:互联网 发布:python zip压缩文件夹 编辑:程序博客网 时间:2024/06/01 17:09

注入内部Bean

现在kenny已经可以演奏萨克斯、钢琴或其他实现了Instrument接口的乐器。只要saxophonepianoSpring中声明了,就可以被其他Bean共享。它们可以被注入到任意BeanInstrument属性。其实,在应用中共享Bean是非常普遍的事情。

但这样有个问题:kenny比较担心卫生方面的问题。他更喜欢自己拥有一件萨克斯。因此,我们将使用另一个Spring技术――内部Bean

Java开发者可能会很熟悉内部Bean――即在其他类的范围内所定义的类。下面的XMLkennySaxophone声明为一个内部Bean

<bean id="kenny” class=" com.speakermore.springinaction.ch02.idol.Instrumentalist">

         <property name="song" value="Jingle Bells" />

                   <property name="instrument">

                            <bean class=" com.speakermore.springinaction.ch02.idol.Saxophone" />

                   </property>

</bean>

声明一个<bean>来定义内部Bean,将其作为<property>的子集注入其中。在这个示例中,将创建SaxoPhone,并装配到kennyinstrument属性中。

内部Bean并不局限于setter注入,也可以在构造函数中装配内部bean

<bean id="duke" class=" com.speakermore.springinaction.ch02.idol.PoeticJuggler">

         <constructor-arg value="15" />

         <constructor-arg>

                   <bean class=" com.speakermore.springinaction.ch02.idol.Mordern5" />

         </constructor-arg>

</bean>

这里,Mordern5实例将以内部Bean的形式被创建,并作为参数发送到PoeticJuggler的构造函数中。

内部Bean没有id属性。虽然其中声明一个id还是不错的,但是没有必要这样做,因为从不会通过名称来引用内部Bean。使用内部Bean的最大缺点是:它们不能复用。内部Bean只用于注入,且不能被其他Bean所引用。

此外,内部Bean还会影响Spring上下文中的XML的可读性。

kenny的天份在于可以演奏各种乐器,然而,他却有一个限制,一次只能演奏一种乐器。下一个在Spring Idol竞赛场登台的hank可不是这样,他能同时演奏多个乐器。

       2.3.3装配集合

到目前为止,你已经学会如何使用Spring配置简单属性的值(使用value)以及如何引用其它的Bean(使用ref)。但valueref仅仅用于当你的Bean属性是单个的时候才起作用。如果你的属性值是一个集合呢?数组呢?

Spring提供了四个标签来帮助你处理这件事情,表2.3说明了这四个标签的作用。

2.3       Spring支持的集合类型

集合标签

用途

<list>

List类型填充值,值允许重复

<set>

Set类型填充值,如果值出现重复,只会取一个(它能保证装配完成后不出现重复的值)

<map>

以键-值对填充,键和值都可以是任意类型

<props>

以键-值对填充,键和值都只能是字符串

<list><set>对于数组类型或java.util.Connection类型的属性装配很有用。你将会看到,无论你使用<list>还是<set>,对于java.util.Connection是何种具体类型是没有影响的,其实它们完全可以互换。

至于<map><props>,它们对应于java.util.Mapjava.util.Properties。如果你需要使用“键-值”对集合,它们能派上用场。两个标记的区别在于:<map>的键和值可以是任意类型而<props>的键和值只能是String

为了说明在Spring中装配集合,下面有请hank上场!他就是所谓“一个人组成的乐队”,他的才能在于可以在同一时间演奏多种乐器。程序清单2.7定义了OneManBand

程序清单2.7  一个人组成的乐队

package com.speakermore.springinaction.ch02.idol;

 

import java.util.Collection;

public class OneManBand implements Performer {

    private Collection<Instrument> instruments;

    public OneManBand(){};

    public void perform() throws PerformanceException {

        for(Instrument instrument:instruments){

            instrument.play();

        }

    }

         //注入instrument集合

   public void setInstruments(Collection<Instrument> instruments) {

        this.instruments = instruments;

    }

}

你可以看到,当OneManBandperform方法被调用时,它迭代整个乐器集合。这里最重要的事情是,乐器集合是通过setInstruments()方法来注入的。让我们一起来看一下,Spring如何给它这个乐器集合。

Listsarrays

为给hand一个乐器集合,让我们来使用<list>做一下配置:

<bean id="hank" class="com.speakermore.springinaction.ch02.idol.OneManBand">

        <property name="instruments">

            <list>

                <ref bean="saxophone" />

                <ref bean="piano" />

            </list>

        </property>

</bean>

<list>标签可以包含一个或多个值。<ref>标签用来引用在Spring上下文中的bean(我们在做kenny的时候,就做了两个乐器:萨克斯和钢琴,这里都给他了),除了可以使用<ref>标签来给集合填充Bean引用以外,还可以使用<value><bean><null />来给集合填充简单数据值、内部Bean和空。实际上,我们还可以给<list>填充另一个<list>,以形成一个多维集合。

在程序清单2.7中,OneManBand类的instruments属性使用了Java 5的泛型,它限制java.util.Collection只能包含Instrument值的集合。而<list>其实可以用于任意java.util.Collection或数组实现的类型。也就是说,即使代码换成这样:

java.util.List<;Instrument> instruments;

或者这样:

Instrument[] instruments;

<list>都能正常工作。

Sets

<list>更关心的是Spring如何来处理这些集合,而不是这些集合的具体类型。所以可以使用<list>的地方,都可以换成<set>。而带来的好处就是:<set>可以保证元素不会重复。为了说明这种情况,这里有一个新的hank的配置,它使用<set>代替了<list>

<bean id="hank" class="com.speakermore.springinaction.ch02.idol.OneManBand">

        <property name="instruments">

            <set>

                <ref bean="saxophone" />

                                     <ref bean="saxophone" />

                <ref bean="piano" />

            </set>

        </property>

</bean>

虽然hank可以演奏多个乐器,但实际上他每次只能演奏一个萨克斯(你知道,hank只有一张嘴)。在这个例子中,hank有两个萨克斯。由于使用<set>,所以多余的一个萨克斯会被忽略。

其他方面<set><list>是一样的,可用于配置数组或java.util.Collection类型的属性。当然,也许用<set>配置java.util.List会显得有些古怪,但这样做也是没问题的,它可以保证List中的成员唯一。

(默然说话:下面再给出spring-idol.xmlIdolTest.java文件的代码:)

程序清单 m-2.8 spring-idol.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"

         xsi:schemaLocation="http://www.springframework.org/schema/beans

                   http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">

         <!--那个会念诗的魔术师duke-->

    <bean id="duke" class="com.speakermore.springinaction.ch02.idol.PoeticJuggler" >

        <constructor-arg value="15" />

        <constructor-arg ref="jialibai" />

    </bean>

    <!--魔术师念的诗-->

    <bean id="jialibai" class="com.speakermore.springinaction.ch02.idol.Mordern5" />

    <!--天才演奏员kenny-->

    <bean id="kenny" class="com.speakermore.springinaction.ch02.idol.Instrumentalist" >

        <property name="song" value="Jingle Bells" />

        <property name="instrument" ref="piano" />

    </bean>

    <!--kenny的最爱:萨克斯-->

    <bean id="saxophone" class="com.speakermore.springinaction.ch02.idol.Saxophone" />

    <!--另一件kenny要演奏的乐器:钢琴-->

    <bean id="piano" class="com.speakermore.springinaction.ch02.idol.Piano" />

    <!--一个人组成的乐队:hank-->

    <bean id="hank" class="com.speakermore.springinaction.ch02.idol.OneManBand">

        <property name="instruments">

            <set>

                <ref bean="saxophone" />

                <ref bean="saxophone" />

                <ref bean="piano" />

            </set>

        </property>

    </bean>

</beans>

 

程序清单m-2.9 IdolTest.java现在的内容

package com.speakermore.springinaction.ch02.idol;

 

import org.springframework.context.ApplicationContext;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class IdolTest {

    public static void main(String[] args) {

        ApplicationContext ctx=

                new ClassPathXmlApplicationContext("com/speakermore/springinaction/ch02/idol/spring-idol.xml");

        //Performer performer1=(Performer)ctx.getBean("duke");

       // Performer performer2=(Performer)ctx.getBean("kenny");

        Performer performer3=(Performer)ctx.getBean("hank");

        try {

            //performer1.perform();

            //performer2.perform();

            performer3.perform();

        } catch (PerformanceException ex) {

            ex.printStackTrace();

        }

    }

}

Maps

OneManBand执行时,每种乐器的声音在perform()方法中被迭代打印出来。现在假设我们还想知道某声音是由哪个乐器发出的呢?程序清单2.8的改动适应了我们新需求的变化。

程序清单2.8  instruments更改为Map

package com.speakermore.springinaction.ch02.idol;

import java.util.Map;

public class OneManBand implements Performer {

    private Map<String,Instrument> instruments;

 

    public OneManBand(){};

    public void perform() throws PerformanceException {

        for(String key:instruments.keySet()){

            //打印乐器的名称

            System.out.print(key+":");

            Instrument instrument=instruments.get(key);

            instrument.play();

        }

    }

 

    /**

     * @param instrutments Map的形式注入乐器集合

     */

    public void setInstruments(Map<String,Instrument> instruments) {

        this.instruments = instruments;

    }

}

在新版本的OneManBand中,instruments集合改成了一个java.util.Map,它的每一个成员都由一个String的键(乐器名)和一个Instrument的值(乐器对象)组成。因为Map的每个成员都由键-值对组成,所以简单的<list><set>都不能满足装配这个属性的需要了。

<bean id="hank" class="com.speakermore.springinaction.ch02.idol.OneManBand">

        <property name="instruments">

            <map>

                <entry key="萨克斯" value-ref="saxophone" />

                <entry key="钢琴" value-ref="piano" />

            </map>

        </property>

</bean>

<map>标记声明了一个Java.util.Map,每一个<entry>定义一个Map的成员。在上面这个示例中,<entry>key属性定义了Map成员的键(乐器名),而value-ref属性定义了Map成员的值(乐器对象),它是在Spring上下文中定义的bean的引用。

虽然我们使用了key来定义一个字符串的键,使用value-ref来定义一个引用值,其实<entry>各有两个子属性可以用来键和值。表2.4列出了这些属性。

2.4       一个<map>里的<entry>用来装置键和值,无论是键还是值均可以是原始数据类型,也可以是对象引用,这些属性可以帮助<entry>完成键和值的指定

属性

描述

key

指定一个String类型的键

key-ref

指定一个对象(Object)类型的键(应该是Spring上下文中的已声明bean)

value

指定一个String类型的值

value-ref

指定一个对象(Object)类型的值(应该是Spring上下文中的已声明bean)

<map>是键或值中存在Object类型的情况进行注入的唯一办法。如果你的键和值都是String类型的,我们有一个更简单的标记<props>来完成注入。我们一起来了解吧。

Properties

当把OnManBandistruments(乐器集合)属性声明为一个Map时,我们需要使用value-ref,因为每一个乐器对象都是Spring上下文中声明过的bean

为了完成演示,我们设想OnManBand成为一个口技演员,它可以模仿各种乐器发出的声音。程序清单2.9列出了新的OnManBand

程序清单2.9  使用Properties集合来装配OneManBandinstruments

package com.speakermore.springinaction.ch02.idol;

import java.util.Properties;

public class OneManBand implements Performer {

    private Properties instruments;

 

    public OneManBand(){};

    public void perform() throws PerformanceException {

        for(Object key:instruments.keySet()){

            //口技表演

            System.out.println(key+":"+instruments.getProperty((String)key));

        }

    }

 

    /**

     * @param instrutments Properties的形式注入乐器集合

     */

    public void setInstruments(Properties instruments) {

        this.instruments = instruments;

    }

 

}

我们使用<props>来完成乐器声音的装配:

<bean id="hank" class="com.speakermore.springinaction.ch02.idol.OneManBand">

        <property name="instruments">

            <props>

               <prop key="钢琴">叮叮当,叮叮当,铃儿响叮当!</prop>

               <prop key="萨克斯">我是萨克斯呀,伊丫伊得罗</prop>

               <prop key="小提琴">吱吱吱...吱吱...</prop>

               <prop key="">咣当叮</prop>

            </props>

        </property>

</bean>

<props>构建了java.util.Properties的成员。每一个<prop>就是一个键-值对,键由key属性指定,值则由<prop>的内容来指定。在我们这个例子里,键为<prop>指定了值为咣当叮

(默然说话:以上的修改仅对Spring-idol.xml,不需要修改IdolTest.java,只要修改了xml文件,就可以运行测试了。所以,我不再反复列出这两个文件的内容了。)

说到这里,我们得提一下在Spring配置中反复使用的几个标记,因为它们都使用术语“属性(property)”,所以要牢牢记住以下几点:

n  <property>是用于注入Bean属性的。

n  <props>是用于装配java.util.Property集合类型的。

n  <prop>是用于装配<props>集合中的一个键-值对的。

现在我们已经学会了好些办法用来装配Bean的属性,下面我们再来学点不同的东西:装配一个空。

       2.3.4装配空值

在绝大部分情况下,我们都会使用DI来为Bean的属性装配一个值或对象引用。可如果你真的想为一个属性装配一个null呢?很变态呀。。。为啥要装配null?不是所有的属性在装配之前就是一个null么?倒底是啥意思?

的确,在通常情况下,装配之前属性均为空,可有时写Bean的人很可能会为属性设置了默认值而不是null。而你又需要在装配时确认这个属性为空时,你应该怎么办?当然是显式设置其为null啦。

<property name=”someNonNullProperty”><null /></property>

另一个需要装配为null的原因就是重载自动装配。自动装配是啥?下一节我们开始介绍。

2.4    自动装配

到现在为止,你已经了解了如何使用<constructor-arg><property>来装配Bean,但是在大型应用中,这会导致过多的XML。与其显式装配所有的Bean,不如配置<bean>的一个autowire属性来自动完成整个装配过程。

       2.4.1四种自动装配类型

Spring提供了四种自动装配类型:

n  byName――尝试在容器中找那些name(id)名与属性名相同的Bean引用进行装配,如果找不到匹配的,就不进行装配(默然说话:我试过了,配置文件解析的时候不会出错,但在运行时由于对象为空,有可能会报空指针异常。)

n  byType――尝试在容器中寻找类型与属性类型相同的Bean引用进行装配,如果找不到,就不装配,如果找到了一个以上,就抛出org.springframework.beans.factory.UnsatisfiedDependencyException

n  constructor――寻找参数能匹配的构造函数,如果一个参数能对应多个Bean引用或者找到了一个以上可以匹配的构造函数,就抛出org.springframework.beans.factory.UnsatisfiedDependencyException

n  autodetect—首先尝试constructor,然后尝试byType。各自的处理方式同上。

使用byName自动装配

我们先查看一下2.3.2节中的kenny

<bean id="kenny" class="com.springinaction.springidol.Instrumentalist">

         <property name="song" value="Jingle Bells" />

         <property name="instrument" ref="saxophone" />

</bean>

这里,kenny的一个属性”instrument”被配置为”saxophone”bean,现在,我们假设这个beanid改为instrument

<bean id="instrument" class="com.speakermore.springinaction.ch02.idol.Saxophone" />

由于这个beanidkenny的属性”instrument”相同,Spring可以复用这个来自动配置kenny的乐器。只要如下设置autowire

<bean id="kenny" autowire="byName" class="com.speakermore.springinaction.ch02.idol.Instrumentalist" >

        <property name="song" value="Jingle Bells" />

</bean>

在设置autowire属性为byName时,就让Spring考虑kenny的所有属性,查找那些已声明的Bean,是否有与属性名匹配的Bean名,如果有,则使用setter进行注入。

使用byName时有一个缺陷,就是Bean名与Bean的属性名要一致。如果有多个相同类型的Bean声明存在,那么,它们的相同属性就只能注入同一个Bean了,也许在大多数情况下并没有什么问题,但我们还是需要留意这一点的。

使用byType自动装配

自动装配中的byType方式的原理与byName差不多,所不同的只是使用属性类型与Bean的类型进行匹配。

由于我们的Idol存在许多相同类型的乐器(它们都是Instrument类的对象),而在这种情况下,Spring是会抛出异常的,所以我们就不做演示了。

使用constructor自动装配

如果使用constructor自动装配,那你可以放弃<constructor-arg>标记,让Spring自己去帮助你完成参数匹配。

例如,duke可以这样来进行配置:

<bean  id="duke" autowire=”constructor” class="com.speakermore.springinaction.ch02.idol.PoeticJuggler"  />

在这个声明中,没有<constructor-arg>标记,autowire属性被设置为constructor,这告诉Spring在上下文中去寻找一些Bean,让它们可以来匹配duke的某个构造函数。我们已经在Spring中声明的” jialibai”恰好符合duke的一个构造函数的参数要求(它要求一个Poem),所以,Spring调用了这个构造函数(public PoeticJuggler(Poem poem))

按照构造函数来自动装配与按类型自动装配存在相同的问题,当存在多个可装配的bean声明,或存在多个符合要求的构造函数时,Spring都会抛异常。

使用autodetect自动装配

如果你想使用自动装配的功能,但又不知道该怎么设置autowire的值,那你就把它设置成autodetect。让Spring容器来帮你进行选择。例如:

<bean  id="duke" autowire=”autodetect” class="com.speakermore.springinaction.ch02.idol.PoeticJuggler"  />

当一个beanautowire属性被设置为autodetect时,Spring会首先尝试应用constructor,如果没有构造函数可以匹配,则Spring会尝试应用byType

默认自动装配

在默认情况下,如果你没有设置autowire,那bean是不会自动装配的。不过,你可以使用<beans>根标记的defaut-autowire属性来为所有的bean加上默认的自动装配设置。

<beans default-autowire=”byName”>

</beans>

象上面这样设置之后,所有的bean都会默认使用byName完成自动装配,除非有bean显式指定了autowire

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

对一个bean选择了自动装配,并不意味着你不能再使用显式方式装配这个bean的属性了。

其实前面的kenny就是这样的。让我们回顾一下:

<bean id="kenny" autowire="byName" class="com.speakermore.springinaction.ch02.idol.Instrumentalist" >

        <property name="song" value="Jingle Bells" />

</bean>

kenny有两个属性(歌曲名和乐器),而这一段代码我们使用<property>标记显式装配了一个字符串,乐器并没有显式装配,它使用了byName的自动装配。

混合使用自动和显式装配,可以有效解决在byTypebean不明确的问题。对于那些存在多个同类型bean声明的属性装配,我们可以使用显式装配来避免Spring抛出异常。

最后要注意的一个问题是:如果使用构造函数自动装配时,不能混合使用<constructor-arg>。只能让Spring完成所有参数的自动装配。

       2.4.3何时采用自动装配

尽管自动装配看上去很美,但是,它会带来的问题和它的美一样明显。

自动装配是一个功能强大的工具,但权力越大责任就越大。如果你选择自动装配,请多加小心。

(默然说话:这里我不详述它的缺点了,总之,我不太喜欢自动装配,所以,我也劝那些喜欢偷懒的初学者们:这是一个陷阱,掂一下自己的份量吧。)

2.5    控制Bean创建

至此,我们已经学会了最基本的bean配置方法。在本章结束之前,还有一些东西你需要知道。

n  控制一个指定的bean可以创建多少个实例,无论是整个应用只有一个实例(单例?!),还是一个用户请求分配一个实例,还是每次使用bean时都创建一个新的。

n  不使用构造函数,而是利用静态工厂来创建bean

n  在创建bean之后能执行一个初始化方法,在消毁后能执行一个清理方法。

       2.5.1Bean作用域

默认情况下,所有的bean都是单例的。也就是说,当容器分配一个bean(无论是依赖注入时用到的实例,还是通过调用容器的getBean()方法获得的实例),都的确是同一个bean。但很有可能你需要在每次请求时都获得一个和别人不同的bean实例。那你应该如何重写Spring的这个默认特点呢?

在你声明一个bean时,Spring提供了给你选择Bean作用域的scope选项。要强制Spring为每次请求都产生一个新实例,可以设置beanscope属性为prototype(原型)。如下:

<bean id="instrument" scope=”prototype” class="com.speakermore.springinaction.ch02.idol.Saxophone" />

现在,每个演奏家都可以获得自己的萨克斯了,kenny也不用再为卫生问题而发愁了。皆大欢喜。

除了prototypeSpring还提供了几个作用域选项,如表2.5所示。

大部分情况保留singleton(单例)也许是比较好的选择,但是,如果你使用Spring做为工厂来产生域对象(domain object)实例时,prototype会非常有用。

2.5       Springscope让你可以为每个bean声明它们如何创建,不用在bean中进行硬编码

scope

作用

singleton

定义bean的作用域为单例(整个容器只有一个实例,默认)

prototype

允许bean被多次创建(用一次创建一次)

request

定义bean的作用域是HttpRequest,只有在使用有Web能力的Spring上下文(Spring MVC)时才有效

session

定义bean的作用域是HttpSession,只有在使用有Web能力的Spring上下文(Spring MVC)时才有效

global-session

定义bean的作用域是全局HttpSession,只有在portlet上下文中才有效

注意:scopeSpring2.0新增加的功能。在Spring2.0之前,可以设置<bean>singletonfalse,就指定它是一个prototype(原型) beansingleton只能指定truefalse,这样有很大的局限性,且不允许有其他的作用域设置。所以2.0增加了scope属性,这个属性是不向后兼容的,如果在定义上下文时使用了Spring2.0 DTDXML方案,则必须使用scope属性。但是,如果仍然使用Spring1.x DTD,则必须使用singleton属性。

聪明的读者一定已经发觉Spring的单例的概念不同于我们平常所说的单例模式,平常所说的单例模式规定了每个类加载器仅产生一个实例,而Spring的单例却是保证在应用上下文(ApplicationContext)中对于每一个<bean>声明仅产生一个实例,它并不禁止你通过其他一些通常的做法(比如在Java中直接new)来产生实例,也没有禁止你相同的类型只能声明一个<bean>

       2.5.2利用工厂方法来创建Bean

在大多数时候,你用Spring配置的Bean都是通过某个公有的构造函数来完成的,你自己写的类,当然可以都声明公有的构造函数。可是,当你使用某些第三方的API时,它们却不一定提供公共的构造函数,而是通过静态方法来创建实例的,这时应该怎么办呢?难道就不能在Spring中配置为Bean了?

为了说明这种情况,我们假设有一个单例类(这里的单例不是Spring的那个单例,而是设计模式里的单例。),它只允许你通过静态工厂方法创建一个实例。程序清单2.10Stage就是这样一个单例类的例子。

程序清单2.10 一个单例的舞台(Stage)

package com.speakermore.springinaction.ch02.idol;

public class Stage {

    private Stage(){}

 

    private static class StageSingletonHolder{

        //使用内部类的静态变量,简单实现一个实例

        //(使用static关键字保证只有一个实例)

        static Stage instance = new Stage();

    }

 

    public static Stage getInstance() {

        //静态工厂方法,返回实例

        return StageSingletonHolder.instance;

    }

}

Spring Idol竞赛需要我们确保有且只有一个舞台供演出者进行他们的表演,Stage使用了单例设计模式来确保绝对没有办法来创建一个以上的Stage对象。

但是,你应该注意到了:Stage的构造函数是私有的,每次你需要Stage对象时,你只能通过静态的getInstance()方法来获得同一个对象。那我们应该如何在Spring里不使用公有构造函数创建一个Bean

幸运的是,Spring<bean>标记提供了一个factory-method属性让你可以配置一个静态方法以取代构造函数的调用来创建一个类的实例。为了将Stage配置到Spring上下文中,我们使用以下简单的写法:

<bean id="theStage" class="com.speakermore.springinaction.ch02.idol.Stage" factory-method="getInstance" />

我们演示了如何将一个单例通过静态方法配置为Spring的一个bean,其实这个方法适用于任何需要使用静态方法产生实例的地方。在第4章我们还将看到关于factory-method的更多内容。

       2.5.3初始化和销毁Bean

当一个Bean被实例化时,可能我们需要执行一些初始化以使它成为可用状态。同样的,当Bean不再被使用,并被移除容器时,一些例行公事的清理工作可能也是必要的。为了完成这些设置与清除的工作,Spring提供了相关支持。

要完成Bean的设置与清除方法的调用,只需要简单的声明init-method/destroy-method属性即可。init-method属性指定一个在bean实例化完成之后立即调用的方法,与此相似,destroy-method属性指定一个在bean即将移除容器之前被调用的方法。

为了演示,我们对Instrumentalist进行扩展,假设每个登台表演的人在表演前都需要调整乐器,那么,让我们来为Instrumentalist添加一个tuneInstrument()方法。

public void tuneInstrument() {

        instrument.tune();

}

同样,在表演结束之后,需要把乐器收起来。

public void cleanInstrument() {

        instrument.clean();

}

好了,现在我们所需要做的,就是在Instrumentalist被创建后调用tuneInstrument()方法,在Instrumentalist被销毁前调用cleanInstrument()方法。让我们用kenny bean的声明试试:

<bean id="kenny" class=" com.speakermore.springinaction.ch02.idol.Instrumentalist"

         init-method="tuneInstrument"

         destroy-method="cleanInstrument">

                   <property name="song" value="Jingle Bells" />

                   <property name="instrument" ref="saxophone" />

</bean>

当象这样进行声明之后,kenny可以在表演前为它的萨克斯调音,并在表演结束之前为萨克斯打好包。

(默然说话:下面列出目前为止,相关类和配置文件的内容)

程序清单 m-2.10 相关类和配置文件的内容

Instrumentalist.java

package com.speakermore.springinaction.ch02.idol;

 

public class Instrumentalist implements Performer {

 

    private String song;

    private Instrument instrument;

 

    public Instrumentalist() {

 

    }

 

    public void perform() throws PerformanceException {

        System.out.println("演奏" + song + " : ");

        instrument.play();

    }

 

    /**

     * @param song 注入歌曲

     */

    public void setSong(String song) {

        this.song = song;

    }

 

    /**

     * @param instrument 注入乐器

     */

    public void setInstrument(Instrument instrument) {

        this.instrument = instrument;

    }

    /**

     * 调音方法

     */

    public void tuneInstrument() {

        instrument.tune();

    }

    /**

     * 收乐器方法

     */

    public void cleanInstrument() {

        instrument.clean();

    }

}

 

Instrument.java

package com.speakermore.springinaction.ch02.idol;

public interface Instrument {

 

    public void clean();

 

    public void play();

 

    public void tune();

 

}

 

Piano.java

package com.speakermore.springinaction.ch02.idol;

 

public class Piano implements Instrument {

 

    public void play() {

        System.out.println("叮叮当,叮叮当,铃儿响叮当!");

    }

 

    public void tune() {

        System.out.println("钢琴调音中,请稍等...");

    }

 

    public void clean() {

        System.out.println("钢琴正在打包中,请稍候...");

    }

}

 

Saxophone.java

package com.speakermore.springinaction.ch02.idol;

 

public class Saxophone implements Instrument {

 

    public void play() {

        System.out.println("我是萨克斯呀,伊丫伊得罗");

    }

 

    public void tune() {

        System.out.println("萨克斯调音中,请稍等...");

    }

 

    public void clean() {

        System.out.println("萨克斯正在打包中,请稍候...");

    }

 

}

 

spring-idol.xml

<?xml version="1.0" encoding="UTF-8"?>

 

<beans default-autowire="byName" 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-2.0.xsd">

         <!--那个会念诗的魔术师duke-->

    <bean  id="duke" autowire="constructor" class="com.speakermore.springinaction.ch02.idol.PoeticJuggler" >

    </bean>

    <!--魔术师念的诗-->

    <bean id="jialibai" class="com.speakermore.springinaction.ch02.idol.Mordern5" />

    <!--天才演奏员kenny-->

    <bean id="kenny"

                   init-method="tuneInstrument"

                   destroy-method="cleanInstrument"

                   class="com.speakermore.springinaction.ch02.idol.Instrumentalist" >

        <property name="song" value="Jingle Bells" />

    </bean>

    <!--kenny的最爱:萨克斯-->

    <bean id="instrument" scope="prototype" class="com.speakermore.springinaction.ch02.idol.Saxophone" />

    <!--另一件kenny要演奏的乐器:钢琴-->

    <bean id="piano" class="com.speakermore.springinaction.ch02.idol.Piano" />

    <!--一个人组成的乐队:hank-->

    <bean id="hank" class="com.speakermore.springinaction.ch02.idol.OneManBand">

        <property name="instruments">

            <props>

                <prop key="钢琴">叮叮当,叮叮当,铃儿响叮当!</prop>

                <prop key="萨克斯">我是萨克斯呀,伊丫伊得罗</prop>

                <prop key="小提琴">吱吱吱...吱吱...</prop>

                <prop key="">咣当叮</prop>

            </props>

        </property>

    </bean>

 

    <bean id="theStage" class="com.speakermore.springinaction.ch02.idol.Stage" factory-method="getInstance" />

</beans>

 

IdolTest.java

package com.speakermore.springinaction.ch02.idol;

 

import org.springframework.context.ApplicationContext;

import org.springframework.context.support.ClassPathXmlApplicationContext;

 

public class IdolTest {

    public static void main(String[] args) {

        ApplicationContext ctx=

                new ClassPathXmlApplicationContext("com/speakermore/springinaction/ch02/idol/spring-idol.xml");

        Performer performer1=(Performer)ctx.getBean("duke");

        Performer performer2=(Performer)ctx.getBean("kenny");

        Performer performer3=(Performer)ctx.getBean("hank");

        try {

            System.out.println("======第一个参赛者:Duke!!=================");

            performer1.perform();

            System.out.println("======第二个参赛者:Kenny!!=================");

            performer2.perform();

            System.out.println("======第三个参赛者:Hank!!=================");

            performer3.perform();

        } catch (PerformanceException ex) {

            ex.printStackTrace();

        }

    }

}

默认的 init-method destroy-method

如果在上下文定义文件中有很多bean都使用相同名称的初始化方法和销毁方法,你不需要一一在各个bean标签中声明init-methoddestroy-method,只需要在<beans>标签中直接使用default-init-methoddefault-destroy-method属性就可以了。象这样:

<?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-2.0.xsd"

default-init-method="tuneInstrument"

default-destroy-method="cleanInstrument">

</beans>

这样我们就可以让Spring上下文中的所有bean在实例化时调用tuneInstrument方法,而在销毁前调用cleanstrument方法(如果bean没有这些方法,则什么也不会发生)

InitializingBeanDisposableBean

另一个可以实现初始化和清除的办法是,我们可以重写Instrumentalist,让它实现Spring特定的接口InitializingBeanDisposableBeanSpring容器会在bean生命周期中处理实现了InitializingBeanDisposableBean接口的这些bean。程序清单2.11列出实现了这两个接口的Instrumentalist类。

程序清单2.11 实现Spring生命周期接口的一个Instrumentalist版本

package com.speakermore.springinaction.ch02.idol;

 

import org.springframework.beans.factory.DisposableBean;

import org.springframework.beans.factory.InitializingBean;

public class Instrumentalist implements Performer,InitializingBean, DisposableBean {

 

    private String song;

    private Instrument instrument;

 

    public Instrumentalist() {

 

    }

 

    public void perform() throws PerformanceException {

        System.out.println("演奏" + song + " : ");

        instrument.play();

    }

 

    /**

     * @param song 注入歌曲

     */

    public void setSong(String song) {

        this.song = song;

    }

 

    /**

     * @param instrument 注入乐器

     */

    public void setInstrument(Instrument instrument) {

        this.instrument = instrument;

    }

  

    //重写InitializingBean的方法,初始化

    public void afterPropertiesSet() throws Exception {

         instrument.tune();

    }

 

    //重写DisposableBean的方法,销毁

    public void destroy() throws Exception {

        instrument.clean();

    }

}

InitializingBeanafterPropertiesSet方法会在bean的所有属性均设置完毕之后被调用,同样,DisposableBeandestroy方法会在容器销毁一个bean之前被调用。最好的一点是:如果使用接口,你不需要在配置文件里做任何声明,但缺点也很明显:这样一来,你的类就再也离不开Spring了。所以,除非你所开发的项目指定必须使用Spring容器,否则我还是推荐你使用spring配置文件来定义初始化方法和销毁方法,

2.6  小结

Spring框架的核心是Spring容器。Spring为容器提供了多种实现,这些实现可以归纳为两类。BeanFactory是最简单的容器实现,提供了DIBean装配服务。然而,当需要更高级的服务时,ApplicationContext将是不二之选。

本章中,我们介绍了如何将BeanSpring容器装配在一起。通常,在Spring容器中使用XML来执行装配。这个XML文件包含了帮助容器执行DI的信息,以便Bean能够与其所依赖的其他Bean相关联。

此外,我们还介绍了如何让Spring自动装配Bean,方法是建立反射机制并猜测哪些Bean会相互关联。

本章中所介绍的所有内容都是下面章节的基础,在以后开发基于Spring的应用程序时都会用到这些知识。在本书的其他章节中,可以继续使用本章样例中所使用的Spring Bean定义XML文件。

本章介绍的技术对于所有Spring应用都是通用的,而下一章介绍的内容不是很么简单。在下一章中,我们将介绍更高级的Spring配置,功能更为强大的、更为有用的技术。

原创粉丝点击