您的位置:首页 > 编程语言 > Java开发

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

2008-12-26 15:04 579 查看
第2章 基本Bean装配

本章内容:[/b]
² 介绍Spring[/b]容器[/b]
² 声明Bean[/b]
² 构造函数注入和setter[/b]注入[/b]
² 装配Bean[/b]
² 控制Bean[/b]的创建和销毁[/b]
你是否曾经在电影结束后留下来观看致谢名单?难以置信,一个画面是由那么多人员齐心协力***出来的。
现在想象一下如果这些人相互不交流,你最喜爱的电影会是什么样子?他们都在工作,但相互没有合作。导演保持沉默,不说“开机”,摄影师不会开始摄影。这或许并不要紧,因为女主角还在她的保姆车内,而由于还没有雇用灯光人员,所以没有灯光。大多数电影(总之是优秀的)都是由上百人一起工作完成的,他们有着共同的目标――***一部流行佳作。
在这方面,大部分软件也没什么区别。任何一个成功的应用软件都是由很多为了实现同一商业目标而相互协作的组件组成的。这些组件必须相互知道对方,并且相互交流来完成工作。
但是,正如我们在第1章中看到的,传统建立应用系统对象之间关联关系的方法会导致复杂的代码,使它们很难被复用,很难做单元测试。最好的情况是,这些组件做了某些不该自己做的工作。最坏的情况是,它们被紧紧地耦合在一起,致使它们很难被复用,很难被测试。
在Spring中,组件无需自己负责与其他组件的关联。取而代之的是,容器负责把协作组件的引用给予各个组件。
创建系统组件之间协作关系的这个动作是DI的关键,通常被称之为装配。在本章中,我们将介绍使用Spring装配Bean的基本知识。由于DI是Spring中最基本的要素,因此在你开发的基于Spring的应用程序中几乎都需要这些技术。

2.1 容纳你的Bean
在基于Spring的应用中,应用组件在Spring范围内是激活的。如图2-1所示,容器可以创建组件,装配和配置组件,以及管理它们的整个生命周期(从new到finalize())。
在2.2节,我们将介绍如何配置Spring,了解哪些组件必须创建,配置和装配在一起。首先,要知道容器是组件存活的地方。理解容器有助于掌握组件管理的方式。
容器是Spring框架的核心。Spring容器使用DI管理所有组成应用系统的组件。这包括在协作组件之间建立关联。而且,这些对象简洁易懂,支持重用,容易被测试。
不是只有一种Spring容器。Spring提供了多种容器实现,并分为两类。Bean工厂(由org.springframework.beans.factory.BeanFactory接口定义)是最简单的容器,提供了基础的依赖注入支持。应用上下文(由org.springframework.context.ApplicationContext接口定义)建立在Bean工厂基础之上,提供了系统构架服务,如:从属性文件中读取文本信息,向有关的事件监听器发布事件。
注意:尽管在提到系统组件的时候,Spring无区别地使用“bean”和“JavaBean”这两个单词,这不是说Spring组件一定要严格遵循JavaBean规范。Spring组件可以是任意类型的POJO(简单Java对象)。在本书中,叫做松散JavaBean定义,和POJO是一个意思。
2.1.1BeanFactory介绍
正如其名字所暗示的,Bean工厂采用了工厂设计模式。就是说,这个类负责创建和分发Bean。但是,不像其他工厂模式的实现,它们只是分发一种类型的对象,而Bean工厂是一个通用的工厂,可以创建和分发各种类型的Bean。
但是,除了简单的实例化和分发应用对象以外,Bean工厂还有很多工作需要做。由于Bean工厂知道应用系统中的很多对象,所以它可以在实例化这些对象的时候,创建协作对象间的关联关系。这样就把配置的负担从Bean自身以及Bean的调用者中脱离出来。结果,Bean工厂分发出来的Bean都已经配置好了。此外,Bean工厂还要参与到Bean的生命周期中,调用用户定义的初始化和销毁方法(如果定义了这些方法的话)。
在Spring中有几种BeanFactory的实现。其中最常使用的是org.springframework.beans.factory.xml.XmlBeanFactory,需要传递一个org.springframework.core.io.Resource实例给构造函数。此Resource对象提供XML文件给工厂。Spring提供了有用的Resource实现。如表2.1所示。
表2.1XmlBeanFactory[/b]可以使用任意一种Resource[/b]来实现,并允许Spring[/b]配置多个源的细节[/b]

