在本章中作者讲解了Spring的IoC概念,详细介绍了如何配置我们自己的应用系统,如何编写松耦合的组件。如何在XML中配置Bena。
这里提个建议,现在的Spring已经升级到2.0版本了,而本书(《Spring in Action》)出版时还是Spring1.X。2.0有了较大的变化建议新学Spring的人下载2.0版本的来学习,现在最新的版本已经是Rc3了。同时2.0版本的中文参考已经在Spring中文论坛有人组织翻译了。而且现在已经有预览版了,大家可以先参考一下。(Spring中文预览版:http://www.jactiongroup.net/reference2/html/)同时向无偿翻译Spring文档的这些高手们致敬。
一、容纳你的Bean
容器是Spring的核心。Spring使用IoC管理所有组成应用系统的组件。Spring有两种不同的容器:
l Bena工厂(BeanFactory):由org.springframework.beans.factory.BeanFactory接口定义。是最简单的容器,提供了基础的依赖注入支持。
l 应用上下文(ApplicationContext):由org.springframework.context.ApplicationContext接口定义。建立在Bean工厂之上提供了系统架构服务。
1.BeanFactory介绍
见名知意,BeanFactory采用了工厂模式(抽象工厂模式:http://blog.csdn.net/qutr/archive/2006/01/22/586034.aspx;工厂方法模式:http://blog.csdn.net/qutr/archive/2006/07/21/954070.aspx)。这个类专门负责创建类(对象)。有些书上说Spring的BeanFactory像一个超负荷运转的机器,因为除了简单的创建对象以外,BeanFactory可以在实例化这些对象的时候,创建协作对象间的关联关系。这样就把配置的负担从Bean自身以及Bean的调用者中脱离出来。更详细的可以查看BeanFactory的源代码。
在Spring中有几种BeanFactory的实现。其中最长用的是org.springframework.beans.factory.xml.XmlBeanFactory,它根据XML文件中的定义装载Bean。在XmlBeanFactory类中提供了两个构造函数,通常我们用的是XmlBeanFactory(Resource resource) throws BeansException这个。他一般这样定义:BeanFactory factory = new XmlBeanFactory(new FileSystemResource(“beans.xml”));其中传递的参数引用为:import org.springframework.core.io.FileSystemResource;(书中这里是不太正确的,因为在Spring2.0里已经不提供传递java.io.InputStream对象的构造函数了。翻译者非常的不负责任,应该在这里给出说明。)
从BeanFactory得到一个Bean只需调用getBean(“beanName”)方法,把你需要的Bean的名字当作参数传递进去就可以了。像这样:MyBean myBean = (MyBean)factory.getBean(“myBean”);当getBean()方法被调用的时候,工厂就会开始实例化Bean 并且使用依赖注入开始设置Bean的属性。
事实上BeanFactory接口提供有6种方法供客户代码调用:
(1)boolean containsBean(String): 如果BeanFactory包含符合给定名称的Bean定义或Bean实例,则返回true。
(2)Object getBean(String): 返回以给定名字注册的Bean实例。就是上面提到的最简单的一种。
(3)Object getBean(String, Class): 返回以给定名称注册的Bean,返回的Bean将被扔给(cast)给定的类。如果Bean不能被Cast,相应的异常(BeanNotOfRequiredTypeException)将被抛出。
(4)Class getType(String name): 返回给定名称的Bean的Class。如果没有相应于给定名称的Bean,则抛出NoSuchBeanDefinitionException异常。
(5)boolean isSingleton(String): 决定在给定名称时,Bean定义或Bean实例注册是否为单件模式,如果相应于给定名称的Bean不能被找到,则抛出NoSuchBeanDefinitionException异常。
(6)String[] getAliases(String): 如果在Bean的定义中有定义,则返回给定Bean名称的别名。
2.使用应用上下文(ApplicationContext)
使用ApplicationContext可以获得Sroing框架的强大功能。表面上ApplicationContext和BeanFactory差不多,但是ApplicationContext提供了更多的功能:
l ApplicationContext提供了文本信息解析工具,包括对国际化的支持。
l ApplicationContext提供了载入文件资源的通用方法,如载入图片。
l ApplicationContext可以向注册为监听器的Bean发送事件。
l ApplicationContext在Spring2.0里可能还加了其他功能。
有三种ApplicationContext的实现经常被用到:
l ClassPathXmlApplicationContext—从类路径中的XML文件载入上下文定义信息,把上下文定义文件当成类路径资源。(可以在整个类路径中寻找XML文件)
l FileSystemXmlApplicationContext—从文件系统中的XML文件载入上下文定义信息。(只能在指定的路径中寻找XML文件)
l XmlWebApplicationContext—从Web系统中的XMLwenjian 载入上下文定义信息。
其中FileSystemXmlApplicationContext和ClassPathXmlApplicationContext(他们都包含在org.springframework.context.support包下)的使用形式分别如下:
ApplicationContext context = new FileSystemXmlApplicationContext(“c:/foo.xml”);
ApplicationContext context2 = new ClassPathXmlApplicationContext(“foo.xml”)
应用上下文会在上下文启动后预载入所有的单实例Bean。确保当你需要的时候他们已经准备好了你的应用不需要等待他们被创建。
总之实例化一个Bean大概有三种方法:
第一种:
Resource resource = new FileSystemResource("beans.xml");
BeanFactory factory = new XmlBeanFactory(resource);
第二种:
ClassPathResource resource = new ClassPathResource("beans.xml");
BeanFactory factory = new XmlBeanFactory(resource);
第三种:
ApplicationContext context = new ClassPathXmlApplicationContext(new String[] {"applicationContext.xml", "applicationContext-part2.xml"});
BeanFactory factory = (BeanFactory) context;
ApplicathionContext的超类是BeanFactory,所以理所当然的这里的context可以是BeanFactory。
3.Bean的生命周期
在书中作者用图文并茂的方式解说了Bean在BeanFactory和ApplicationContext中的生命周期,从创建到销毁的全过程。他们二者之间略有不同,书上讲的比较详细,这里就不罗列了。
二、基本装配
在Spring容器内拼凑Bean叫做装配。装配Bean的时候,你是在告诉容器需要哪些Bean以及容器如何使用依赖注入将他们配合在一起。
1.使用XML装配
Bean装配在Spring中最常用的是XML文件。在前面提到过的XmlBeanFactory、ClassPathXmlApplicationContext、FileSystemXmlApplicationContext和XmlWebApplicationContext都支持用XML装配Bean。
Spring规定了自己的XML文件格式,根元素为
xml version="1.0" encoding="UTF-8"?>
DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean id = "foo"
class = "springinaction.foo">
bean>
<bean id = "bar"
class = "springinaction.bar">
bean>
beans>
在Spring中,对一个Bean的最基本的配置包括Bean的ID和他的类全称。在test.xml文件中其中
2.添加一个Bean
Spring中的Bean在缺省情况下是单实例模式(也叫单态模式,http://blog.csdn.net/qutr/archive/2006/02/28/611868.aspx)。如果想每次从容器中得到得到一个Bean的不同实例,需要将Bean定义为原型模式(http://blog.csdn.net/qutr/archive/2006/07/24/968297.aspx)。我们可以用Bean的singleton属性来设置,缺省情况为true。
<bean id = "foo"
class = "springinaction.foo"
singleton="false">
在Bean的定义中用init-method来初始化某些参数,用destroy-method来销毁对象。
<bean id = "foo"
class = "springinaction.foo"
singleton="false"
init-method="init"
destroy-method="destory">
Spring也提供了两个接口来实现相同的功能:InitializingBean和DisposableBean。
3.通过Set方法注入依赖。
写过JavaBean的人可能都熟悉get和set方法。Spring就是用Set注入来配置Bean的。
每个Bean(通常就是你定义的一个class)通常都会有一些简单的类型成员,如int或String等等。通过使用
<bean id = "bar" class = "springinaction.bar">
<property name="barName">
<value>coffeeBarvalue>
property>
bean>
如上所示,在我们的springinaction.bar类中定义了一个名为barName的String类型的成员,用<value>coffeeBarvalue>中的coffeeBar来给他设置值。如:setName(“barName”);如果定义了一个int类型的成员我们可以在
利用property丰富的属性我们可以为一个Bean引用其它Bean,通过实现;可以嵌入一个内部Bean,通常很少使用。可以装配各种集合,如java.util.List, java.util.Set, java.util.Map等等。可以设置properties和null值。
下面给出一个例子,这个例子同样来自官方文档。
<bean id="moreComplexObject" class="example.ComplexObject">
<property name="adminEmails">
<props>
<prop key="administrator">[email protected]prop>
<prop key="support">[email protected]prop>
<prop key="development">[email protected]prop>
props>
property>
<property name="someList">
<list>
<value>a list element followed by a referencevalue>
<ref bean="myDataSource" />
list>
property>
<property name="someMap">
<map>
<entry>
<key>
<value>yup an entryvalue>
key>
<value>just some stringvalue>
entry>
<entry>
<key>
<value>yup a refvalue>
key>
<ref bean="myDataSource" />
entry>
map>
property>
<property name="someSet">
<set>
<value>just some stringvalue>
<ref bean="myDataSource" />
set>
property>
bean>
一目了然,非常清楚就不多解释了。关于集合Spring2.0又添加了新的内容,如:你如果使用的是JDK1.5那么还可以使用Java的泛型来清晰的解析各种容器所包含的类型,请参看:http://static.springframework.org/spring/docs/2.0.x/reference/beans.html#beans-collection-elements。
4.通过构造函数注入依赖
Set注入是Srping所推荐的,但是Set注入的缺点是,他无法清晰的表示出哪些属性是必须的,哪些是可选的。而使用构造函数注入的优势是通过构造函数来强制以来关系,有了构造函数的约束不可能实现一个不完全或无法使用的Bean。
<bean id="coo" class="springinaction.coo">
<constructor-arg>
<value>coolvalue>
constructor-arg>
bean>
上面的例子通过构造函数传值来实例化一个coo对象。如coo co = new coo(“cool”);
如果有多个构造函数,那么可以设置<constructor-arg>的type或index来传入参数了如果类型都一样那么只能用index了,index的值是从0开始的。
Spring为我们提供了Set注入和构造函数注入,在两者的使用方式上个人有个人的理由和不同见解,在本书中作者给我们的建议大概是:看情况,那个合适用哪个(废话J)。但是,Spring的开发团队通常提倡人们使用setter注入。其实Spring一共给我提供了三种注入方式:接口注入和上面的两种注入。他们都有各自的优点,详细的说明见《Spring开发指南》。
下面再给出两个例子来说明一下Setter注入和constructor注入:
<bean id="exampleBean"
class="examples.ExampleBean">
<property name="beanOne">
<ref bean="anotherExampleBean"/>
property>
<property name="beanTwo" ref = "yetAnotherBean"/>—我这里是正确的,2.0的中文预览版这里写错了-->
<property name="integerProperty" value="1"/>
bean>
<bean id="anotherExampleBean" class="examples.AnotherBean"/>
<bean id="yetAnotherBean" class="examples.YetAnotherBean"/>
Spring提供了快捷的ref属性,来代替ref元素,但是使用ref属性你要小!
下面是一个类
publicclass ExampleBean
{
private AnotherBean beanOne;
private YetAnotherBean beanTwo;
privateinti;
publicvoid setBeanOne(AnotherBean beanOne) {
this.beanOne = beanOne;
}
publicvoid setBeanTwo(YetAnotherBean beanTwo) {
this.beanTwo = beanTwo;
}
publicvoid setIntegerProperty(int i) {
this.i = i;
}
}
上面是一个Bean(其实就是一个普通的不能在普通的Java类)和它相对应的XML的配置文件,是一个典型的setter注入。例子清晰易懂就不多说了。
<bean id="exampleBean"
class="examples.ExampleBean">
<constructor-arg>
<ref bean="anotherExampleBean"/>
constructor-arg>
<constructor-arg ref="yetAnotherBean"/>
<constructor-arg type="int" value="1"/>
bean>
<bean id="anotherExampleBean" class="examples.AnotherBean"/>
<bean id="yetAnotherBean" class="examples.YetAnotherBean"/>
下面是一个类
publicclass ExampleBean
{
private AnotherBean beanOne;
private YetAnotherBean beanTwo;
privateinti;
public ExampleBean(AnotherBean anotherBean, YetAnotherBean yetAnotherBean, int i)
{
this.beanOne = anotherBean;
this.beanTwo = yetAnotherBean;
this.i = i;
}
}
上面是一个Bean和它相对应的XML的配置文件,是一个典型的constructor注入。例子也比较清晰易懂这里也就不多说了。
三、自动装配
在前面讲到的都是手动显示装配Bean的方法,Spring还提供了自动装配Bean的方法。只要设置
l byName:在容器中寻找和需要自动装配的属性名相同的Bean(或ID)。如果没有找到这个属性就没有装配上。
l byType:在容器中寻找一个与自动装配的属性类型相同的Bean。如果没有找到这个属性就没有装配上。如果找到超过一个则抛出org.springframework.beans.factory.UnsatisfiedDependencyException异常。
l constructor:在容器中查找与需要自动装配的Bean的构造函数参数一致的一个或多个Bean。如果存在不确定Bean或构造函数容器会抛出org.springframework.beans.factory.UnsatisfiedDependencyException异常。
l autodetect:首先尝试使用constuctor来自动装配,然后使用byType方式。不确定性的处理与constructor方式和byType方式一样,都抛出org.springframework.beans.factory.UnsatisfiedDependencyException异常。
如下实例:
在前面的显示装配:
<bean id="courseService"
class="springinaction.chapter02.CourseServiceImpl">
<property name="courseDao">
<ref bean="courseDao"/>
property>
<property name="studentService">
<ref bean="studentService"/>
property>
bean>
下面是自动装配:
<bean id="courseService"
class="springinaction.chapter02.CourseServiceImpl"
autowire="byName"/>
在Spring对Bean自动装配的过程中很容易出现不确定性,这些不确定性会导致程序无法运行。那么在我们的实际应用中要避免出现装配的不确定性。避免装配的不确定性的最好的方法就是使用显示装配和自动装配的混合方法。对有二义性的Bena使用显示装配,对没有二义性的Bean使用自动装配。
在通常情况下我们会分门别类的吧Bena设置在多个XML文件中。另外一种方法是使用一个或多个的
上例中,三行import引入了必要的XML文件。两行bean定义了Bean。依照XML的Schema或DTD,被导入文件必须是完全有效的XMLBean定义文件,且包含上层的
四、使用Spring的特殊Bean和应用举例
在书中谈到的Spring的特殊Bean的使用方法,我个人觉得这时Spring的IoC的高级用法或者是边角用法,由于我也是新手初学这里就不多啰嗦了,等以后在实践中有了体会在补上,关于这方面的内容在Spring2.0的官方文档上都有提到,这份文档非常的详细,我个人觉得只要把这份文档好好看了其他的书甚至可以放在一边了,当然Rod写的书还是要好好看的。
关于应用举例,本书中在这章举的例子不是很全面,刚开了个头后面感觉就不说了。我本来想将我最近正做的一个实际应用中的例子放到这里,但是我的这个程序好像有点为了Spring而Spring所以让我改了又改,至今也没有出来所以也就不再这里显示了。等以后专门写一篇文章来讨论一下。
五、小结
Spring框架的核心是Spring容器。BeanFactory是最简单的容器,他提供了最基本的依赖注入和Bean装配服务。但是,毕竟Spring是一个轻量级框架,要想得到更多的高级框架服务时我们需要使用ApplicationContext容器,所以为了让你的应用程序看起来更高级更上档次尽量多的使用ApplicationContext,这也是本书作者所提倡的。
其实关于Spring的IoC的核心和大部分内容在上面的文字中已经提到了,说来说去表面上就那些东西了。而IoC(DI)的真正思想是要靠大量的实践去体会和领悟的。