org.springframework.core.io.ByteArrayResource
定义内容由一组字节给定的资源
org.springframework.core.io.ClassPahResource定义可从classpath提取的资源
org.springframework.core.io.DescriptiveResource定义包含资源描述符但是实际没有可读资源的资源
org.springframework.core.io.FileSystemResource定义可以从文件系统提取的资源
org.springframework.core.io.InputStreamResource定义可从输入流提取的资源
org.springframework.core.web.portlet.context.PortletContextResource定义可用在portlet上下文中的资源
org.springframework.core.web.context.support.ServletContextResource定义可用在servlet上下文中的资源
org.springframework.core.io.UrlResource定义可从给定URL提取的资源
例如,下面的代码片段使用一个FileSystemresource来创建一个XmlBeanFactory,其Bean的定义来自文件系统中的XML文件:
BeanFactory factory=new XmlBeanFactory(new FileSystemResource(“c:/beans.xml”));
Bean是被延迟载入到Bean工厂中的,就是说Bean工厂会立即把Bean定义信息载入进来,但Bean只有在被需要的时候才被实例化。
为了从BeanFactory得到一个Bean,只要简单地调用getBean()方法,把你需要的Bean的名字当作参数传递进去就行了。
MyBean myBean=(MyBean)factory.getBean(“myBean”);
当getBean()方法被调用时,工厂就会实例化Bean并且依赖注入Bean的属性。就这样,一个Bean的生命开始了。2.1.3节我们将介绍Bean的生命周期,下一节我们先看看其他的Spring容器――应用上下文(ApplicationContext)
2.1.2使用应用上下文
表面上,ApplicationContext和BeanFactory差不多。两者都是载入Bean信息,装配Bean,根据需要分发Bean。但是ApplicationContext提供了更多功能:
n 应用上下文提供了文本信息解析工具,包括对国际化(I18N)的支持。
n 应用上下文提供了载入文件资源的通用方法,如载入图片。
n 应用上下文可以向注册为监听器的Bean发送事件。
由于它提供的附加功能,几乎所有的应用系统都选择ApplicationContext而不是BeanFactory。只有在资源很少的情况下,才会考虑采用BeanFactory。如在移动设备上。在本书中我们将使用ApplicationContext。
在ApplicationContext的诸多实现中,有三个实现经常用到:
n ClassPathXmlApplicationContext――从类路径中的XML文件载入上下文定义信息,把上下文定义文件当成类路径资源。
n FileSystemXmlApplicationContext――从文件系统中的XML文件载入上下文定义信息。
n XmlWebApplicationContext――从Web系统中的XML文件载入上下文定义信息。
在第13章,当我们讨论基于Web的Spring应用系统的时候,我们将详细介绍XmlWebApplicationContext。现在,让我们只是简单地使用FileSystemXmlApplicationContext从文件系统中载入应用上下文,或使用ClassPathXmlApplicationContext从类路径中载入。
从文件系统或类路径载入应用上下文与将Bean载入BeanFactory相似。例如,下面代码显示了如何载入FileSystemXmlApplicationContext:
ApplicationContext context=new FileSystemXmlApplicationContext(“c:/foo.xml”);//使用绝对路
同样,你也可以使用ClassPathXmlApplicationContext从系统的类路径中载入应用上下文。
ApplicationContext context=new ClassPathXmlApplicationContext(“foo.xml”);//使用相对路径
使用FileSystemXmlApplicationContext和ClassPathXmlApplicationContext的区别是,FileSystemXmlApplicationContext只能在指定的路径寻找文件,而ClassPathXmlApplicationContext可以在整个类路径(WEB-INF/classes与lib下的jar文件)中寻找文件。
应用上下文与Bean工厂的另一个重要区别是,Bean工厂延迟加载所有的Bean,而应用上下文而会预载入所有的单实例Bean。
现在,你已经了解了配置Spring容器的基础知识,让我们详细了解一下你的Bean在容器中的存在情况。
2.1.3Bean的生命
在传统Java应用中,Bean的生命周期非常简单。Java的关键词new用来实例化Bean(或许它是非序列化的),这样就够用了。而Spring容器中Bean的生命周期要细致得多。
如图2.2所示,当一个典型的Bean被载入到BeanFactory容器时,它的生命周期也就开始了。
正如你所看到的,Bean工厂在一个Bean可以使用之前完成了很多步工作。表2.2详细解释每一步工作的内容。
表2.2 Bean[/b]生命周期中的步骤明细[/b]

步骤
说明
1.实例化Spring实例化Bean
2.设置属性Spring注入Bean的属性
3.设置Bean名称如果Bean实现了BeanNameAware接口,Spring传递Bean的ID给setBeanName()(默然说话:我的天。。。中文版漏掉了这一点,让我迷惑了整整一周。总算找到了英文版,得以补充上这一点)
4.设置BeanFactory如果Bean实现了BeanFactoryAware接口,Spring传递Bean工厂给setBeanFactory ()
5.预处理(在初始化之前)如果有多个BeanPostProcessors,Spring将调用postProcessBeforeInitialization()
6.初始化Bean如果Bean实现InitializingBean,其afterPropertiesSet()方法将被调用。如果Bean声明了自定义的初始化方法,那么将调用指定的初始化方法。
7.预处理(在初始化之后)如果有多个BeanPostProcessors,Spring将调用postProcessAtferInitialization()方法
8.Bean已经准备好此时Bean已经准备好,可以使用,并且将一直保留在Bean工厂中,直到不再需要它。
9.销毁Bean如果Bean实现了DisposableBean,将调用destroy()方法,如果Bean有自定义的销毁方法,将调用指定的方法。
Bean在Spring应用上下文中的生命周期与在Bean工厂中的生命周期只有一点不同,见图2.3。
在应用上下文中,Bean可以实现ApplicationContextAware接口,如果是这样,那么setApplicationContextAware()方法就会被调用。
(默然说话:大家可能也会迷惑,图在哪里?因为CSND这边传Word的图片有问题,无法上传,不过文字已经说得很清楚了,所以我其实也没有画这两个图,其实上下文和BeanFactory相比,上下文多一个步骤,就是在第4步之后还没有开始预处理,而是再检查Bean是否实现了ApplicationContextAware接口,如果实现了,就调用setApplicationCOntextAware()方法,这些在第3章还会详细讲到,大家不用着急。)
现在你已经了解了如何创建和装载Spring容器。下一节让我们一起来看看如何使用XML把Bean放到Spring容器里。

2.2 创建Bean
现在,我们欢迎你来到首届(当然,也极有可能是最后一届)每年一度的JavaBean天才竞赛大会。我们搜索了很多最好的JavaBean(其实,对不起,我只在我的IDE的工作区中找了一下),在后面几章中,将开始竞赛和判断。Spring编程者,这是你的Spring Idol(默然说话:真为难译者呀。)。
在这次竞赛中,我们需要一些表演者,他们被指定要实现Performer接口。
public interface Performer{
void perform() throws PerformanceException;
}
在Spring Idol天才竞赛中,我们将到遇到一些实现了Performer接口的参赛者,下面让我们见见第一位参赛,它将帮助我们说明Spring如何进行构造器注入。
2.2.1声明一个简单的Bean
不象你可能听说过的那些比赛,Spring Idol不提供歌手,实际上很多参赛者根本不会看谱,例如,这个Juggler。请看程序清单2.1:

程序清单2.1 Juggler Bean(魔术师 bean)
package com.speakermore.springinaction.ch02.idol;
public class Juggler implements Performer {
private int beanBags=3;
public Juggler(){};
public Juggler(int beanBags){
this.beanBags=beanBags;
}
public void perform() throws PerformanceException {
System.out.println("杂技:抛接"+beanBags+"个豆包");
}
}
请欢迎我们的第一位Juggler参赛者――duke登台亮相~~~~!下面是duke在Spring配置文件(spring-idol.xml)中的声明:

程序清单m-2.1 目前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"> <bean id="duke" class="com.speakermore.springinaction.ch02.idol.Juggler" />
</beans>
<bean>标记是Spring中最基本的配置单元。它告诉Spring为我们创建一个对象。这里创建的是一个叫duke的Bean。使用了最简单的<bean>声明。id定义了该Bean的名称,如果我们要使用它,就使用这个id向Spring的容器申请。同时,class指明了它的类名的完全限定名(默然说话:完全限定名?就是带包名的类名称,这样Spring就可以在类路径下面去找到它)
当Spring容器装载该Bean时,它将用默认的构造函数来实例化duke。也就是说,Spring将使用下面这句代码来创建duke(默然说话:嘿嘿,其实是骗大家的,实际情况比这个要复杂,Spring使用反射机制来完成对Bean对象的创建)。
new com.speakermore.springinaction.ch02.idol.Juggler();
为了让duke有机会表演,我们应该这样来装载Spring的应用上下文(IdolTest.java):

程序清单 m-2.2 目前IdolTest.java的内容
package com.speakermore.springinaction.ch02.idol;

import java.util.logging.Level;
import java.util.logging.Logger;
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 performer=(Performer)ctx.getBean("duke");
try {
performer.perform();
} catch (PerformanceException ex) {
ex.printStackTrace();
}
}
}
虽然这不是一个真正的竞赛,但是前面的代码给duke有机会来实践。当它运行时,这个代码将打印出:
杂技:抛接3个豆包
默认时,duke一次能变出3个豆包。但每一个Juggler都可以做到。如果duke想赢得比赛,他必须要能变出更多。让我们来试着把duke配置为一个冠军吧。
2.2.2通过构造函数注入
为了给评委留下印象,duke决定一次变出15个豆包,创个世界纪录。
(默然说话:在网上查了好半天,总算弄清楚beanbag是个什么游戏了,与其说是魔术,不如说是杂耍更合适,其实就是中国杂技里的抛接多个小球――做程序员真不容易――目前抛接小球的世界纪录是13个,所以,如果你能抛接15个小球,那你就打破世界纪录了!)
正如程序清单2.1中所列出的,Juggler类有两种构造方法:
n 使用默认的构造方法
n 使用带有参数int(表示豆包的数量)的构造方法
虽然2.2.1部分的声明是有效的,但是它使用了Juggler的默认构造函数,它只能使用默认值。如果要变成15个的话,那只能使用另外一个构造函数。下面就是XML配置的代码:

程序清单 m-2.3 pring-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"> <bean id="duke" class="com.speakermore.springinaction.ch02.idol.Juggler" >
<constructor-arg value="15" />
</bean>
</beans>
<constructor-arg>是在创建Bean时用来告诉Spring更多信息的元素。如果没有这个元素,那么就是用默认的构造函数,现在给出了一个,就会使用传一个参数的那个构造函数。
IdolTest.java的代码不用改动,执行之后,结果是:
杂技:抛接15个豆包
好象这样做并没有什么令人惊奇之处,其实关于duke,有些事情我们没有告诉你。他不仅是一个魔术师,而且他还会朗诵诗歌。边魔术边诵读李白的诗歌一定可以拿到冠军!(我们说过,这是一个不同寻常的竞赛)
通过构造函数注入对象[/b]
因为duke不仅是个魔术师,而且是个会吟诗的魔术师,所以,为了让他能吟诗,我们得写一个新的类型――会吟诗的魔术师(PoeticJuggler(程序清单2.2))――它更能展示duke的才华。

程序清单2.2 背诗的魔术师
package com.speakermore.springinaction.ch02.idol;

public class PoeticJuggler extends Juggler {
private Poem poem;
public PoeticJuggler(Poem poem){
this.poem=poem;
}

//导入一个Poem和豆包(beanbags)的数量
public PoeticJuggler(int beanBags,Poem poem){
super(beanBags);
this.poem=poem;
}

@Override
public void perform()throws PerformanceException{
super.perform();
System.out.println("同时朗诵......");
poem.recite();
}
}
这个新类型可做魔术师的任何事,同时还可以朗诵诗歌。朗诵诗歌的接口定义如下:

程序清单m-2.4 Poem.java目前的内容
package com.speakermore.springinaction.ch02.idol;
public interface Poem {
public void recite();
}
duke最喜欢的诗是李白的诗歌,还有流行诗歌(骑白马的不一定是王子,还可能是唐僧之类)。Mordern5(程序清单2.3)实现了接口Poem

程序清单2.3 能吟诵伟大诗歌的诗人
package com.speakermore.springinaction.ch02.idol;
public class Mordern5 implements Poem {
private static String[] LINES={
"天上白云朵朵白,疑是银河下九天",
"骑白马的不一定是王子,也可能是唐僧",
"今朝有酒今朝醉,不管他人门前雪",
"举头不明白,低头撕纸玩",
"钻石恒久远,一颗就破产",
"孩子是自己的好,老婆是自己和别人的都好",
"如果可以再来一次,我会对你说:你去死!",
"抢还是不抢(银行),这是一个问题",
"中国股市有奇迹,你不破产不理你!"
};
public Mordern5(){}
public void recite() {
for(int i=0;i<LINES.length;i++){
System.out.println(LINES[i]);
}
}

}
Mordern5可以使用下列XML来声明为一个Spring的bean。
<bean id="jialibai" class="com.speakermore.springinaction.ch02.idol.Mordern5" />
有了poem,我们就可以将poem赋给duke。哦,对了,duke现在应该是PoeticJuggler,所以我们需要修改一下他的bean声明了。
<bean id="duke" class="com.speakermore.springinaction.ch02.idol.PoeticJuggler" >
<constructor-arg value="15" />
<constructor-arg ref="jialibai" />
</bean>
正如在程序清单2.2中你所看到的,PoeticJuggler没有默认构造函数,我们只能使用带参构造函数实例化它,在这个例子中我们把一个int和一个Poem类型的参数传递给构造函数。duke的Bean声明 提供了15这个int给第一个参数,它使用了value属性。
但是我们不能使用value属性给第二个参数赋值,因为它是一个对象类型,而不是简单类型。我们使用了ref属性,来指定该值是一个ID叫“jialibai”的bean。当Spring遇到jialibai和duke时,它将执行如下Java代码(默然说话:同样,这里的代码仅仅用来说明逻辑,Spring实际并不是这样做的,它使用反射):
Poem jialibai=new Mordern5();
Performer duke=new PoeticJuggler(15,jialibai);
(默然说话:spring-idol.xml已经更新,下面给出更新后的内容)

程序清单 m-2.5 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"> <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" />
</beans>
现在执行IdolTest.java(它不用做任何更新,执行就可以了),你会看到如下输出:
杂技:抛接15个豆包
同时朗诵......
天上白云朵朵白,疑是银河下九天
骑白马的不一定是王子,也可能是唐僧
今朝有酒今朝醉,不管他人门前雪
举头不明白,低头撕纸玩
钻石恒久远,一颗就破产
孩子是自己的好,老婆是自己和别人的都好
如果可以再来一次,我会对你说:你去死!
抢还是不抢(银行),这是一个问题
中国股市有奇迹,你不破产不理你!
构造函数注入是保证Bean在使用之前被完整配置的最安全方法。但是它不能配置复杂的内容。好在Spring中构造函数并不是配置Bean的唯一方法,还可以选择setter注入方法。下一节,我们将介绍如何使用setter方法来配置Bean。
如何选择:构造函数还是setter方法?
在选择是使用构造函数还是setter方法注入这个问题上存在很大的争议,就像是选择奶酪还是选择花生酱一样。它们都既有优点也有缺点。该如何选择呢?
下面是赞同使用构造函数注入的理由:
就像我们前面说的,构造函数注入强制使用强依赖契约。简单地说,就是如果没有提供所有需要的依赖。一个Bean就是无法被实例化。在实例化后,类的实例是有效的,可以被使用了。当然这是假设Bean的构造函数的参数包括了所有的依赖。
由于Bean的依赖都通过它的构造函数设置了,所以没有必要再写多余的setter方法。这有助于减少代码。
因为只能通过构造函数设置类的属性,这样你有效地保证了属性的不可变性。
但是,同样也有很多反对构造函数注入的依据(也就是,支持setter注入)
如果Bean有很多依赖,那么构造函数的参数列表会很长。
如果一个对象已经有很多种构造方式,那么很难再提出不同的构造函数,因为构造函数只能通过参数的个数和类型来区分。
如果构造函数的参数中有两个以上是相同类型的,那么很难确定每个参数的用途。
构造函数注入不利于身身的继承。Bean的构造函数为了给父类的私有变量赋值,需要把参数传递给super()方法。
幸运的是,Spring没有强制你必须使用哪种注入方式。你可以使用任何一种依赖注入方式对Bean注入。事实上,你可以在同一上下文定义文件中自由地混合使用setter注入和构造函数注入,既使是同一个Bean也可以setter注入与构造函数注入混合使用。作为个人来说,在更多时候更倾向于使用setter注入方法,但是偶尔也使用构造函数注入方法。

2.3 注入Bean属性
通常Bean提供一对函数用来访问属性:setXXX()和getXXX()方法。Spring通过setter注入来配置属性值。
为了演示Spring的setter注入,我们请下一位表演者上场!Kenny是一个了不起的演奏家,由Instrumentalist类所定义,如程序清单2.4所定义:

程序清单2.4 一个天才演奏家
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();
}

//注入歌曲名
public void setSong(String song) {
this.song = song;
}

//注入乐器
public void setInstrument(Instrument instrument) {
this.instrument = instrument;
}
}
我们可以看到,在程序清单2.4中有两个属性:song和instrument。song表示要演奏的歌曲名,而instrument表示要演奏的乐器。它由Instrument接口声明:
package com.speakermore.springinaction.ch02.idol;
public interface Instrument {
public void play();
}
因为Instrumentalist类有默认的构造函数,所以Kenny可以在Spring里象下面这样声明为一个Bean。
<bean id=”kenny” class=” com.speakermore.springinaction.ch02.idol.Instrumentalist” />
尽管Spring可以初始化以上的Bean声明,让kenny成为一个Instrumentalist,但是如果没有曲名和乐器,Kenny根本无法演奏。下面让我们来看看,如何通过setter注入,给kenny歌曲名和乐器。
2.3.1注入简单的数值
Bean可以通过<property>标签来进行属性配置。<property>与<constructor-arg>非常类似。除了前者是通过属性的setter方法完成注入,而后者是通过构造函数来完成注入。
下面我们就使用setter注入给kenny一首歌曲名,下列XML完成了这个功能:
<bean id="kenny" class="com.speakermore.springinaction.ch02.idol.Instrumentalist" >
<property name="song" value="Jingle Bells" />
</bean>
一旦Spring实例化了kenny,Spring就会调用setter方法把<property>标签里设置的值注入到对象中。上面的XML说明了Spring将调用setSong()方法,将”Jingle Bells”传递给song。相同效果的Java代码如下:(默然说话:同样强调,Spring其实是使用反射完成这项任务)
Instrumentalist kenny=new Instrumentalist();
kenny.setSong(“Jingle Bells”);
通过Spring的setter注入一首歌名和通过Java来setter一首歌名最根本的区别就在于,如果在Spring中设置它,Instrumentalist就被解耦合了,这样,Instrumentalist并且被硬编码到某首特定的歌曲,如果需要更换歌曲,只需要重新配置Spring的XML就可以了。这样使代码获得了更高的灵活性。
在song这个例子中,<property>标签通过它的value属性注入了一个String类型的值,其实其他基本数据类型(int,float,java.lang.Double等等,还有boolean)的值也可以使用同样的方式注入。
例如,假设Instrumentalist类有一个age属性,类型为int,表示该演奏家的年龄,我们可以这样来设置kenny的年龄:
<bean id="kenny" class="com.speakermore.springinaction.ch02.idol.Instrumentalist" >
<property name="song" value="Jingle Bells" />
<property name=”age” value=”37” />
</bean>
Spring会根据属性的类型正确的决定value的类型。因此,age是一个整型,所以Spring会在调用setAge()之前自动把37转换成int类型。
是不是感觉很棒?DI绝对比硬编码属性值好得多!使用DI注入在程序需要相互合作的对象,可以使它们不再耦合在一起。下面我们将看看如何给kenny一个乐器以便让他可以进行演奏。
2.3.2使用其他的Bean
kenny是一个天才的演奏家,他可以演奏任何乐器,只要这个乐器实现了Instrument接口。当然,kenny有最喜欢的乐器――萨克斯,程序清单2.5定义一个Saxophone类:

程序清单2.5 一个实现了Instrument的Saxophone
package com.speakermore.springinaction.ch02.idol;
public class Saxophone implements Instrument {

public void play() {
System.out.println("我是萨克斯呀,伊丫伊得罗");
}

}
为了给kenny一个可以演奏的萨克斯,必须在Spring中声明一个<bean>,如下列XML所示:
<bean id="saxophone" class="com.speakermore.springinaction.ch02.idol.Saxophone" />
注意Saxophone类没有做setter注入,因为它没有需要注入的属性。
有了saxophone之后,可以让kenny演奏了,修改kenny bean,使用setter注入来设置instrument。
<bean id="kenny" class="com.speakermore.springinaction.ch02.idol.Instrumentalist" >
<property name="song" value="Jingle Bells" />
<property name="instrument" ref="saxophone" />
</bean>
现在Kenny已经被注入所有属性,并且可以开始演出。跟duke一样,我们可以来执行下列Java代码。
ApplicationContext ctx=
new ClassPathXmlApplicationContext("com/speakermore/springinaction/ch02/idol/spring-idol.xml");
Performer performer=(Performer)ctx.getBean("kenny");
performer.perform();
(默然说话:下面我们来说明一下目前spring-idol.xml与Idol.java的内容)

程序清单 m-2.6 srping-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" >[/b]
<property name="song" value="Jingle Bells" />[/b]
<property name="instrument" ref="saxophone" />[/b]
</bean>[/b]
<!--kenny的最爱:萨克斯-->
<bean id="saxophone" class="com.speakermore.springinaction.ch02.idol.Saxophone" />[/b]

</beans>


程序清单m-2.7 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");[/b]
try {
//performer1.perform();
performer2.perform();[/b]
} catch (PerformanceException ex) {
ex.printStackTrace();
}
}
}
以上代码的运行结果如下:
演奏Jingle Bells :
我是萨克斯呀,伊丫伊得罗
这个例子其实说明了一件重要的事情,如果你跟执行duke的代码做比较,你会发现,它们几乎没什么区别,唯一不同点就是getBean()方法的参数(Bean的ID)不一样。仅仅这一点改动,我们就获得了两个完全不一样的Performer:一个魔术师,一个演奏家。
这不是Spring的优点,而是面向接口编程的优势。通过一个Performer接口,我们可以使用任何的Performer对象,无论是诗人,还是弹琴的。这正是Spring强烈推荐使用接口编程的原因。接下来你还会看到接口与DI紧密合作,可以让代码更加松散。
就象前面提到的,kenny可以演奏任何实现Instrument接口的乐器。现在给他一个钢琴试试。

程序清单2.6 实现Instrument的钢琴
package com.speakermore.springinaction.ch02.idol;
public class Piano implements Instrument {

public void play() {
System.out.println("叮叮当,叮叮当,铃儿响叮当!");
}

}
Piano的Bean声明如下:
<bean id="piano" class=" com.speakermore.springinaction.ch02.idol.Piano" />
现在如果想让kenny演奏钢琴,只需要改一下kenny的配置就可以了:
<bean id="kenny" class="com.speakermore.springinaction.ch02.idol.Instrumentalist" >
<property name="song" value="Jingle Bells" />
<property name="instrument" ref="piano[/b]" />
</bean>
无需改动任何代码,现在kenny就已经可以演奏钢琴了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: