Beans, BeanFactory和ApplicationContext
程序员文章站
2022-07-13 17:42:25
...
在Spring中,两个最基本最重要的包是 org.springframework.beans 和 org.springframework.context. 这两个包中的代码为Spring的反向控制 特性(也叫作依赖注射)提供了基础。 BeanFactory提供了一种先进的配置机制来管理任何种类bean(对象),这种配置机制考虑到任何一种可能的存储方式。 ApplicationContext建立在BeanFactory之上,并增加了其他的功能,比如更容易同Spring AOP特性整合, 消息资源处理(用于国际化),事件传递,以声明的方式创建ApplicationContext, 可选的父上下文和与应用层相关的上下文(比如WebApplicationContext),以及其他方面的增强。
简而言之,BeanFactory提供了配置框架和基本的功能, 而 ApplicationContext为 它增加了更强的功能,这些功能中的一些或许更加接近J2EE并且围绕企业级应用。一般来说,ApplicationContext是 BeanFactory的完全超集, 任何BeanFactory功能和行为的描述也同样被认为适用于ApplicationContext
用户有时不能确定BeanFactory和ApplicationContext中哪一个在特定场合下更适合。 通常大部分在J2EE环境的应用中,最好选择使用ApplicationContext, 因为它不仅提供了BeanFactory所有的特性以及它自己附加的特性,而且还提供以声明的方式使用一些功能, 这通常是令人满意的。BeanFactory主要是在非常关注内存使用的情况下 (比如在一个每kb都要计算的applet中)使用,而且你也不需要用到ApplicationContext的所有特性。
这一章粗略地分为两部分,第一部分包括对BeanFactory和ApplicationContext都适用的一些基本原则。第二部分包括仅仅适用于ApplicationContext的一些特性
3.2. BeanFactory 和 BeanDefinitions - 基础
3.2.1. BeanFactory
BeanFactory实际上是实例化,配置和管理众多bean的容器。 这些bean通常会彼此合作,因而它们之间会产生依赖。 BeanFactory使用的配置数据可以反映这些依赖关系中 (一些依赖可能不像配置数据一样可见,而是在运行期作为bean之间程序交互的函数)。
一个BeanFactory可以用接口org.springframework.beans.factory.BeanFactory表示, 这个接口有多个实现。 最常使用的的简单的eanFactory实现是org.springframework.beans.factory.xml.XmlBeanFactory。 (这里提醒一下:ApplicationContext是BeanFactory的子类, 所以大多数的用户更喜欢使用ApplicationContext的XML形式)。
虽然大多数情况下,几乎所有被BeanFactory管理的用户代码都不需要知道BeanFactory, 但是BeanFactory还是以某种方式实例化。可以使用下面的代码实例化BeanFactory:
InputStream is = new FileInputStream("beans.xml");
XmlBeanFactory factory = new XmlBeanFactory(is);或者
ClassPathResource res = new ClassPathResource("beans.xml");
XmlBeanFactory factory = new XmlBeanFactory(res);或者
ClassPathXmlApplicationContext appContext = new ClassPathXmlApplicationContext(
new String[] {"applicationContext.xml", "applicationContext-part2.xml"});
// of course, an ApplicationContext is just a BeanFactory
BeanFactory factory = (BeanFactory) appContext;很多情况下,用户代码不需要实例化BeanFactory, 因为Spring框架代码会做这件事。例如,web层提供支持代码,在J2EE web应用启动过程中自动载入一个Spring ApplicationContext。这个声明过程在这里描述:
编程操作BeanFactory将会在后面提到,下面部分将集中描述BeanFactory的配置.
一个最基本的BeanFactory配置由一个或多个它所管理的Bean定义组成。在一个XmlBeanFactory中,根节点beans中包含一个或多个bean元素。
<?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="..." class="...">
...
</bean>
<bean id="..." class="...">
...
</bean>
...
</beans>3.2.2. BeanDefinition
一个XmlBeanFactory中的Bean定义包括的内容有:
classname:这通常是bean的真正的实现类。但是如果一个bean使用一个静态工厂方法所创建而不是被普通的构造函数创建,那么这实际上就是工厂类的classname
bean行为配置元素:它声明这个bean在容器的行为方式(比如prototype或singleton,自动装配模式,依赖检查模式,初始化和析构方法)
构造函数的参数和新创建bean需要的属性:举一个例子,一个管理连接池的bean使用的连接数目(即可以指定为一个属性,也可以作为一个构造函数参数),或者池的大小限制
和这个bean工作相关的其他bean:比如它的合作者(同样可以作为属性或者构造函数的参数)。这个也被叫做依赖。
上面列出的概念直接转化为组成bean定义的一组元素。这些元素在下面的表格中列出,它们每一个都有更详细的说明的链接。
表 3.1. Bean定义的解释
特性 详细说明
class 第 3.2.3 节 “bean的类”
id和name 第 3.2.4 节 “Bean的标志符 (id与name)”
singleton或prototype 第 3.2.5 节 “Singleton的使用与否”
构造函数参数 第 3.3.1 节 “设置bean的属性和合作者”
bean的属性 第 3.3.1 节 “设置bean的属性和合作者”
自动装配模式 第 3.3.5 节 “自动装配协作对象”
依赖检查模式 第 3.3.6 节 “依赖检查”
初始化模式 第 3.4.1 节 “生命周期接口”
析构方法 第 3.4.1 节 “生命周期接口”
注意bean定义可以表示为真正的接口org.springframework.beans.factory.config.BeanDefinition以及它的各种子接口和实现。然而,绝大多数的用户代码不需要与BeanDefination直接接触。
3.2.3. bean的类
class属性通常是强制性的(参考第 3.2.3.3 节 “通过实例工厂方法创建bean”和第 3.5 节 “子bean定义”),有两种用法。在绝大多数情况下,BeanFactory直接调用bean的构造函数来"new"一个bean(相当于调用new的Java代码),class属性指定了需要创建的bean的类。 在比较少的情况下,BeanFactory调用某个类的静态的工厂方法来创建bean, class属性指定了实际包含静态工厂方法的那个类。 (至于静态工厂方法返回的bean的类型是同一个类还是完全不同的另一个类,这并不重要)。
3.2.3.1. 通过构造函数创建bean
当 使用构造函数创建bean时,所有普通的类都可以被Spring使用并且和Spring兼容。 这就是说,被创建的类不需要实现任何特定的接口或者按照特定的样式进行编写。仅仅指定bean的类就足够了。 然而,根据bean使用的IoC类型,你可能需要一个默认的(空的)构造函数。
另外,BeanFactory并不局限于管理真正的JavaBean,它也能管理任何你 想让它管理的类。虽然很多使用Spring的人喜欢在BeanFactory中用真正的JavaBean (仅包含一个默认的(无参数的)构造函数,在属性后面定义相对应的setter和getter方法),但是在你的BeanFactory中也可以使用特殊 的非bean样式的类。 举例来说,如果你需要使用一个遗留下来的完全没有遵守JavaBean规范的连接池, 不要担心,Spring同样能够管理它。
使用XmlBeanFactory你可以像下面这样定义你的bean class:
<bean id="exampleBean"
class="examples.ExampleBean"/>
<bean name="anotherExample"
class="examples.ExampleBeanTwo"/> 至于为构造函数提供(可选的)参数,以及对象实例创建后设置实例属性,将会在后面叙述
3.2.3.2. 通过静态工厂方法创建Bean
当你定义一个使用静态工厂方法创建的bean,同时使用class属性指定包含静态工厂方法的类,这个时候需要factory-method属性来指定工厂方法名。Spring调用这个方法(包含一组可选的参数)并返回一个有效的对象,之后这个对象就完全和构造方法创建的对象一样。用户可以使用这样的bean定义在遗留代码中调用静态工厂。
下面是一个bean定义的例子,声明这个bean要通过factory-method指定的方法创建。注意这个bean定义并没有指定返回对象的类型,只指定包含工厂方法的类。在这个例子中,createInstance 必须是static方法 .
<bean id="exampleBean"
class="examples.ExampleBean2"
factory-method="createInstance"/>至于为工厂方法提供(可选的)参数,以及对象实例被工厂方法创建后设置实例属性,将会在后面叙述.
3.2.3.3. 通过实例工厂方法创建bean
使用一个实例工厂方法(非静态的)创建bean和使用静态工厂方法非常类似,调用一个已存在的bean(这个bean应该是工厂类型)的工厂方法来创建新的bean。
使用这种机制,class属性必须为空,而且factory-bean属性必须指定一个bean的名字,这个bean一定要在当前的bean工厂或者父bean工厂中,并包含工厂方法。 而工厂方法本身仍然要通过factory-method属性设置。
下面是一个例子:
<!-- The factory bean, which contains a method called
createInstance -->
<bean id="myFactoryBean"
class="...">
...
</bean>
<!-- The bean to be created via the factory bean -->
<bean id="exampleBean"
factory-bean="myFactoryBean"
factory-method="createInstance"/>虽然我们要在后面讨论设置bean的属性,但是这个方法意味着工厂bean本身能够被容器通过依赖注射来管理和配置
3.2.4. Bean的标志符 (id与name)
每一个bean都有一个或多个id(也叫作标志符,或名字;这些名词说的是一回事)。这些id在管理bean的BeanFactory或ApplicationContext中必须是唯一的。 一个bean差不多总是只有一个id,但是如果一个bean有超过一个的id,那么另外的那些本质上可以认为是别名。
在 一个XmlBeanFactory中(包括ApplicationContext的形式), 你可以用id或者name属性来指定bean的id(s),并且在这两个或其中一个属性中至少指定一个id。 id属性允许你指定一个id,并且它在XML DTD(定义文档)中作为一个真正的XML元素的ID属性被标记, 所以XML解析器能够在其他元素指回向它的时候做一些额外的校验。正因如此,用id属性指定bean的id是一个比较好的方式。 然而,XML规范严格限定了在XML ID中合法的字符。通常这并不是真正限制你, 但是如果你有必要使用这些字符(在ID中的非法字符),或者你想给bean增加其他的别名, 那么你可以通过name属性指定一个或多个id(用逗号,或者分号;分隔)。
3.2.5. Singleton的使用与否
Beans被定义为两种部署模式中的一种:singleton或non-singleton。 (后一种也别叫作prototype,尽管这个名词用的不精确因为它并不是非常适合)。 如果一个bean是singleton形态的,那么就只有一个共享的实例存在, 所有和这个bean定义的id符合的bean请求都会返回这个唯一的、特定的实例。
如果bean以non-singleton,prototype模式部署的话,对这个bean的每次请求都会创建一个新的bean实例。这对于例如每个user需要一个独立的user对象这样的情况是非常理想的。
Beans 默认被部署为singleton模式,除非你指定。要记住把部署模式变为non-singletion(prototype)后,每一次对这个bean的 请求都会导致一个新创建的bean,而这可能并不是你真正想要的。所以仅仅在绝对需要的时候才把模式改成prototype。
在下面这个例子中,两个bean一个被定义为singleton,而另一个被定义为non-singleton(prototype)。客户端每次向BeanFactory请求都会创建新的exampleBean,而AnotherExample仅仅被创建一次;在每次对它请求都会返回这个实例的引用。
<bean id="exampleBean"
class="examples.ExampleBean" singleton="false"/>
<bean name="yetAnotherExample"
class="examples.ExampleBeanTwo" singleton="true"/>注 意:当部署一个bean为prototype模式,这个bean的生命周期就会有稍许改变。 通过定义,Spring无法管理一个non-singleton/prototype bean的整个生命周期, 因为当它创建之后,它被交给客户端而且容器根本不再跟踪它了。当说起non-singleton/prototype bean的时候, 你可以把Spring的角色想象成“new”操作符的替代品。从那之后的任何生命周期方面的事情都由客户端来处理 。BeanFactory中bean的生命周期将会在 第 3.4.1 节 “生命周期接口”一节中有更详细的叙述。
3.3. 属性,合作者,自动装配和依赖检查
3.3.1. 设置bean的属性和合作者
反向控制通常与依赖注入同时提及。基本的规则是bean通过以下方式来定义它们的依赖(比如它们与之合作的其他对象):构造函数的参数,工厂方法的参数;当对象实例被构造出来或从一个工厂方法返回后设置在这个实例上的属性。容器的工作就是创建完bean之后,真正地注入这些依赖。这完全是和一般控制方式相反的(因此称为反向控制),比如bean实例化,或者直接使用构造函数定位依赖关系,或者类似Service Locator模式的东西。我们不会详细阐述依赖注射的优点,很显然通过使用它:代码变得非常清晰;当bean不再自己查找他们依赖的类而是由容器提供,甚至不需要知道这些类在哪里以及它们实际上是什么类型,这时高层次的解耦也变得很容易了。
正如上面提到的那样,反向控制/依赖注射存在两种主要的形式:
基于setter的依赖注射,是在调用无参的构造函数或无参的静态工厂方法实例化你的bean之后, 通过调用你的bean上的setter方法实现的。 在BeanFactory中定义的使用基于setter方法的注射依赖的bean是真正的JavaBean。 Spring一般提倡使用基于setter方法的依赖注射,因为很多的构造函数参数将会是笨重的, 尤其在有些属性是可选的情况下。
基于构造函数的依 赖注射,它是通过调用带有许多参数的构造方法实现的, 每个参数表示一个合作者或者属性。 另外,调用带有特定参数的静态工厂方法来构造bean可以被认为差不多等同的, 接下来的文字会把构造函数的参数看成和静态工厂方法的参数类似。 虽然Spring一般提倡在大多数情况下使用基于setter的依赖注射, 但是Spring还是完全支持基于构造函数的依赖注射, 因为你可能想要在那些只提供多参数构造函数并且没有setter方法的遗留的bean上使用Spring。 另外对于一些比较简单的bean,一些人更喜欢使用构造函数方法以确保bean不会处于错误的状态。
BeanFactory同时支持这两种方式将依赖注射到被管理bean中。(实际上它还支持在一些依赖已经通过构造函数方法注射后再使用setter方法注射依赖)。依赖的配置是以BeanDefinition的形式出现,它和JavaBeans的PropertyEditors一起使用从而知道如何把属性从一个格式转变为另一个。真正传送的值被封装为PropertyValue对象。然而,大多数Spring的使用者并不要直接(比如编程的方式)处理这些类,而更多地使用一个XML定义文件,这个文件会在内部被转变为这些类的实例,用来读取整个BeanFactory或ApplicationContext。
Bean依赖的决定通常取决于下面这些内容:
BeanFactory通过使用一个描述所有bean的配置被创建和实例化。大多数的Spring用户使用一个支持XML格式配置文件的BeanFactory或ApplicationContext实现。
每一个bean的依赖表现为属性,构造函数参数,或者当用静态工厂方法代替普通构造函数时工厂方法的参数。这些依赖将会在bean真正被创建出来后提供给bean。
每一个属性或者构造函数参数要么是一个要被设置的值的定义,要么是一个指向BeanFactory中其他bean的引用。在ApplicationContext的情况下,这个引用可以指向一个父亲ApplicationContext中bean。
每一个属性或构造函数参数的值,必须能够从(配置文件中)被指定的格式转变为真实类型。缺省情况下,Spring能够把一个字符串格式的值转变为所有内建的类型,比如int, long,String, boolean等等。另外当说到基于XML的BeanFactory实现的时候(包括ApplicationContext实现),它们已经为定义Lists, Maps, Sets和Properties集合类型提供了内在的支持。另外,Spring通过使用JavaBeans的 PropertyEditor定义,能够将字符串值转变为其他任意的类型。(你可以为PropertyEditor提供你自己的PropertyEditor定义从而能够转变你自定义的类型;更多关于PropertyEditors的信息以及如何手工增加自定义的PropertyEditors请参看第 3.9 节 “注册附加的定制PropertyEditor”)。当一个bean属性是一个Java Class类型,Spring允许你用这个类的名字的字符串作为这个属性的值,ClassEditor 这个内建的PropertyEditor会帮你把类的名字转变成真实的Class实例。
很 重要的一点就是:Spring在BeanFactory创建的时候要校验BeanFactory中每一个Bean的配置。这些校验包括作为Bean引用的 属性必须实际引用一个合法的bean(比如被引用的bean也定义在BeanFactory中,或者当ApplicationContext时,在父亲 ApplicationContext中)。但是,bean属性本身直到bean被真实建立的的 时候才被设置。对于那些是singleton并且被设置为pre-instantiated的bean来说(比如一个 ApplicationContext中的singletonbean),bean在创建BeanFactory的时候创建,但是对于其他情况,发生在 bean被请求的时候。当一个bean必须被创建时,它会潜在地导致一系列的其他bean被创建,像它的依赖以及它的依赖的依赖(如此下去)被创建和赋 值。
通常你可以信任Spring做了正确的事情。它会在BeanFactory装载的时候检查出错误,包括对不存在 bean的引用和循环引用。它会尽可能晚地设置属性和解决依赖(比如创建那些需要的依赖),也就是在bean真正被创建的时候。这就意味着:就算一个 BeanFactory被正确地装载,稍后当你请求一个bean的时候,如果创建那个bean或者它的依赖的时候出现了错误,这个BeanFactory 也会抛出一个异常。比如,如果一个bean抛出一个异常作为缺少或非法属性的结果,这样的情况就会发生。这种潜在地推迟一些配置错误可见性的行为正是 ApplicationContext默认预实例化singleton bean的原因。以前期的时间和内存为代价在beans真正需要之前创建它们,你就可以在ApplicationContext创建的时候找出配置错误, 而不是在后来。如果你愿意,你也可以覆盖这种默认的行为,设置这些singleton bean为lazy-load(不是预实例化的)。
几个例子:
首先,一个使用BeanFactory以及基于setter方法的依赖注射。下面是一个定义了一些bean的XmlBeanFactory 配置文件的一小部分。接下去是正式的bean的代码,演示了正确的setter方法声明。
<bean id="exampleBean" class="examples.ExampleBean">
<property name="beanOne"><ref bean="anotherExampleBean"/></property>
<property name="beanTwo"><ref bean="yetAnotherBean"/></property>
<property name="integerProperty"><value>1</value></property>
</bean>
<bean id="anotherExampleBean" class="examples.AnotherBean"/>
<bean id="yetAnotherBean" class="examples.YetAnotherBean"/>public class ExampleBean {
private AnotherBean beanOne;
private YetAnotherBean beanTwo;
private int i;
public void setBeanOne(AnotherBean beanOne) {
this.beanOne = beanOne;
}
public void setBeanTwo(YetAnotherBean beanTwo) {
this.beanTwo = beanTwo;
}
public void setIntegerProperty(int i) {
this.i = i;
}
}正如你所看到的一样,setter方法被声明以符合XML文件中指定的属性。(XML文件中的属性,直接对应着RootBeanDefinition中的PropertyValues对象)
接着是一个使用IoC type3(基于构造函数的依赖注射)的BeanFactory。下面是XML配置中的一段,指定了构造函数参数以及展示构造函数的代码:
<bean id="exampleBean" class="examples.ExampleBean">
<constructor-arg><ref bean="anotherExampleBean"/></constructor-arg>
<constructor-arg><ref bean="yetAnotherBean"/></constructor-arg>
<constructor-arg><value>1</value></constructor-arg>
</bean>
<bean id="anotherExampleBean" class="examples.AnotherBean"/>
<bean id="yetAnotherBean" class="examples.YetAnotherBean"/>public class ExampleBean {
private AnotherBean beanOne;
private YetAnotherBean beanTwo;
private int i;
public ExampleBean(AnotherBean anotherBean, YetAnotherBean yetAnotherBean, int i) {
this.beanOne = anotherBean;
this.beanTwo = yetAnotherBean;
this.i = i;
}
}正如你所看到的,bean定义中指定的构造函数参数将会作为ExampleBean的构造函数参数被传入。
现在考虑一下不用构造函数,而是调用一个静态工厂方法来返回一个对象的实例:
<bean id="exampleBean" class="examples.ExampleBean"
factory-method="createInstance">
<constructor-arg><ref bean="anotherExampleBean"/></constructor-arg>
<constructor-arg><ref bean="yetAnotherBean"/></constructor-arg>
<constructor-arg><value>1</value></constructor-arg>
</bean>
<bean id="anotherExampleBean" class="examples.AnotherBean"/>
<bean id="yetAnotherBean" class="examples.YetAnotherBean"/>public class ExampleBean {
...
// a private constructor
private ExampleBean(...) {
...
}
// a static factory method
// the arguments to this method can be considered the dependencies of the bean that
// is returned, regardless of how those arguments are actually used.
public static ExampleBean ExampleBean(AnotherBean anotherBean,
YetAnotherBean yetAnotherBean, int i) {
ExampleBean eb = new ExampleBean(...);
// some other operations
...
return eb;
}
}需要注意的是:静态工厂方法的参数由 constructor-arg元素提供,这和构造函数的用法是一样的。这些参数是可选的。重要的一点是工厂方法所返回的对象类型不一定和包含这个静态工厂方法的类一致,虽然上面这个例子中是一样的。前面所提到的实例工厂方法(non-static)用法基本上是一样的(除了使用factory-bean属性代替class属性),在这里就不再详细叙述了。 .
3.3.2. 深入Bean属性和构造函数参数
正如前面提到的那样,bean的属性和构造函数参数可以被定义为其他bean的引用(合作者),或者内联定义的值。为了达到这个目的,XmlBeanFactory在property和constructor-arg元素中支持许多子元素类型。
value元素用适合人读的字符串形式指定属性或构造函数参数。正如前面提到的那样,JavaBeans的PropertyEditors被用来将这些字符串从java.lang.String类型转变为真实的属性类型或参数类型。
<beans>
<bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<!-- results in a setDriverClassName(String) call -->
<property name="driverClassName">
<value>com.mysql.jdbc.Driver</value>
</property>
<property name="url">
<value>jdbc:mysql://localhost:3306/mydb</value>
</property>
<property name="username">
<value>root</value>
</property>
</bean>
</beans> null元素被用来处理null值。Spring将porperties等的空参数视为空的字符串。下面这个XmlBeanFactory配置:
<bean class="ExampleBean">
<property name="email"><value></value></property>
</bean> 导致email属性被设置为””,同java代码:exampleBean.setEmail("")等价。而专门的<null>元素则可以用来指定一个null值,所以
<bean class="ExampleBean">
<property name="email"><null/></property>
</bean> 同代码:exampleBean.setEmail(null)是等价的.
list, set, map, 以及 props 元素可以用来定义和设置类型 为Java的List,Set, Map, 和 Properties .
<beans>
...
<bean id="moreComplexObject" class="example.ComplexObject">
<!-- results in a setPeople(java.util.Properties) call -->
<property name="people">
<props>
<prop key="HarryPotter">The magic property</prop>
<prop key="JerrySeinfeld">The funny property</prop>
</props>
</property>
<!-- results in a setSomeList(java.util.List) call -->
<property name="someList">
<list>
<value>a list element followed by a reference</value>
<ref bean="myDataSource"/>
</list>
</property>
<!-- results in a setSomeMap(java.util.Map) call -->
<property name="someMap">
<map>
<entry key="yup an entry">
<value>just some string</value>
</entry>
<entry key="yup a ref">
<ref bean="myDataSource"/>
</entry>
</map>
</property>
<!-- results in a setSomeSet(java.util.Set) call -->
<property name="someSet">
<set>
<value>just some string</value>
<ref bean="myDataSource"/>
</set>
</property>
</bean>
</beans>注意:Map的entry或set的value,它们的值又可以是下面元素中的任何一个:
(bean | ref | idref | list | set | map | props | value | null)在property 元素中定义的bean元素用来定义一个内联的bean,而不是引用BeanFactory其他地方定义的bean。内联bean定义不需要任何id定义
<bean id="outer" class="...">
<!-- Instead of using a reference to target, just use an inner bean -->
<property name="target">
<bean class="com.mycompany.PersonImpl">
<property name="name"><value>Tony</value></property>
<property name="age"><value>51</value></property>
</bean>
</property>
</bean>idref元素完全是一种简写和防止错误的方式,用来设置属性值为容器中其他bean的id 或name。
<bean id="theTargetBean" class="...">
</bean>
<bean id="theClientBean" class="...">
<property name="targetName">
<idref bean="theTargetBean"/>
</property>
</bean>这个在运行时同下面的片段等价:
<bean id="theTargetBean" class="...">
</bean>
<bean id="theClientBean" class="...">
<property name="targetName">
<value>theTargetBean</value>
</property>
</bean>第一种形式比第二种形式更好的原因是:使用idref标记将会使Spring在部署的时候就验证其他的bean是否真正存在;在第二种形式中,targetName属性的类仅仅在Spring实例化这个类的时候做它自己的验证,这很可能在容器真正部署完很久之后。
另外,如果被引用的bean在同一个xml文件中而且bean的名称是bean的 id,那么就可以使用local属性。它会让XML解析器更早,在XML文档解析的时候,验证bean的名称。
<property name="targetName">
<idref local="theTargetBean"/>
</property>ref元素是最后一个能在property元 素中使用的元素。它是用来设置属性值引用容器管理的其他bean(可以叫做合作者)。正如前一节提到的,拥有这些属性的bean依赖被引用的bean,被 引用的bean将会在属性设置前,必要的时候需要时初始化(如果是一个singleton bean可能已经被容器初始化)。所有的引用根本上是一个指向其他对象的引用,不过有3种形式指定被引用对象的id/name,这3种不同形式决定作用域 和如何处理验证。
用ref元素的bean属性指定目标bean是最常见的形式,它允许指向的bean可以在同一个BeanFactory/ApplicationContext(无论是否在同一个XML文件中)中,也可以在父BeanFactory/ApplicationContext中。bean属性的值可以同目标bean的id属性相同,也可以同目标bean的name属性中任何一个值相同。
<ref bean="someBean"/>用local属性指定目标bean可以利用XML解析器的能力在同一个文件中验证XML id引用。local属性的值必须与目标bean的id属性一致。如果在同一个文件中没有匹配的元素,XML解析器将会产生一个错误。因此,如果目标bean在同一个XML文件中,那么使用local形式将是最好的选择(为了能够尽可能早的发现错误)。
<ref local="someBean"/>用parent属性指定目标bean允许引用当前BeanFactory(ApplicationContext)的父BeanFactory(ApplicationContext)中的bean。parent属性的值可以同目标bean的id属性相同,也可以同目标bean的name属 性中的一个值相同,而且目标bean必须在当前BeanFactory(ApplicationContext)的父 BeanFactory(ApplicationContext)中。当需要用某种proxy包装一个父上下文中存在的bean(可能和父上下文中的有同 样的name),所以需要原始的对象用来包装它。
<ref parent="someBean"/>3.3.3. 方法注入
对 于大部分的用户来说,容器中多数的bean是singleton的。当一个singleton的bean需要同另一个singleton的 bean合作(使用)时,或者一个非singleton的bean需要同另一个非singleton的bean合作的时候,通过定义一个bean为另一个 bean的属性来处理这种依赖的关系就足够了。然而当bean的生命周期不同的时候就有一个问题。想想一下一个singleton bean A,或许在每次方法调用的时候都需要使用一个non-singleton bean B。容器仅仅会创建这个singleton bean A一次,因此仅仅有一次的机会去设置它的属性。因此容器没有机会每次去为bean A提供新的bean B的实例。
一个解决这个问题的方法是放弃一些反向控制。Bean A可以通过实现 BeanFactoryAware知道容器的存在(参见这里)),使用编程的手段(参见这里)在需要的时候通过调用getBean("B")来向容器请求新的bean B实例。 因为bean的代码知道Spring并且耦合于Spring,所以这通常不是一个好的方案。
方法注入,BeanFactory的高级特性之一,可以以清洁的方式处理这种情况以及其他一些情况。
3.3.3.1. Lookup方法注入
Lookup 方法注射指容器能够重写容器中bean的抽象或具体方法,返回查找容器中其他bean的结果。 被查找的bean在上面描述的场景中通常是一个non-singleton bean (尽管也可以是一个singleton的)。Spring通过使用CGLIB库在客户端的类之上修改二进制码, 从而实现上述的场景要求。
包含方法注入的客户端类,必须按下面的形式的抽象(具体)定义方法:
protected abstract SingleShotHelper createSingleShotHelper();如果方法不是抽象的,Spring就会直接重写已有的实现。在XmlBeanFactory的情况下,你可以使用bean定义中的lookup-method 属性来指示Spring去注入/重写这个方法,以便从容器返回一个特定的bean。举个例子说明:
<!-- a stateful bean deployed as a protype (non-singleton) -->
<bean id="singleShotHelper class="..." singleton="false">
</bean>
<!-- myBean uses singleShotHelper -->
<bean id="myBean" class="...">
<lookup-method name="createSingleShotHelper"
bean="singleShotHelper"/>
<property>
...
</property>
</bean>当myBean需要一个新的singleShotHelper的实例的时候, 它就会调用它自己的createSingleShotHelper 方法。 值得注意的是:部署beans的人员必须小心地将singleShotHelper作为一个non-singleton部署 (如果确实需要这么做)。如果它作为一个singleton(除非明确说明,否则缺省就是singletion)而部署, 同一个singleShotHelper实例将会每次被返回。
注意Lookup方法注射能够同构造函数注射结合(对创建的bean提供可选的构造函数参数), 也可以同setter方法注射结合(在创建的bean之上设置属性)。
3.3.3.2. 任意方法的替换
另一种方法注射没有lookup方法注入用的多,它用另一个方法实现替换被管理bean的任意一个方法。用户可以放心跳过这一节(这是个有点高级的特性),除非这个功能确实需要。
在一个XmlBeanFactory中,对于一个被部署的bean, replaced-method元素可以用来把已存在的方法实现替换为其他的实现。 考虑如下的类,有一个我们想要重写的computeValue方法:
...
public class MyValueCalculator {
public String computeValue(String input) {
... some real code
}
... some other methods
}需要为新方法定义提供实现 org.springframework.beans.factory.support.MethodReplacer接口的类。
/** meant to be used to override the existing computeValue
implementation in MyValueCalculator */
public class ReplacementComputeValue implements MethodReplacer {
public Object reimplement(Object o, Method m, Object[] args) throws Throwable {
// get the input value, work with it, and return a computed result
String input = (String) args[0];
...
return ...;
}部署原始的类和指定方法重写的BeanFactory部署定义象下面所示的 :
<bean id="myValueCalculator class="x.y.z.MyValueCalculator">
<!-- arbitrary method replacement -->
<replaced-method name="computeValue" replacer="replacementComputeValue">
<arg-type>String</arg-type>
</replaced-method>
</bean>
<bean id="replacementComputeValue" class="a.b.c.ReplaceMentComputeValue">
</bean>replaced-method元素中的一个或多个arg-type 元素用来表示,这个被重载方法的方法签名。注意,参数的签名只有在方法被重载并且该方法有多个不同的形式的时候才真正需要。为了方便,参数的类型字符串可以使全限定名的子字符串。比如,以下的都匹配 java.lang.String。
java.lang.String
String
Str因为参数的个数通常就足够区别不同的可能,所以仅仅使用匹配参数的最短的字符串能够节省很多键入工作。
3.3.4. 使用 depends-on
对于大多数的情况,一个bean被另一个bean依赖,是由这个bean是否被当作其他bean的属性来表达的。 在XmlBeanFactory中,它是通过ref元素来完成的。 与这种方式不同的是,有时一个知道容器的bean仅仅会被给与它所的依赖的id (使用一个字符串值或等价的idref元素)。接着第一个bean就以编程的方式地向容器请求它的依赖。 在两种情况下,被依赖的bean都会在依赖它的bean之前被恰当地初始化。
对于相对罕见的情况,beans之间的依赖不够直接(举例,当一个类中的静态初始块需要被触发,比如数据库驱动的注册) ,depends-on 元素可以用来在初始化使用这个元素的bean之前,强制一个或多个beans初始化。
下面是一个配置的例子:
<bean id="beanOne" class="ExampleBean" depends-on="manager">
<property name="manager"><ref local="manager"/></property>
</bean>
<bean id="manager" class="ManagerBean"/>3.3.5. 自动装配协作对象
BeanFactory能够自动装配合作bean之间的关系。这就意味着,让Spring通过检查BeanFactory的内容来自动装配你的bean的合作者(也就是其他的bean)。自动装配功能有5种模式。自动装配可以指定给每一个bean,因此可以给一些bean使用而其他的bean不自动装配。通过使用自动装配,可以 减少(或消除)指定属性(或构造函数参数)的需要,显著节省键入工作。 [1] 在XmlBeanFactory中,使用bean元素的autowire属性来指定bean定义的自动装配模式。以下是允许的值.
表 3.2. 自动装配模式
模式 解释
no 不使用自动装配。Bean的引用必须通过ref元素定义。这是默认的配置,在较大的部署环境中不鼓励改变这个配置,因为明确的指定合作者能够得到更多的控制和清晰性。从某种程度上说,这也是系统结构的文档形式。
byName 通过属性名字进行自动装配。这个选项会会检查BeanFactory,查找一个与将要装配的属性同样名字的bean 。比如,你有一个bean的定义被设置为通过名字自动装配,它包含一个master属性(也就是说,它有一个setMaster(...)方法),Spring就会查找一个叫做master的bean定义,然后用它来设置master属性。
byType 如果BeanFactory中正好有一个同属性类型一样的bean,就自动装配这个属性。如果有多于一个这样的bean,就抛出一个致命异常,它指出你可能不能对那个bean使用byType的自动装配。如果没有匹配的bean,则什么都不会发生,属性不会被设置。如果这是你不想要的情况(什么都不发生),通过设置dependency-check="objects"属性值来指定在这种情况下应该抛出错误。
constructor 这个同byType类似,不过是应用于构造函数的参数。如果在BeanFactory中不是恰好有一个bean与构造函数参数相同类型,则一个致命的错误会产生。
autodetect 通过对bean 检查类的内部来选择constructor或byType。如果找到一个缺省的构造函数,那么就会应用byType。
注意:显式的指定依赖,比如property和constructor-arg元素,总会覆盖自动装配。自动装配的行为可以和依赖检查结合使用,依赖检查会在自动装配完成后发生。
注意:正如我们已经提到过的,对于大型的应用,自动装配不鼓励使用,因为它去除了你的合作类的透明性和结构。
3.3.6. 依赖检查
对于部署在BeanFactory的bean的未解决的依赖,Spring有能力去检查它们的存在性。 这些依赖要么是bean的JavaBean式的属性,在bean的定义中并没有为它们设置真实的值, 要么是通过自动装配特性被提供。
当 你想确保所有的属性(或者某一特定类型的所有属性)都被设置到bean上面的时候, 这项特性就很有用了。当然,在很多情况下一个bean类的很多属性都会有缺省的值, 或者一些属性并不会应用到所有的应用场景,那么这个特性的作用就有限了 。 依赖检查能够分别对每一个bean应用或取消应用,就像自动装配功能一样。缺省的是不 检查依赖关系。 依赖检查可以以几种不同的模式处理。在XmlBeanFactory中, 通过bean定义中的 dependency-check 属性来指定依赖检查,这个属性有以下的值。
表 3.3. 依赖检查模式
模式 解释
none 不进行依赖检查。没有指定值的bean属性仅仅是没有设值。
simple 对基本类型和集合(除了合作者外,比如其他的bean,所有东西)进行依赖检查。
object 对合作者进行依赖检查。
all 对合作者,基本类型和集合都进行依赖检查。
3.4. 自定义bean的本质特征
3.4.1. 生命周期接口
Spring提供了一些标志接口,用来改变BeanFactory中的bean的行为。 它们包括InitializingBean和DisposableBean。 实现这些接口将会导致BeanFactory调用前一个接口的afterPropertiesSet()方法, 调用后一个接口destroy()方法,从而使得bean可以在初始化和析构后做一些特定的动作。
在内部,Spring使用BeanPostProcessors 来处理它能找到的标志接口以及调用适当的方法。 如果你需要自定义的特性或者其他的Spring没有提供的生命周期行为, 你可以实现自己的 BeanPostProcessor。关于这方面更多的内容可以看这里: 第 3.7 节 “使用BeanPostprocessors定制bean”。
所有的生命周期的标志接口都在下面叙述。在附录的一节中,你可以找到相应的图, 展示了Spring如何管理bean;那些生命周期的特性如何改变你的bean的本质特征以及它们如何被管理。
3.4.1.1. InitializingBean / init-method
实现org.springframework.beans.factory.InitializingBean 接口允许一个bean在它的所有必须的属性被BeanFactory设置后, 来执行初始化的工作。InitializingBean接口仅仅制定了一个方法:
* Invoked by a BeanFactory after it has set all bean properties supplied
* (and satisfied BeanFactoryAware and ApplicationContextAware).
* <p>This method allows the bean instance to perform initialization only
* possible when all bean properties have been set and to throw an
* exception in the event of misconfiguration.
* @throws Exception in the event of misconfiguration (such
* as failure to set an essential property) or if initialization fails.
*/
void afterPropertiesSet() throws Exception;注意:通常InitializingBean接口的使用是能够避免的(而且不鼓励,因为没有必要把代码同Spring耦合起来)。Bean的定义支持指定一个普通的初始化方法。在使用XmlBeanFactory的情况下,可以通过指定init-method属性来完成。 举例来说,下面的定义:
<bean id="exampleInitBean" class="examples.ExampleBean" init-method="init"/>
public class ExampleBean {
public void init() {
// do some initialization work
}
}同下面的完全一样:
<bean id="exampleInitBean" class="examples.AnotherExampleBean"/>
public class AnotherExampleBean implements InitializingBean {
public void afterPropertiesSet() {
// do some initialization work
}
}但却不把代码耦合于Spring。
3.4.1.2. DisposableBean / destroy-method
实现org.springframework.beans.factory.DisposableBean接口允许一个bean, 可以在包含它的BeanFactory销毁的时候得到一个回调。DisposableBean也只指定了一个方法:
/**
* Invoked by a BeanFactory on destruction of a singleton.
* @throws Exception in case of shutdown errors.
* Exceptions will get logged but not rethrown to allow
* other beans to release their resources too.
*/
void destroy() throws Exception;
注意:通常DisposableBean接口的使用能够避免的(而且是不鼓励的,因为它不必要地将代码耦合于Spring)。 Bean的定义支持指定一个普通的析构方法。在使用XmlBeanFactory使用的情况下,它是通过 destroy-method属性完成。 举例来说,下面的定义:
<bean id="exampleInitBean" class="examples.ExampleBean" destroy-method="destroy"/>
public class ExampleBean {
public void cleanup() {
// do some destruction work (like closing connection)
}
}同下面的完全一样:
<bean id="exampleInitBean" class="examples.AnotherExampleBean"/>
public class AnotherExampleBean implements DisposableBean {
public void destroy() {
// do some destruction work
}
}但却不把代码耦合于Spring。
重 要的提示:当以portotype模式部署一个bean的时候,bean的生命周期将会有少许的变化。 通过定义,Spring无法管理一个non-singleton/prototype bean的整个生命周期, 因为当它创建之后,它被交给客户端而且容器根本不再留意它了。 当说起non-singleton/prototype bean的时候,你可以把Spring的角色想象成“new”操作符的替代品。 从那之后的任何生命周期方面的事情都由客户端来处理。BeanFactory中bean的生命周期将会在第 3.4.1 节 “生命周期接口” 一节中有更详细的叙述 .
3.4.2. 了解自己
3.4.2.1. BeanFactoryAware
对于实现了org.springframework.beans.factory.BeanFactoryAware接口的类, 当它被BeanFactory创建后,它会拥有一个指向创建它的BeanFactory的引用。
public interface BeanFactoryAware {
/**
* Callback that supplies the owning factory to a bean instance.
* <p>Invoked after population of normal bean properties but before an init
* callback like InitializingBean's afterPropertiesSet or a custom init-method.
* @param beanFactory owning BeanFactory (may not be null).
* The bean can immediately call methods on the factory.
* @throws BeansException in case of initialization errors
* @see BeanInitializationException
*/
void setBeanFactory(BeanFactory beanFactory) throws BeansException;
}这允许bean可以以编程的方式操控创建它们的BeanFactory, 既可以直接使用 org.springframework.beans.factory.BeanFactory接 口, 也可以将引用强制将类型转换为已知的子类型从而获得更多的功能。这个特性主要用于编程式地取得其他bean。 虽然在一些场景下这个功能是有用的,但是一般来说它应该避免使用,因为它使代码与Spring耦合在一起, 而且也不遵循反向控制的风格(合作者应当作属性提供给bean)。
3.4.2.2. BeanNameAware
如果一个bean实现了org.springframework.beans.factory.BeanNameAware接口, 并且被部署到一个 BeanFactory中,那么BeanFactory就会通过这个接口来调用bean,以便通知这个bean它被部署的id 。 这个回调发生在普通的bean属性设置之后,在初始化回调之前,比如InitializingBean的afterPropertiesSet方法(或者自定义的init- method)。
3.4.3. FactoryBean
接口org.springframework.beans.factory.FactoryBean 一般由本身是工厂类的对象实现。BeanFactory接口提供了三个方法:
Object getObject(): 必须返回一个这个工厂类创建的对象实例。这个实例可以是共享的(取决于这个工厂返回的是singleton还是prototype)。
boolean isSingleton(): 如果Factory返回的对象是singleton,返回true,否则返回false。
Class getObjectType(): 返回getObject()方法返回的对象的类型,如果类型不是预先知道的,则返回null。
3.5. 子bean定义
一 个bean定义可能会包含大量的配置信息,包括容器相关的信息(比如初始化方法,静态工厂方法名等等)以及构造函数参数和属性的值。一个子bean定义是 一个能够从父bean定义继承配置数据的bean定义。 它可以覆盖一些值,或者添加一些其他需要的值。使用父和子的bean定义可以节省很多的输入工作。实际上,这就是一种模版形式。
当以编程的方式使用一个BeanFactory,子bean定义用ChildBeanDefinition类表示。 大多数的用户从来不需要以这个方式使用它们,而是在类似XmlBeanFactory的BeanFactory 中以声明的方式配置bean定义。在一个XmlBeanFactory的bean定义中,使用parent属性指出一个子bean定义,而父bean则作为这个属性的值。
<bean id="inheritedTestBean" class="org.springframework.beans.TestBean">
<property name="name"><value>parent</value></property>
<property name="age"><value>1</value></property>
</bean>
<bean id="inheritsWithDifferentClass" class="org.springframework.beans.DerivedTestBean"
parent="inheritedTestBean" init-method="initialize">
<property name="name"><value>override</value></property>
<!-- age should inherit value of 1 from parent -->
</bean>如果子bean定义没有指定class属性,将使用父定义的class属性,当然也可以覆盖它。 在后面一种情况中,子bean的class属性值必须同父bean的兼容,也就是它必须能够接受父亲的属性值。
一个子bean定义可以从父亲处继承构造函数参数,属性值以及方法,并且可以选择增加新的值。 如果init-method,destroy-method和/或静态factory-method被指定了,它们就会覆盖父亲相应的设置。
剩余的设置将 总是 从子定义处得到: 依赖, 自动装配模式, 依赖检查, singleton, 延迟初始化。
在下面的例子中父定义并没有指定class属性:
<bean id="inheritedTestBeanWithoutClass">
<property name="name"><value>parent</value></property>
<property name="age"><value>1</value></property>
</bean>
<bean id="inheritsWithClass" class="org.springframework.beans.DerivedTestBean"
parent="inheritedTestBeanWithoutClass" init-method="initialize">
<property name="name"><value>override</value></property>
<!-- age should inherit value of 1 from parent -->
</bean>这 个父bean就无法自己实例化;它实际上仅仅是一个纯模版或抽象bean,充当子定义的父定义。 若要尝试单独使用这样的父bean(比如将它作为其他bean的ref属性而引用,或者直接使用这个父 bean的id调用getBean()方法),将会导致一个错误。同样地,容器内部的preInstantiateSingletons方法会完全忽略这种既没有parent属性也没有class属性的bean定义,因为它们是不完整的。
特别注意: 这里并没有办法显式地声明一个bean定义为抽象的。 如果一个bean确实有一个class属性定义,那么它就能够被实例化。而且要注意 XmlBeanFactory默认地将会预实例化所有的singleton的bean。 因此很重要的一点是:如果你有一个(父)bean定义指定了class属性,而你又想仅仅把它当作模板使用, 那么你必须保证将lazy-init属性设置为true(或者将bean标记为non-singleton),否则 XmlBeanFactory(以及其他可能的容器)将会预实例化它。
3.6. BeanFactory之间的交互
BeanFactory 本质上不过是高级工厂的接口,它维护不同bean和它们所依赖的bean的注册。 BeanFactory使得你可以利用 bean工厂读取和访问bean定义。 当你使用BeanFactory的时候,你可以象下面一样创建并且读入一些XML格式的bean定义:
InputStream is = new FileInputStream("beans.xml");
XmlBeanFactory factory = new XmlBeanFactory(is);基本上这就足够了。使用getBean(String)你可以取得你的bean的实例。 如果你将它定义为一个singleton(缺省的)你将会得到同一个bean的引用, 如果你将singleton设置为false,那么你将会每次得到一个新的实例。 在客户端的眼里BeanFactory是惊人的简单。BeanFactory接口仅仅为客户端调用提供了5个方法:
boolean containsBean(String): 如果BeanFactory包含一个与所给名称匹配的bean定义,则返回true
Object getBean(String): 返回一个以所给名字注册的bean的实例。返回一个singleton的共享的实例还是一个新创建的实例, 这取决于bean在BeanFactory配置中如何被配置的。一个BeansException将会在下面两种情况中抛出:bean没有被找到(在这种情况下,抛出的是NoSuchBeanDefinitionException),或者在实例化和准备bean的时候发生异常
Object getBean(String,Class): 返回一个以给定名字注册的bean。返回的bean将会被强制类型转换成给定的Class。 如果bean不能被类型转换,相应的异常将会被抛出(BeanNotOfRequiredTypeException)。 此外getBean(String)的所有规则也同样适用这个方法(同上)
boolean isSingleton(String): 判断以给定名字注册的bean定义是一个singleton还是一个prototype。 如果与给定名字相应的bean定义没有被找到,将会抛出一个异常(NoSuchBeanDefinitionException)
String[] getAliases(String):如果给定的bean名字在bean定义中有别名,则返回这些别名
leexhwhy 发表于:2005.05.08 14:16 ::分类: ( spring aop ) ::阅读:(424次) :: 评论 (0)
===========================================================
Beans, BeanFactory和ApplicationContext
===========================================================
第 3 章 Beans, BeanFactory和ApplicationContext
3.1. 简介
在Spring中,两个最基本最重要的包是 org.springframework.beans 和 org.springframework.context. 这两个包中的代码为Spring的反向控制 特性(也叫作依赖注射)提供了基础。 BeanFactory提供了一种先进的配置机制来管理任何种类bean(对象),这种配置机制考虑到任何一种可能的存储方式。 ApplicationContext建立在BeanFactory之上,并增加了其他的功能,比如更容易同Spring AOP特性整合, 消息资源处理(用于国际化),事件传递,以声明的方式创建ApplicationContext, 可选的父上下文和与应用层相关的上下文(比如WebApplicationContext),以及其他方面的增强。
简而言之,BeanFactory提供了配置框架和基本的功能, 而 ApplicationContext为 它增加了更强的功能,这些功能中的一些或许更加接近J2EE并且围绕企业级应用。一般来说,ApplicationContext是 BeanFactory的完全超集, 任何BeanFactory功能和行为的描述也同样被认为适用于ApplicationContext
用户有时不能确定BeanFactory和ApplicationContext中哪一个在特定场合下更适合。 通常大部分在J2EE环境的应用中,最好选择使用ApplicationContext, 因为它不仅提供了BeanFactory所有的特性以及它自己附加的特性,而且还提供以声明的方式使用一些功能, 这通常是令人满意的。BeanFactory主要是在非常关注内存使用的情况下 (比如在一个每kb都要计算的applet中)使用,而且你也不需要用到ApplicationContext的所有特性。
这一章粗略地分为两部分,第一部分包括对BeanFactory和ApplicationContext都适用的一些基本原则。第二部分包括仅仅适用于ApplicationContext的一些特性
3.2. BeanFactory 和 BeanDefinitions - 基础
3.2.1. BeanFactory
BeanFactory实际上是实例化,配置和管理众多bean的容器。 这些bean通常会彼此合作,因而它们之间会产生依赖。 BeanFactory使用的配置数据可以反映这些依赖关系中 (一些依赖可能不像配置数据一样可见,而是在运行期作为bean之间程序交互的函数)。
一个BeanFactory可以用接口org.springframework.beans.factory.BeanFactory表示, 这个接口有多个实现。 最常使用的的简单的eanFactory实现是org.springframework.beans.factory.xml.XmlBeanFactory。 (这里提醒一下:ApplicationContext是BeanFactory的子类, 所以大多数的用户更喜欢使用ApplicationContext的XML形式)。
虽然大多数情况下,几乎所有被BeanFactory管理的用户代码都不需要知道BeanFactory, 但是BeanFactory还是以某种方式实例化。可以使用下面的代码实例化BeanFactory:
InputStream is = new FileInputStream("beans.xml");
XmlBeanFactory factory = new XmlBeanFactory(is);或者
ClassPathResource res = new ClassPathResource("beans.xml");
XmlBeanFactory factory = new XmlBeanFactory(res);或者
ClassPathXmlApplicationContext appContext = new ClassPathXmlApplicationContext(
new String[] {"applicationContext.xml", "applicationContext-part2.xml"});
// of course, an ApplicationContext is just a BeanFactory
BeanFactory factory = (BeanFactory) appContext;很多情况下,用户代码不需要实例化BeanFactory, 因为Spring框架代码会做这件事。例如,web层提供支持代码,在J2EE web应用启动过程中自动载入一个Spring ApplicationContext。这个声明过程在这里描述:
编程操作BeanFactory将会在后面提到,下面部分将集中描述BeanFactory的配置.
一个最基本的BeanFactory配置由一个或多个它所管理的Bean定义组成。在一个XmlBeanFactory中,根节点beans中包含一个或多个bean元素。
<?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="..." class="...">
...
</bean>
<bean id="..." class="...">
...
</bean>
...
</beans>3.2.2. BeanDefinition
一个XmlBeanFactory中的Bean定义包括的内容有:
classname:这通常是bean的真正的实现类。但是如果一个bean使用一个静态工厂方法所创建而不是被普通的构造函数创建,那么这实际上就是工厂类的classname
bean行为配置元素:它声明这个bean在容器的行为方式(比如prototype或singleton,自动装配模式,依赖检查模式,初始化和析构方法)
构造函数的参数和新创建bean需要的属性:举一个例子,一个管理连接池的bean使用的连接数目(即可以指定为一个属性,也可以作为一个构造函数参数),或者池的大小限制
和这个bean工作相关的其他bean:比如它的合作者(同样可以作为属性或者构造函数的参数)。这个也被叫做依赖。
上面列出的概念直接转化为组成bean定义的一组元素。这些元素在下面的表格中列出,它们每一个都有更详细的说明的链接。
表 3.1. Bean定义的解释
特性 详细说明
class 第 3.2.3 节 “bean的类”
id和name 第 3.2.4 节 “Bean的标志符 (id与name)”
singleton或prototype 第 3.2.5 节 “Singleton的使用与否”
构造函数参数 第 3.3.1 节 “设置bean的属性和合作者”
bean的属性 第 3.3.1 节 “设置bean的属性和合作者”
自动装配模式 第 3.3.5 节 “自动装配协作对象”
依赖检查模式 第 3.3.6 节 “依赖检查”
初始化模式 第 3.4.1 节 “生命周期接口”
析构方法 第 3.4.1 节 “生命周期接口”
注意bean定义可以表示为真正的接口org.springframework.beans.factory.config.BeanDefinition以及它的各种子接口和实现。然而,绝大多数的用户代码不需要与BeanDefination直接接触。
3.2.3. bean的类
class属性通常是强制性的(参考第 3.2.3.3 节 “通过实例工厂方法创建bean”和第 3.5 节 “子bean定义”),有两种用法。在绝大多数情况下,BeanFactory直接调用bean的构造函数来"new"一个bean(相当于调用new的Java代码),class属性指定了需要创建的bean的类。 在比较少的情况下,BeanFactory调用某个类的静态的工厂方法来创建bean, class属性指定了实际包含静态工厂方法的那个类。 (至于静态工厂方法返回的bean的类型是同一个类还是完全不同的另一个类,这并不重要)。
3.2.3.1. 通过构造函数创建bean
当 使用构造函数创建bean时,所有普通的类都可以被Spring使用并且和Spring兼容。 这就是说,被创建的类不需要实现任何特定的接口或者按照特定的样式进行编写。仅仅指定bean的类就足够了。 然而,根据bean使用的IoC类型,你可能需要一个默认的(空的)构造函数。
另外,BeanFactory并不局限于管理真正的JavaBean,它也能管理任何你 想让它管理的类。虽然很多使用Spring的人喜欢在BeanFactory中用真正的JavaBean (仅包含一个默认的(无参数的)构造函数,在属性后面定义相对应的setter和getter方法),但是在你的BeanFactory中也可以使用特殊 的非bean样式的类。 举例来说,如果你需要使用一个遗留下来的完全没有遵守JavaBean规范的连接池, 不要担心,Spring同样能够管理它。
使用XmlBeanFactory你可以像下面这样定义你的bean class:
<bean id="exampleBean"
class="examples.ExampleBean"/>
<bean name="anotherExample"
class="examples.ExampleBeanTwo"/> 至于为构造函数提供(可选的)参数,以及对象实例创建后设置实例属性,将会在后面叙述
3.2.3.2. 通过静态工厂方法创建Bean
当你定义一个使用静态工厂方法创建的bean,同时使用class属性指定包含静态工厂方法的类,这个时候需要factory-method属性来指定工厂方法名。Spring调用这个方法(包含一组可选的参数)并返回一个有效的对象,之后这个对象就完全和构造方法创建的对象一样。用户可以使用这样的bean定义在遗留代码中调用静态工厂。
下面是一个bean定义的例子,声明这个bean要通过factory-method指定的方法创建。注意这个bean定义并没有指定返回对象的类型,只指定包含工厂方法的类。在这个例子中,createInstance 必须是static方法 .
<bean id="exampleBean"
class="examples.ExampleBean2"
factory-method="createInstance"/>至于为工厂方法提供(可选的)参数,以及对象实例被工厂方法创建后设置实例属性,将会在后面叙述.
3.2.3.3. 通过实例工厂方法创建bean
使用一个实例工厂方法(非静态的)创建bean和使用静态工厂方法非常类似,调用一个已存在的bean(这个bean应该是工厂类型)的工厂方法来创建新的bean。
使用这种机制,class属性必须为空,而且factory-bean属性必须指定一个bean的名字,这个bean一定要在当前的bean工厂或者父bean工厂中,并包含工厂方法。 而工厂方法本身仍然要通过factory-method属性设置。
下面是一个例子:
<!-- The factory bean, which contains a method called
createInstance -->
<bean id="myFactoryBean"
class="...">
...
</bean>
<!-- The bean to be created via the factory bean -->
<bean id="exampleBean"
factory-bean="myFactoryBean"
factory-method="createInstance"/>虽然我们要在后面讨论设置bean的属性,但是这个方法意味着工厂bean本身能够被容器通过依赖注射来管理和配置
3.2.4. Bean的标志符 (id与name)
每一个bean都有一个或多个id(也叫作标志符,或名字;这些名词说的是一回事)。这些id在管理bean的BeanFactory或ApplicationContext中必须是唯一的。 一个bean差不多总是只有一个id,但是如果一个bean有超过一个的id,那么另外的那些本质上可以认为是别名。
在 一个XmlBeanFactory中(包括ApplicationContext的形式), 你可以用id或者name属性来指定bean的id(s),并且在这两个或其中一个属性中至少指定一个id。 id属性允许你指定一个id,并且它在XML DTD(定义文档)中作为一个真正的XML元素的ID属性被标记, 所以XML解析器能够在其他元素指回向它的时候做一些额外的校验。正因如此,用id属性指定bean的id是一个比较好的方式。 然而,XML规范严格限定了在XML ID中合法的字符。通常这并不是真正限制你, 但是如果你有必要使用这些字符(在ID中的非法字符),或者你想给bean增加其他的别名, 那么你可以通过name属性指定一个或多个id(用逗号,或者分号;分隔)。
3.2.5. Singleton的使用与否
Beans被定义为两种部署模式中的一种:singleton或non-singleton。 (后一种也别叫作prototype,尽管这个名词用的不精确因为它并不是非常适合)。 如果一个bean是singleton形态的,那么就只有一个共享的实例存在, 所有和这个bean定义的id符合的bean请求都会返回这个唯一的、特定的实例。
如果bean以non-singleton,prototype模式部署的话,对这个bean的每次请求都会创建一个新的bean实例。这对于例如每个user需要一个独立的user对象这样的情况是非常理想的。
Beans 默认被部署为singleton模式,除非你指定。要记住把部署模式变为non-singletion(prototype)后,每一次对这个bean的 请求都会导致一个新创建的bean,而这可能并不是你真正想要的。所以仅仅在绝对需要的时候才把模式改成prototype。
在下面这个例子中,两个bean一个被定义为singleton,而另一个被定义为non-singleton(prototype)。客户端每次向BeanFactory请求都会创建新的exampleBean,而AnotherExample仅仅被创建一次;在每次对它请求都会返回这个实例的引用。
<bean id="exampleBean"
class="examples.ExampleBean" singleton="false"/>
<bean name="yetAnotherExample"
class="examples.ExampleBeanTwo" singleton="true"/>注 意:当部署一个bean为prototype模式,这个bean的生命周期就会有稍许改变。 通过定义,Spring无法管理一个non-singleton/prototype bean的整个生命周期, 因为当它创建之后,它被交给客户端而且容器根本不再跟踪它了。当说起non-singleton/prototype bean的时候, 你可以把Spring的角色想象成“new”操作符的替代品。从那之后的任何生命周期方面的事情都由
简而言之,BeanFactory提供了配置框架和基本的功能, 而 ApplicationContext为 它增加了更强的功能,这些功能中的一些或许更加接近J2EE并且围绕企业级应用。一般来说,ApplicationContext是 BeanFactory的完全超集, 任何BeanFactory功能和行为的描述也同样被认为适用于ApplicationContext
用户有时不能确定BeanFactory和ApplicationContext中哪一个在特定场合下更适合。 通常大部分在J2EE环境的应用中,最好选择使用ApplicationContext, 因为它不仅提供了BeanFactory所有的特性以及它自己附加的特性,而且还提供以声明的方式使用一些功能, 这通常是令人满意的。BeanFactory主要是在非常关注内存使用的情况下 (比如在一个每kb都要计算的applet中)使用,而且你也不需要用到ApplicationContext的所有特性。
这一章粗略地分为两部分,第一部分包括对BeanFactory和ApplicationContext都适用的一些基本原则。第二部分包括仅仅适用于ApplicationContext的一些特性
3.2. BeanFactory 和 BeanDefinitions - 基础
3.2.1. BeanFactory
BeanFactory实际上是实例化,配置和管理众多bean的容器。 这些bean通常会彼此合作,因而它们之间会产生依赖。 BeanFactory使用的配置数据可以反映这些依赖关系中 (一些依赖可能不像配置数据一样可见,而是在运行期作为bean之间程序交互的函数)。
一个BeanFactory可以用接口org.springframework.beans.factory.BeanFactory表示, 这个接口有多个实现。 最常使用的的简单的eanFactory实现是org.springframework.beans.factory.xml.XmlBeanFactory。 (这里提醒一下:ApplicationContext是BeanFactory的子类, 所以大多数的用户更喜欢使用ApplicationContext的XML形式)。
虽然大多数情况下,几乎所有被BeanFactory管理的用户代码都不需要知道BeanFactory, 但是BeanFactory还是以某种方式实例化。可以使用下面的代码实例化BeanFactory:
InputStream is = new FileInputStream("beans.xml");
XmlBeanFactory factory = new XmlBeanFactory(is);或者
ClassPathResource res = new ClassPathResource("beans.xml");
XmlBeanFactory factory = new XmlBeanFactory(res);或者
ClassPathXmlApplicationContext appContext = new ClassPathXmlApplicationContext(
new String[] {"applicationContext.xml", "applicationContext-part2.xml"});
// of course, an ApplicationContext is just a BeanFactory
BeanFactory factory = (BeanFactory) appContext;很多情况下,用户代码不需要实例化BeanFactory, 因为Spring框架代码会做这件事。例如,web层提供支持代码,在J2EE web应用启动过程中自动载入一个Spring ApplicationContext。这个声明过程在这里描述:
编程操作BeanFactory将会在后面提到,下面部分将集中描述BeanFactory的配置.
一个最基本的BeanFactory配置由一个或多个它所管理的Bean定义组成。在一个XmlBeanFactory中,根节点beans中包含一个或多个bean元素。
<?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="..." class="...">
...
</bean>
<bean id="..." class="...">
...
</bean>
...
</beans>3.2.2. BeanDefinition
一个XmlBeanFactory中的Bean定义包括的内容有:
classname:这通常是bean的真正的实现类。但是如果一个bean使用一个静态工厂方法所创建而不是被普通的构造函数创建,那么这实际上就是工厂类的classname
bean行为配置元素:它声明这个bean在容器的行为方式(比如prototype或singleton,自动装配模式,依赖检查模式,初始化和析构方法)
构造函数的参数和新创建bean需要的属性:举一个例子,一个管理连接池的bean使用的连接数目(即可以指定为一个属性,也可以作为一个构造函数参数),或者池的大小限制
和这个bean工作相关的其他bean:比如它的合作者(同样可以作为属性或者构造函数的参数)。这个也被叫做依赖。
上面列出的概念直接转化为组成bean定义的一组元素。这些元素在下面的表格中列出,它们每一个都有更详细的说明的链接。
表 3.1. Bean定义的解释
特性 详细说明
class 第 3.2.3 节 “bean的类”
id和name 第 3.2.4 节 “Bean的标志符 (id与name)”
singleton或prototype 第 3.2.5 节 “Singleton的使用与否”
构造函数参数 第 3.3.1 节 “设置bean的属性和合作者”
bean的属性 第 3.3.1 节 “设置bean的属性和合作者”
自动装配模式 第 3.3.5 节 “自动装配协作对象”
依赖检查模式 第 3.3.6 节 “依赖检查”
初始化模式 第 3.4.1 节 “生命周期接口”
析构方法 第 3.4.1 节 “生命周期接口”
注意bean定义可以表示为真正的接口org.springframework.beans.factory.config.BeanDefinition以及它的各种子接口和实现。然而,绝大多数的用户代码不需要与BeanDefination直接接触。
3.2.3. bean的类
class属性通常是强制性的(参考第 3.2.3.3 节 “通过实例工厂方法创建bean”和第 3.5 节 “子bean定义”),有两种用法。在绝大多数情况下,BeanFactory直接调用bean的构造函数来"new"一个bean(相当于调用new的Java代码),class属性指定了需要创建的bean的类。 在比较少的情况下,BeanFactory调用某个类的静态的工厂方法来创建bean, class属性指定了实际包含静态工厂方法的那个类。 (至于静态工厂方法返回的bean的类型是同一个类还是完全不同的另一个类,这并不重要)。
3.2.3.1. 通过构造函数创建bean
当 使用构造函数创建bean时,所有普通的类都可以被Spring使用并且和Spring兼容。 这就是说,被创建的类不需要实现任何特定的接口或者按照特定的样式进行编写。仅仅指定bean的类就足够了。 然而,根据bean使用的IoC类型,你可能需要一个默认的(空的)构造函数。
另外,BeanFactory并不局限于管理真正的JavaBean,它也能管理任何你 想让它管理的类。虽然很多使用Spring的人喜欢在BeanFactory中用真正的JavaBean (仅包含一个默认的(无参数的)构造函数,在属性后面定义相对应的setter和getter方法),但是在你的BeanFactory中也可以使用特殊 的非bean样式的类。 举例来说,如果你需要使用一个遗留下来的完全没有遵守JavaBean规范的连接池, 不要担心,Spring同样能够管理它。
使用XmlBeanFactory你可以像下面这样定义你的bean class:
<bean id="exampleBean"
class="examples.ExampleBean"/>
<bean name="anotherExample"
class="examples.ExampleBeanTwo"/> 至于为构造函数提供(可选的)参数,以及对象实例创建后设置实例属性,将会在后面叙述
3.2.3.2. 通过静态工厂方法创建Bean
当你定义一个使用静态工厂方法创建的bean,同时使用class属性指定包含静态工厂方法的类,这个时候需要factory-method属性来指定工厂方法名。Spring调用这个方法(包含一组可选的参数)并返回一个有效的对象,之后这个对象就完全和构造方法创建的对象一样。用户可以使用这样的bean定义在遗留代码中调用静态工厂。
下面是一个bean定义的例子,声明这个bean要通过factory-method指定的方法创建。注意这个bean定义并没有指定返回对象的类型,只指定包含工厂方法的类。在这个例子中,createInstance 必须是static方法 .
<bean id="exampleBean"
class="examples.ExampleBean2"
factory-method="createInstance"/>至于为工厂方法提供(可选的)参数,以及对象实例被工厂方法创建后设置实例属性,将会在后面叙述.
3.2.3.3. 通过实例工厂方法创建bean
使用一个实例工厂方法(非静态的)创建bean和使用静态工厂方法非常类似,调用一个已存在的bean(这个bean应该是工厂类型)的工厂方法来创建新的bean。
使用这种机制,class属性必须为空,而且factory-bean属性必须指定一个bean的名字,这个bean一定要在当前的bean工厂或者父bean工厂中,并包含工厂方法。 而工厂方法本身仍然要通过factory-method属性设置。
下面是一个例子:
<!-- The factory bean, which contains a method called
createInstance -->
<bean id="myFactoryBean"
class="...">
...
</bean>
<!-- The bean to be created via the factory bean -->
<bean id="exampleBean"
factory-bean="myFactoryBean"
factory-method="createInstance"/>虽然我们要在后面讨论设置bean的属性,但是这个方法意味着工厂bean本身能够被容器通过依赖注射来管理和配置
3.2.4. Bean的标志符 (id与name)
每一个bean都有一个或多个id(也叫作标志符,或名字;这些名词说的是一回事)。这些id在管理bean的BeanFactory或ApplicationContext中必须是唯一的。 一个bean差不多总是只有一个id,但是如果一个bean有超过一个的id,那么另外的那些本质上可以认为是别名。
在 一个XmlBeanFactory中(包括ApplicationContext的形式), 你可以用id或者name属性来指定bean的id(s),并且在这两个或其中一个属性中至少指定一个id。 id属性允许你指定一个id,并且它在XML DTD(定义文档)中作为一个真正的XML元素的ID属性被标记, 所以XML解析器能够在其他元素指回向它的时候做一些额外的校验。正因如此,用id属性指定bean的id是一个比较好的方式。 然而,XML规范严格限定了在XML ID中合法的字符。通常这并不是真正限制你, 但是如果你有必要使用这些字符(在ID中的非法字符),或者你想给bean增加其他的别名, 那么你可以通过name属性指定一个或多个id(用逗号,或者分号;分隔)。
3.2.5. Singleton的使用与否
Beans被定义为两种部署模式中的一种:singleton或non-singleton。 (后一种也别叫作prototype,尽管这个名词用的不精确因为它并不是非常适合)。 如果一个bean是singleton形态的,那么就只有一个共享的实例存在, 所有和这个bean定义的id符合的bean请求都会返回这个唯一的、特定的实例。
如果bean以non-singleton,prototype模式部署的话,对这个bean的每次请求都会创建一个新的bean实例。这对于例如每个user需要一个独立的user对象这样的情况是非常理想的。
Beans 默认被部署为singleton模式,除非你指定。要记住把部署模式变为non-singletion(prototype)后,每一次对这个bean的 请求都会导致一个新创建的bean,而这可能并不是你真正想要的。所以仅仅在绝对需要的时候才把模式改成prototype。
在下面这个例子中,两个bean一个被定义为singleton,而另一个被定义为non-singleton(prototype)。客户端每次向BeanFactory请求都会创建新的exampleBean,而AnotherExample仅仅被创建一次;在每次对它请求都会返回这个实例的引用。
<bean id="exampleBean"
class="examples.ExampleBean" singleton="false"/>
<bean name="yetAnotherExample"
class="examples.ExampleBeanTwo" singleton="true"/>注 意:当部署一个bean为prototype模式,这个bean的生命周期就会有稍许改变。 通过定义,Spring无法管理一个non-singleton/prototype bean的整个生命周期, 因为当它创建之后,它被交给客户端而且容器根本不再跟踪它了。当说起non-singleton/prototype bean的时候, 你可以把Spring的角色想象成“new”操作符的替代品。从那之后的任何生命周期方面的事情都由客户端来处理 。BeanFactory中bean的生命周期将会在 第 3.4.1 节 “生命周期接口”一节中有更详细的叙述。
3.3. 属性,合作者,自动装配和依赖检查
3.3.1. 设置bean的属性和合作者
反向控制通常与依赖注入同时提及。基本的规则是bean通过以下方式来定义它们的依赖(比如它们与之合作的其他对象):构造函数的参数,工厂方法的参数;当对象实例被构造出来或从一个工厂方法返回后设置在这个实例上的属性。容器的工作就是创建完bean之后,真正地注入这些依赖。这完全是和一般控制方式相反的(因此称为反向控制),比如bean实例化,或者直接使用构造函数定位依赖关系,或者类似Service Locator模式的东西。我们不会详细阐述依赖注射的优点,很显然通过使用它:代码变得非常清晰;当bean不再自己查找他们依赖的类而是由容器提供,甚至不需要知道这些类在哪里以及它们实际上是什么类型,这时高层次的解耦也变得很容易了。
正如上面提到的那样,反向控制/依赖注射存在两种主要的形式:
基于setter的依赖注射,是在调用无参的构造函数或无参的静态工厂方法实例化你的bean之后, 通过调用你的bean上的setter方法实现的。 在BeanFactory中定义的使用基于setter方法的注射依赖的bean是真正的JavaBean。 Spring一般提倡使用基于setter方法的依赖注射,因为很多的构造函数参数将会是笨重的, 尤其在有些属性是可选的情况下。
基于构造函数的依 赖注射,它是通过调用带有许多参数的构造方法实现的, 每个参数表示一个合作者或者属性。 另外,调用带有特定参数的静态工厂方法来构造bean可以被认为差不多等同的, 接下来的文字会把构造函数的参数看成和静态工厂方法的参数类似。 虽然Spring一般提倡在大多数情况下使用基于setter的依赖注射, 但是Spring还是完全支持基于构造函数的依赖注射, 因为你可能想要在那些只提供多参数构造函数并且没有setter方法的遗留的bean上使用Spring。 另外对于一些比较简单的bean,一些人更喜欢使用构造函数方法以确保bean不会处于错误的状态。
BeanFactory同时支持这两种方式将依赖注射到被管理bean中。(实际上它还支持在一些依赖已经通过构造函数方法注射后再使用setter方法注射依赖)。依赖的配置是以BeanDefinition的形式出现,它和JavaBeans的PropertyEditors一起使用从而知道如何把属性从一个格式转变为另一个。真正传送的值被封装为PropertyValue对象。然而,大多数Spring的使用者并不要直接(比如编程的方式)处理这些类,而更多地使用一个XML定义文件,这个文件会在内部被转变为这些类的实例,用来读取整个BeanFactory或ApplicationContext。
Bean依赖的决定通常取决于下面这些内容:
BeanFactory通过使用一个描述所有bean的配置被创建和实例化。大多数的Spring用户使用一个支持XML格式配置文件的BeanFactory或ApplicationContext实现。
每一个bean的依赖表现为属性,构造函数参数,或者当用静态工厂方法代替普通构造函数时工厂方法的参数。这些依赖将会在bean真正被创建出来后提供给bean。
每一个属性或者构造函数参数要么是一个要被设置的值的定义,要么是一个指向BeanFactory中其他bean的引用。在ApplicationContext的情况下,这个引用可以指向一个父亲ApplicationContext中bean。
每一个属性或构造函数参数的值,必须能够从(配置文件中)被指定的格式转变为真实类型。缺省情况下,Spring能够把一个字符串格式的值转变为所有内建的类型,比如int, long,String, boolean等等。另外当说到基于XML的BeanFactory实现的时候(包括ApplicationContext实现),它们已经为定义Lists, Maps, Sets和Properties集合类型提供了内在的支持。另外,Spring通过使用JavaBeans的 PropertyEditor定义,能够将字符串值转变为其他任意的类型。(你可以为PropertyEditor提供你自己的PropertyEditor定义从而能够转变你自定义的类型;更多关于PropertyEditors的信息以及如何手工增加自定义的PropertyEditors请参看第 3.9 节 “注册附加的定制PropertyEditor”)。当一个bean属性是一个Java Class类型,Spring允许你用这个类的名字的字符串作为这个属性的值,ClassEditor 这个内建的PropertyEditor会帮你把类的名字转变成真实的Class实例。
很 重要的一点就是:Spring在BeanFactory创建的时候要校验BeanFactory中每一个Bean的配置。这些校验包括作为Bean引用的 属性必须实际引用一个合法的bean(比如被引用的bean也定义在BeanFactory中,或者当ApplicationContext时,在父亲 ApplicationContext中)。但是,bean属性本身直到bean被真实建立的的 时候才被设置。对于那些是singleton并且被设置为pre-instantiated的bean来说(比如一个 ApplicationContext中的singletonbean),bean在创建BeanFactory的时候创建,但是对于其他情况,发生在 bean被请求的时候。当一个bean必须被创建时,它会潜在地导致一系列的其他bean被创建,像它的依赖以及它的依赖的依赖(如此下去)被创建和赋 值。
通常你可以信任Spring做了正确的事情。它会在BeanFactory装载的时候检查出错误,包括对不存在 bean的引用和循环引用。它会尽可能晚地设置属性和解决依赖(比如创建那些需要的依赖),也就是在bean真正被创建的时候。这就意味着:就算一个 BeanFactory被正确地装载,稍后当你请求一个bean的时候,如果创建那个bean或者它的依赖的时候出现了错误,这个BeanFactory 也会抛出一个异常。比如,如果一个bean抛出一个异常作为缺少或非法属性的结果,这样的情况就会发生。这种潜在地推迟一些配置错误可见性的行为正是 ApplicationContext默认预实例化singleton bean的原因。以前期的时间和内存为代价在beans真正需要之前创建它们,你就可以在ApplicationContext创建的时候找出配置错误, 而不是在后来。如果你愿意,你也可以覆盖这种默认的行为,设置这些singleton bean为lazy-load(不是预实例化的)。
几个例子:
首先,一个使用BeanFactory以及基于setter方法的依赖注射。下面是一个定义了一些bean的XmlBeanFactory 配置文件的一小部分。接下去是正式的bean的代码,演示了正确的setter方法声明。
<bean id="exampleBean" class="examples.ExampleBean">
<property name="beanOne"><ref bean="anotherExampleBean"/></property>
<property name="beanTwo"><ref bean="yetAnotherBean"/></property>
<property name="integerProperty"><value>1</value></property>
</bean>
<bean id="anotherExampleBean" class="examples.AnotherBean"/>
<bean id="yetAnotherBean" class="examples.YetAnotherBean"/>public class ExampleBean {
private AnotherBean beanOne;
private YetAnotherBean beanTwo;
private int i;
public void setBeanOne(AnotherBean beanOne) {
this.beanOne = beanOne;
}
public void setBeanTwo(YetAnotherBean beanTwo) {
this.beanTwo = beanTwo;
}
public void setIntegerProperty(int i) {
this.i = i;
}
}正如你所看到的一样,setter方法被声明以符合XML文件中指定的属性。(XML文件中的属性,直接对应着RootBeanDefinition中的PropertyValues对象)
接着是一个使用IoC type3(基于构造函数的依赖注射)的BeanFactory。下面是XML配置中的一段,指定了构造函数参数以及展示构造函数的代码:
<bean id="exampleBean" class="examples.ExampleBean">
<constructor-arg><ref bean="anotherExampleBean"/></constructor-arg>
<constructor-arg><ref bean="yetAnotherBean"/></constructor-arg>
<constructor-arg><value>1</value></constructor-arg>
</bean>
<bean id="anotherExampleBean" class="examples.AnotherBean"/>
<bean id="yetAnotherBean" class="examples.YetAnotherBean"/>public class ExampleBean {
private AnotherBean beanOne;
private YetAnotherBean beanTwo;
private int i;
public ExampleBean(AnotherBean anotherBean, YetAnotherBean yetAnotherBean, int i) {
this.beanOne = anotherBean;
this.beanTwo = yetAnotherBean;
this.i = i;
}
}正如你所看到的,bean定义中指定的构造函数参数将会作为ExampleBean的构造函数参数被传入。
现在考虑一下不用构造函数,而是调用一个静态工厂方法来返回一个对象的实例:
<bean id="exampleBean" class="examples.ExampleBean"
factory-method="createInstance">
<constructor-arg><ref bean="anotherExampleBean"/></constructor-arg>
<constructor-arg><ref bean="yetAnotherBean"/></constructor-arg>
<constructor-arg><value>1</value></constructor-arg>
</bean>
<bean id="anotherExampleBean" class="examples.AnotherBean"/>
<bean id="yetAnotherBean" class="examples.YetAnotherBean"/>public class ExampleBean {
...
// a private constructor
private ExampleBean(...) {
...
}
// a static factory method
// the arguments to this method can be considered the dependencies of the bean that
// is returned, regardless of how those arguments are actually used.
public static ExampleBean ExampleBean(AnotherBean anotherBean,
YetAnotherBean yetAnotherBean, int i) {
ExampleBean eb = new ExampleBean(...);
// some other operations
...
return eb;
}
}需要注意的是:静态工厂方法的参数由 constructor-arg元素提供,这和构造函数的用法是一样的。这些参数是可选的。重要的一点是工厂方法所返回的对象类型不一定和包含这个静态工厂方法的类一致,虽然上面这个例子中是一样的。前面所提到的实例工厂方法(non-static)用法基本上是一样的(除了使用factory-bean属性代替class属性),在这里就不再详细叙述了。 .
3.3.2. 深入Bean属性和构造函数参数
正如前面提到的那样,bean的属性和构造函数参数可以被定义为其他bean的引用(合作者),或者内联定义的值。为了达到这个目的,XmlBeanFactory在property和constructor-arg元素中支持许多子元素类型。
value元素用适合人读的字符串形式指定属性或构造函数参数。正如前面提到的那样,JavaBeans的PropertyEditors被用来将这些字符串从java.lang.String类型转变为真实的属性类型或参数类型。
<beans>
<bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<!-- results in a setDriverClassName(String) call -->
<property name="driverClassName">
<value>com.mysql.jdbc.Driver</value>
</property>
<property name="url">
<value>jdbc:mysql://localhost:3306/mydb</value>
</property>
<property name="username">
<value>root</value>
</property>
</bean>
</beans> null元素被用来处理null值。Spring将porperties等的空参数视为空的字符串。下面这个XmlBeanFactory配置:
<bean class="ExampleBean">
<property name="email"><value></value></property>
</bean> 导致email属性被设置为””,同java代码:exampleBean.setEmail("")等价。而专门的<null>元素则可以用来指定一个null值,所以
<bean class="ExampleBean">
<property name="email"><null/></property>
</bean> 同代码:exampleBean.setEmail(null)是等价的.
list, set, map, 以及 props 元素可以用来定义和设置类型 为Java的List,Set, Map, 和 Properties .
<beans>
...
<bean id="moreComplexObject" class="example.ComplexObject">
<!-- results in a setPeople(java.util.Properties) call -->
<property name="people">
<props>
<prop key="HarryPotter">The magic property</prop>
<prop key="JerrySeinfeld">The funny property</prop>
</props>
</property>
<!-- results in a setSomeList(java.util.List) call -->
<property name="someList">
<list>
<value>a list element followed by a reference</value>
<ref bean="myDataSource"/>
</list>
</property>
<!-- results in a setSomeMap(java.util.Map) call -->
<property name="someMap">
<map>
<entry key="yup an entry">
<value>just some string</value>
</entry>
<entry key="yup a ref">
<ref bean="myDataSource"/>
</entry>
</map>
</property>
<!-- results in a setSomeSet(java.util.Set) call -->
<property name="someSet">
<set>
<value>just some string</value>
<ref bean="myDataSource"/>
</set>
</property>
</bean>
</beans>注意:Map的entry或set的value,它们的值又可以是下面元素中的任何一个:
(bean | ref | idref | list | set | map | props | value | null)在property 元素中定义的bean元素用来定义一个内联的bean,而不是引用BeanFactory其他地方定义的bean。内联bean定义不需要任何id定义
<bean id="outer" class="...">
<!-- Instead of using a reference to target, just use an inner bean -->
<property name="target">
<bean class="com.mycompany.PersonImpl">
<property name="name"><value>Tony</value></property>
<property name="age"><value>51</value></property>
</bean>
</property>
</bean>idref元素完全是一种简写和防止错误的方式,用来设置属性值为容器中其他bean的id 或name。
<bean id="theTargetBean" class="...">
</bean>
<bean id="theClientBean" class="...">
<property name="targetName">
<idref bean="theTargetBean"/>
</property>
</bean>这个在运行时同下面的片段等价:
<bean id="theTargetBean" class="...">
</bean>
<bean id="theClientBean" class="...">
<property name="targetName">
<value>theTargetBean</value>
</property>
</bean>第一种形式比第二种形式更好的原因是:使用idref标记将会使Spring在部署的时候就验证其他的bean是否真正存在;在第二种形式中,targetName属性的类仅仅在Spring实例化这个类的时候做它自己的验证,这很可能在容器真正部署完很久之后。
另外,如果被引用的bean在同一个xml文件中而且bean的名称是bean的 id,那么就可以使用local属性。它会让XML解析器更早,在XML文档解析的时候,验证bean的名称。
<property name="targetName">
<idref local="theTargetBean"/>
</property>ref元素是最后一个能在property元 素中使用的元素。它是用来设置属性值引用容器管理的其他bean(可以叫做合作者)。正如前一节提到的,拥有这些属性的bean依赖被引用的bean,被 引用的bean将会在属性设置前,必要的时候需要时初始化(如果是一个singleton bean可能已经被容器初始化)。所有的引用根本上是一个指向其他对象的引用,不过有3种形式指定被引用对象的id/name,这3种不同形式决定作用域 和如何处理验证。
用ref元素的bean属性指定目标bean是最常见的形式,它允许指向的bean可以在同一个BeanFactory/ApplicationContext(无论是否在同一个XML文件中)中,也可以在父BeanFactory/ApplicationContext中。bean属性的值可以同目标bean的id属性相同,也可以同目标bean的name属性中任何一个值相同。
<ref bean="someBean"/>用local属性指定目标bean可以利用XML解析器的能力在同一个文件中验证XML id引用。local属性的值必须与目标bean的id属性一致。如果在同一个文件中没有匹配的元素,XML解析器将会产生一个错误。因此,如果目标bean在同一个XML文件中,那么使用local形式将是最好的选择(为了能够尽可能早的发现错误)。
<ref local="someBean"/>用parent属性指定目标bean允许引用当前BeanFactory(ApplicationContext)的父BeanFactory(ApplicationContext)中的bean。parent属性的值可以同目标bean的id属性相同,也可以同目标bean的name属 性中的一个值相同,而且目标bean必须在当前BeanFactory(ApplicationContext)的父 BeanFactory(ApplicationContext)中。当需要用某种proxy包装一个父上下文中存在的bean(可能和父上下文中的有同 样的name),所以需要原始的对象用来包装它。
<ref parent="someBean"/>3.3.3. 方法注入
对 于大部分的用户来说,容器中多数的bean是singleton的。当一个singleton的bean需要同另一个singleton的 bean合作(使用)时,或者一个非singleton的bean需要同另一个非singleton的bean合作的时候,通过定义一个bean为另一个 bean的属性来处理这种依赖的关系就足够了。然而当bean的生命周期不同的时候就有一个问题。想想一下一个singleton bean A,或许在每次方法调用的时候都需要使用一个non-singleton bean B。容器仅仅会创建这个singleton bean A一次,因此仅仅有一次的机会去设置它的属性。因此容器没有机会每次去为bean A提供新的bean B的实例。
一个解决这个问题的方法是放弃一些反向控制。Bean A可以通过实现 BeanFactoryAware知道容器的存在(参见这里)),使用编程的手段(参见这里)在需要的时候通过调用getBean("B")来向容器请求新的bean B实例。 因为bean的代码知道Spring并且耦合于Spring,所以这通常不是一个好的方案。
方法注入,BeanFactory的高级特性之一,可以以清洁的方式处理这种情况以及其他一些情况。
3.3.3.1. Lookup方法注入
Lookup 方法注射指容器能够重写容器中bean的抽象或具体方法,返回查找容器中其他bean的结果。 被查找的bean在上面描述的场景中通常是一个non-singleton bean (尽管也可以是一个singleton的)。Spring通过使用CGLIB库在客户端的类之上修改二进制码, 从而实现上述的场景要求。
包含方法注入的客户端类,必须按下面的形式的抽象(具体)定义方法:
protected abstract SingleShotHelper createSingleShotHelper();如果方法不是抽象的,Spring就会直接重写已有的实现。在XmlBeanFactory的情况下,你可以使用bean定义中的lookup-method 属性来指示Spring去注入/重写这个方法,以便从容器返回一个特定的bean。举个例子说明:
<!-- a stateful bean deployed as a protype (non-singleton) -->
<bean id="singleShotHelper class="..." singleton="false">
</bean>
<!-- myBean uses singleShotHelper -->
<bean id="myBean" class="...">
<lookup-method name="createSingleShotHelper"
bean="singleShotHelper"/>
<property>
...
</property>
</bean>当myBean需要一个新的singleShotHelper的实例的时候, 它就会调用它自己的createSingleShotHelper 方法。 值得注意的是:部署beans的人员必须小心地将singleShotHelper作为一个non-singleton部署 (如果确实需要这么做)。如果它作为一个singleton(除非明确说明,否则缺省就是singletion)而部署, 同一个singleShotHelper实例将会每次被返回。
注意Lookup方法注射能够同构造函数注射结合(对创建的bean提供可选的构造函数参数), 也可以同setter方法注射结合(在创建的bean之上设置属性)。
3.3.3.2. 任意方法的替换
另一种方法注射没有lookup方法注入用的多,它用另一个方法实现替换被管理bean的任意一个方法。用户可以放心跳过这一节(这是个有点高级的特性),除非这个功能确实需要。
在一个XmlBeanFactory中,对于一个被部署的bean, replaced-method元素可以用来把已存在的方法实现替换为其他的实现。 考虑如下的类,有一个我们想要重写的computeValue方法:
...
public class MyValueCalculator {
public String computeValue(String input) {
... some real code
}
... some other methods
}需要为新方法定义提供实现 org.springframework.beans.factory.support.MethodReplacer接口的类。
/** meant to be used to override the existing computeValue
implementation in MyValueCalculator */
public class ReplacementComputeValue implements MethodReplacer {
public Object reimplement(Object o, Method m, Object[] args) throws Throwable {
// get the input value, work with it, and return a computed result
String input = (String) args[0];
...
return ...;
}部署原始的类和指定方法重写的BeanFactory部署定义象下面所示的 :
<bean id="myValueCalculator class="x.y.z.MyValueCalculator">
<!-- arbitrary method replacement -->
<replaced-method name="computeValue" replacer="replacementComputeValue">
<arg-type>String</arg-type>
</replaced-method>
</bean>
<bean id="replacementComputeValue" class="a.b.c.ReplaceMentComputeValue">
</bean>replaced-method元素中的一个或多个arg-type 元素用来表示,这个被重载方法的方法签名。注意,参数的签名只有在方法被重载并且该方法有多个不同的形式的时候才真正需要。为了方便,参数的类型字符串可以使全限定名的子字符串。比如,以下的都匹配 java.lang.String。
java.lang.String
String
Str因为参数的个数通常就足够区别不同的可能,所以仅仅使用匹配参数的最短的字符串能够节省很多键入工作。
3.3.4. 使用 depends-on
对于大多数的情况,一个bean被另一个bean依赖,是由这个bean是否被当作其他bean的属性来表达的。 在XmlBeanFactory中,它是通过ref元素来完成的。 与这种方式不同的是,有时一个知道容器的bean仅仅会被给与它所的依赖的id (使用一个字符串值或等价的idref元素)。接着第一个bean就以编程的方式地向容器请求它的依赖。 在两种情况下,被依赖的bean都会在依赖它的bean之前被恰当地初始化。
对于相对罕见的情况,beans之间的依赖不够直接(举例,当一个类中的静态初始块需要被触发,比如数据库驱动的注册) ,depends-on 元素可以用来在初始化使用这个元素的bean之前,强制一个或多个beans初始化。
下面是一个配置的例子:
<bean id="beanOne" class="ExampleBean" depends-on="manager">
<property name="manager"><ref local="manager"/></property>
</bean>
<bean id="manager" class="ManagerBean"/>3.3.5. 自动装配协作对象
BeanFactory能够自动装配合作bean之间的关系。这就意味着,让Spring通过检查BeanFactory的内容来自动装配你的bean的合作者(也就是其他的bean)。自动装配功能有5种模式。自动装配可以指定给每一个bean,因此可以给一些bean使用而其他的bean不自动装配。通过使用自动装配,可以 减少(或消除)指定属性(或构造函数参数)的需要,显著节省键入工作。 [1] 在XmlBeanFactory中,使用bean元素的autowire属性来指定bean定义的自动装配模式。以下是允许的值.
表 3.2. 自动装配模式
模式 解释
no 不使用自动装配。Bean的引用必须通过ref元素定义。这是默认的配置,在较大的部署环境中不鼓励改变这个配置,因为明确的指定合作者能够得到更多的控制和清晰性。从某种程度上说,这也是系统结构的文档形式。
byName 通过属性名字进行自动装配。这个选项会会检查BeanFactory,查找一个与将要装配的属性同样名字的bean 。比如,你有一个bean的定义被设置为通过名字自动装配,它包含一个master属性(也就是说,它有一个setMaster(...)方法),Spring就会查找一个叫做master的bean定义,然后用它来设置master属性。
byType 如果BeanFactory中正好有一个同属性类型一样的bean,就自动装配这个属性。如果有多于一个这样的bean,就抛出一个致命异常,它指出你可能不能对那个bean使用byType的自动装配。如果没有匹配的bean,则什么都不会发生,属性不会被设置。如果这是你不想要的情况(什么都不发生),通过设置dependency-check="objects"属性值来指定在这种情况下应该抛出错误。
constructor 这个同byType类似,不过是应用于构造函数的参数。如果在BeanFactory中不是恰好有一个bean与构造函数参数相同类型,则一个致命的错误会产生。
autodetect 通过对bean 检查类的内部来选择constructor或byType。如果找到一个缺省的构造函数,那么就会应用byType。
注意:显式的指定依赖,比如property和constructor-arg元素,总会覆盖自动装配。自动装配的行为可以和依赖检查结合使用,依赖检查会在自动装配完成后发生。
注意:正如我们已经提到过的,对于大型的应用,自动装配不鼓励使用,因为它去除了你的合作类的透明性和结构。
3.3.6. 依赖检查
对于部署在BeanFactory的bean的未解决的依赖,Spring有能力去检查它们的存在性。 这些依赖要么是bean的JavaBean式的属性,在bean的定义中并没有为它们设置真实的值, 要么是通过自动装配特性被提供。
当 你想确保所有的属性(或者某一特定类型的所有属性)都被设置到bean上面的时候, 这项特性就很有用了。当然,在很多情况下一个bean类的很多属性都会有缺省的值, 或者一些属性并不会应用到所有的应用场景,那么这个特性的作用就有限了 。 依赖检查能够分别对每一个bean应用或取消应用,就像自动装配功能一样。缺省的是不 检查依赖关系。 依赖检查可以以几种不同的模式处理。在XmlBeanFactory中, 通过bean定义中的 dependency-check 属性来指定依赖检查,这个属性有以下的值。
表 3.3. 依赖检查模式
模式 解释
none 不进行依赖检查。没有指定值的bean属性仅仅是没有设值。
simple 对基本类型和集合(除了合作者外,比如其他的bean,所有东西)进行依赖检查。
object 对合作者进行依赖检查。
all 对合作者,基本类型和集合都进行依赖检查。
3.4. 自定义bean的本质特征
3.4.1. 生命周期接口
Spring提供了一些标志接口,用来改变BeanFactory中的bean的行为。 它们包括InitializingBean和DisposableBean。 实现这些接口将会导致BeanFactory调用前一个接口的afterPropertiesSet()方法, 调用后一个接口destroy()方法,从而使得bean可以在初始化和析构后做一些特定的动作。
在内部,Spring使用BeanPostProcessors 来处理它能找到的标志接口以及调用适当的方法。 如果你需要自定义的特性或者其他的Spring没有提供的生命周期行为, 你可以实现自己的 BeanPostProcessor。关于这方面更多的内容可以看这里: 第 3.7 节 “使用BeanPostprocessors定制bean”。
所有的生命周期的标志接口都在下面叙述。在附录的一节中,你可以找到相应的图, 展示了Spring如何管理bean;那些生命周期的特性如何改变你的bean的本质特征以及它们如何被管理。
3.4.1.1. InitializingBean / init-method
实现org.springframework.beans.factory.InitializingBean 接口允许一个bean在它的所有必须的属性被BeanFactory设置后, 来执行初始化的工作。InitializingBean接口仅仅制定了一个方法:
* Invoked by a BeanFactory after it has set all bean properties supplied
* (and satisfied BeanFactoryAware and ApplicationContextAware).
* <p>This method allows the bean instance to perform initialization only
* possible when all bean properties have been set and to throw an
* exception in the event of misconfiguration.
* @throws Exception in the event of misconfiguration (such
* as failure to set an essential property) or if initialization fails.
*/
void afterPropertiesSet() throws Exception;注意:通常InitializingBean接口的使用是能够避免的(而且不鼓励,因为没有必要把代码同Spring耦合起来)。Bean的定义支持指定一个普通的初始化方法。在使用XmlBeanFactory的情况下,可以通过指定init-method属性来完成。 举例来说,下面的定义:
<bean id="exampleInitBean" class="examples.ExampleBean" init-method="init"/>
public class ExampleBean {
public void init() {
// do some initialization work
}
}同下面的完全一样:
<bean id="exampleInitBean" class="examples.AnotherExampleBean"/>
public class AnotherExampleBean implements InitializingBean {
public void afterPropertiesSet() {
// do some initialization work
}
}但却不把代码耦合于Spring。
3.4.1.2. DisposableBean / destroy-method
实现org.springframework.beans.factory.DisposableBean接口允许一个bean, 可以在包含它的BeanFactory销毁的时候得到一个回调。DisposableBean也只指定了一个方法:
/**
* Invoked by a BeanFactory on destruction of a singleton.
* @throws Exception in case of shutdown errors.
* Exceptions will get logged but not rethrown to allow
* other beans to release their resources too.
*/
void destroy() throws Exception;
注意:通常DisposableBean接口的使用能够避免的(而且是不鼓励的,因为它不必要地将代码耦合于Spring)。 Bean的定义支持指定一个普通的析构方法。在使用XmlBeanFactory使用的情况下,它是通过 destroy-method属性完成。 举例来说,下面的定义:
<bean id="exampleInitBean" class="examples.ExampleBean" destroy-method="destroy"/>
public class ExampleBean {
public void cleanup() {
// do some destruction work (like closing connection)
}
}同下面的完全一样:
<bean id="exampleInitBean" class="examples.AnotherExampleBean"/>
public class AnotherExampleBean implements DisposableBean {
public void destroy() {
// do some destruction work
}
}但却不把代码耦合于Spring。
重 要的提示:当以portotype模式部署一个bean的时候,bean的生命周期将会有少许的变化。 通过定义,Spring无法管理一个non-singleton/prototype bean的整个生命周期, 因为当它创建之后,它被交给客户端而且容器根本不再留意它了。 当说起non-singleton/prototype bean的时候,你可以把Spring的角色想象成“new”操作符的替代品。 从那之后的任何生命周期方面的事情都由客户端来处理。BeanFactory中bean的生命周期将会在第 3.4.1 节 “生命周期接口” 一节中有更详细的叙述 .
3.4.2. 了解自己
3.4.2.1. BeanFactoryAware
对于实现了org.springframework.beans.factory.BeanFactoryAware接口的类, 当它被BeanFactory创建后,它会拥有一个指向创建它的BeanFactory的引用。
public interface BeanFactoryAware {
/**
* Callback that supplies the owning factory to a bean instance.
* <p>Invoked after population of normal bean properties but before an init
* callback like InitializingBean's afterPropertiesSet or a custom init-method.
* @param beanFactory owning BeanFactory (may not be null).
* The bean can immediately call methods on the factory.
* @throws BeansException in case of initialization errors
* @see BeanInitializationException
*/
void setBeanFactory(BeanFactory beanFactory) throws BeansException;
}这允许bean可以以编程的方式操控创建它们的BeanFactory, 既可以直接使用 org.springframework.beans.factory.BeanFactory接 口, 也可以将引用强制将类型转换为已知的子类型从而获得更多的功能。这个特性主要用于编程式地取得其他bean。 虽然在一些场景下这个功能是有用的,但是一般来说它应该避免使用,因为它使代码与Spring耦合在一起, 而且也不遵循反向控制的风格(合作者应当作属性提供给bean)。
3.4.2.2. BeanNameAware
如果一个bean实现了org.springframework.beans.factory.BeanNameAware接口, 并且被部署到一个 BeanFactory中,那么BeanFactory就会通过这个接口来调用bean,以便通知这个bean它被部署的id 。 这个回调发生在普通的bean属性设置之后,在初始化回调之前,比如InitializingBean的afterPropertiesSet方法(或者自定义的init- method)。
3.4.3. FactoryBean
接口org.springframework.beans.factory.FactoryBean 一般由本身是工厂类的对象实现。BeanFactory接口提供了三个方法:
Object getObject(): 必须返回一个这个工厂类创建的对象实例。这个实例可以是共享的(取决于这个工厂返回的是singleton还是prototype)。
boolean isSingleton(): 如果Factory返回的对象是singleton,返回true,否则返回false。
Class getObjectType(): 返回getObject()方法返回的对象的类型,如果类型不是预先知道的,则返回null。
3.5. 子bean定义
一 个bean定义可能会包含大量的配置信息,包括容器相关的信息(比如初始化方法,静态工厂方法名等等)以及构造函数参数和属性的值。一个子bean定义是 一个能够从父bean定义继承配置数据的bean定义。 它可以覆盖一些值,或者添加一些其他需要的值。使用父和子的bean定义可以节省很多的输入工作。实际上,这就是一种模版形式。
当以编程的方式使用一个BeanFactory,子bean定义用ChildBeanDefinition类表示。 大多数的用户从来不需要以这个方式使用它们,而是在类似XmlBeanFactory的BeanFactory 中以声明的方式配置bean定义。在一个XmlBeanFactory的bean定义中,使用parent属性指出一个子bean定义,而父bean则作为这个属性的值。
<bean id="inheritedTestBean" class="org.springframework.beans.TestBean">
<property name="name"><value>parent</value></property>
<property name="age"><value>1</value></property>
</bean>
<bean id="inheritsWithDifferentClass" class="org.springframework.beans.DerivedTestBean"
parent="inheritedTestBean" init-method="initialize">
<property name="name"><value>override</value></property>
<!-- age should inherit value of 1 from parent -->
</bean>如果子bean定义没有指定class属性,将使用父定义的class属性,当然也可以覆盖它。 在后面一种情况中,子bean的class属性值必须同父bean的兼容,也就是它必须能够接受父亲的属性值。
一个子bean定义可以从父亲处继承构造函数参数,属性值以及方法,并且可以选择增加新的值。 如果init-method,destroy-method和/或静态factory-method被指定了,它们就会覆盖父亲相应的设置。
剩余的设置将 总是 从子定义处得到: 依赖, 自动装配模式, 依赖检查, singleton, 延迟初始化。
在下面的例子中父定义并没有指定class属性:
<bean id="inheritedTestBeanWithoutClass">
<property name="name"><value>parent</value></property>
<property name="age"><value>1</value></property>
</bean>
<bean id="inheritsWithClass" class="org.springframework.beans.DerivedTestBean"
parent="inheritedTestBeanWithoutClass" init-method="initialize">
<property name="name"><value>override</value></property>
<!-- age should inherit value of 1 from parent -->
</bean>这 个父bean就无法自己实例化;它实际上仅仅是一个纯模版或抽象bean,充当子定义的父定义。 若要尝试单独使用这样的父bean(比如将它作为其他bean的ref属性而引用,或者直接使用这个父 bean的id调用getBean()方法),将会导致一个错误。同样地,容器内部的preInstantiateSingletons方法会完全忽略这种既没有parent属性也没有class属性的bean定义,因为它们是不完整的。
特别注意: 这里并没有办法显式地声明一个bean定义为抽象的。 如果一个bean确实有一个class属性定义,那么它就能够被实例化。而且要注意 XmlBeanFactory默认地将会预实例化所有的singleton的bean。 因此很重要的一点是:如果你有一个(父)bean定义指定了class属性,而你又想仅仅把它当作模板使用, 那么你必须保证将lazy-init属性设置为true(或者将bean标记为non-singleton),否则 XmlBeanFactory(以及其他可能的容器)将会预实例化它。
3.6. BeanFactory之间的交互
BeanFactory 本质上不过是高级工厂的接口,它维护不同bean和它们所依赖的bean的注册。 BeanFactory使得你可以利用 bean工厂读取和访问bean定义。 当你使用BeanFactory的时候,你可以象下面一样创建并且读入一些XML格式的bean定义:
InputStream is = new FileInputStream("beans.xml");
XmlBeanFactory factory = new XmlBeanFactory(is);基本上这就足够了。使用getBean(String)你可以取得你的bean的实例。 如果你将它定义为一个singleton(缺省的)你将会得到同一个bean的引用, 如果你将singleton设置为false,那么你将会每次得到一个新的实例。 在客户端的眼里BeanFactory是惊人的简单。BeanFactory接口仅仅为客户端调用提供了5个方法:
boolean containsBean(String): 如果BeanFactory包含一个与所给名称匹配的bean定义,则返回true
Object getBean(String): 返回一个以所给名字注册的bean的实例。返回一个singleton的共享的实例还是一个新创建的实例, 这取决于bean在BeanFactory配置中如何被配置的。一个BeansException将会在下面两种情况中抛出:bean没有被找到(在这种情况下,抛出的是NoSuchBeanDefinitionException),或者在实例化和准备bean的时候发生异常
Object getBean(String,Class): 返回一个以给定名字注册的bean。返回的bean将会被强制类型转换成给定的Class。 如果bean不能被类型转换,相应的异常将会被抛出(BeanNotOfRequiredTypeException)。 此外getBean(String)的所有规则也同样适用这个方法(同上)
boolean isSingleton(String): 判断以给定名字注册的bean定义是一个singleton还是一个prototype。 如果与给定名字相应的bean定义没有被找到,将会抛出一个异常(NoSuchBeanDefinitionException)
String[] getAliases(String):如果给定的bean名字在bean定义中有别名,则返回这些别名
leexhwhy 发表于:2005.05.08 14:16 ::分类: ( spring aop ) ::阅读:(424次) :: 评论 (0)
===========================================================
Beans, BeanFactory和ApplicationContext
===========================================================
第 3 章 Beans, BeanFactory和ApplicationContext
3.1. 简介
在Spring中,两个最基本最重要的包是 org.springframework.beans 和 org.springframework.context. 这两个包中的代码为Spring的反向控制 特性(也叫作依赖注射)提供了基础。 BeanFactory提供了一种先进的配置机制来管理任何种类bean(对象),这种配置机制考虑到任何一种可能的存储方式。 ApplicationContext建立在BeanFactory之上,并增加了其他的功能,比如更容易同Spring AOP特性整合, 消息资源处理(用于国际化),事件传递,以声明的方式创建ApplicationContext, 可选的父上下文和与应用层相关的上下文(比如WebApplicationContext),以及其他方面的增强。
简而言之,BeanFactory提供了配置框架和基本的功能, 而 ApplicationContext为 它增加了更强的功能,这些功能中的一些或许更加接近J2EE并且围绕企业级应用。一般来说,ApplicationContext是 BeanFactory的完全超集, 任何BeanFactory功能和行为的描述也同样被认为适用于ApplicationContext
用户有时不能确定BeanFactory和ApplicationContext中哪一个在特定场合下更适合。 通常大部分在J2EE环境的应用中,最好选择使用ApplicationContext, 因为它不仅提供了BeanFactory所有的特性以及它自己附加的特性,而且还提供以声明的方式使用一些功能, 这通常是令人满意的。BeanFactory主要是在非常关注内存使用的情况下 (比如在一个每kb都要计算的applet中)使用,而且你也不需要用到ApplicationContext的所有特性。
这一章粗略地分为两部分,第一部分包括对BeanFactory和ApplicationContext都适用的一些基本原则。第二部分包括仅仅适用于ApplicationContext的一些特性
3.2. BeanFactory 和 BeanDefinitions - 基础
3.2.1. BeanFactory
BeanFactory实际上是实例化,配置和管理众多bean的容器。 这些bean通常会彼此合作,因而它们之间会产生依赖。 BeanFactory使用的配置数据可以反映这些依赖关系中 (一些依赖可能不像配置数据一样可见,而是在运行期作为bean之间程序交互的函数)。
一个BeanFactory可以用接口org.springframework.beans.factory.BeanFactory表示, 这个接口有多个实现。 最常使用的的简单的eanFactory实现是org.springframework.beans.factory.xml.XmlBeanFactory。 (这里提醒一下:ApplicationContext是BeanFactory的子类, 所以大多数的用户更喜欢使用ApplicationContext的XML形式)。
虽然大多数情况下,几乎所有被BeanFactory管理的用户代码都不需要知道BeanFactory, 但是BeanFactory还是以某种方式实例化。可以使用下面的代码实例化BeanFactory:
InputStream is = new FileInputStream("beans.xml");
XmlBeanFactory factory = new XmlBeanFactory(is);或者
ClassPathResource res = new ClassPathResource("beans.xml");
XmlBeanFactory factory = new XmlBeanFactory(res);或者
ClassPathXmlApplicationContext appContext = new ClassPathXmlApplicationContext(
new String[] {"applicationContext.xml", "applicationContext-part2.xml"});
// of course, an ApplicationContext is just a BeanFactory
BeanFactory factory = (BeanFactory) appContext;很多情况下,用户代码不需要实例化BeanFactory, 因为Spring框架代码会做这件事。例如,web层提供支持代码,在J2EE web应用启动过程中自动载入一个Spring ApplicationContext。这个声明过程在这里描述:
编程操作BeanFactory将会在后面提到,下面部分将集中描述BeanFactory的配置.
一个最基本的BeanFactory配置由一个或多个它所管理的Bean定义组成。在一个XmlBeanFactory中,根节点beans中包含一个或多个bean元素。
<?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="..." class="...">
...
</bean>
<bean id="..." class="...">
...
</bean>
...
</beans>3.2.2. BeanDefinition
一个XmlBeanFactory中的Bean定义包括的内容有:
classname:这通常是bean的真正的实现类。但是如果一个bean使用一个静态工厂方法所创建而不是被普通的构造函数创建,那么这实际上就是工厂类的classname
bean行为配置元素:它声明这个bean在容器的行为方式(比如prototype或singleton,自动装配模式,依赖检查模式,初始化和析构方法)
构造函数的参数和新创建bean需要的属性:举一个例子,一个管理连接池的bean使用的连接数目(即可以指定为一个属性,也可以作为一个构造函数参数),或者池的大小限制
和这个bean工作相关的其他bean:比如它的合作者(同样可以作为属性或者构造函数的参数)。这个也被叫做依赖。
上面列出的概念直接转化为组成bean定义的一组元素。这些元素在下面的表格中列出,它们每一个都有更详细的说明的链接。
表 3.1. Bean定义的解释
特性 详细说明
class 第 3.2.3 节 “bean的类”
id和name 第 3.2.4 节 “Bean的标志符 (id与name)”
singleton或prototype 第 3.2.5 节 “Singleton的使用与否”
构造函数参数 第 3.3.1 节 “设置bean的属性和合作者”
bean的属性 第 3.3.1 节 “设置bean的属性和合作者”
自动装配模式 第 3.3.5 节 “自动装配协作对象”
依赖检查模式 第 3.3.6 节 “依赖检查”
初始化模式 第 3.4.1 节 “生命周期接口”
析构方法 第 3.4.1 节 “生命周期接口”
注意bean定义可以表示为真正的接口org.springframework.beans.factory.config.BeanDefinition以及它的各种子接口和实现。然而,绝大多数的用户代码不需要与BeanDefination直接接触。
3.2.3. bean的类
class属性通常是强制性的(参考第 3.2.3.3 节 “通过实例工厂方法创建bean”和第 3.5 节 “子bean定义”),有两种用法。在绝大多数情况下,BeanFactory直接调用bean的构造函数来"new"一个bean(相当于调用new的Java代码),class属性指定了需要创建的bean的类。 在比较少的情况下,BeanFactory调用某个类的静态的工厂方法来创建bean, class属性指定了实际包含静态工厂方法的那个类。 (至于静态工厂方法返回的bean的类型是同一个类还是完全不同的另一个类,这并不重要)。
3.2.3.1. 通过构造函数创建bean
当 使用构造函数创建bean时,所有普通的类都可以被Spring使用并且和Spring兼容。 这就是说,被创建的类不需要实现任何特定的接口或者按照特定的样式进行编写。仅仅指定bean的类就足够了。 然而,根据bean使用的IoC类型,你可能需要一个默认的(空的)构造函数。
另外,BeanFactory并不局限于管理真正的JavaBean,它也能管理任何你 想让它管理的类。虽然很多使用Spring的人喜欢在BeanFactory中用真正的JavaBean (仅包含一个默认的(无参数的)构造函数,在属性后面定义相对应的setter和getter方法),但是在你的BeanFactory中也可以使用特殊 的非bean样式的类。 举例来说,如果你需要使用一个遗留下来的完全没有遵守JavaBean规范的连接池, 不要担心,Spring同样能够管理它。
使用XmlBeanFactory你可以像下面这样定义你的bean class:
<bean id="exampleBean"
class="examples.ExampleBean"/>
<bean name="anotherExample"
class="examples.ExampleBeanTwo"/> 至于为构造函数提供(可选的)参数,以及对象实例创建后设置实例属性,将会在后面叙述
3.2.3.2. 通过静态工厂方法创建Bean
当你定义一个使用静态工厂方法创建的bean,同时使用class属性指定包含静态工厂方法的类,这个时候需要factory-method属性来指定工厂方法名。Spring调用这个方法(包含一组可选的参数)并返回一个有效的对象,之后这个对象就完全和构造方法创建的对象一样。用户可以使用这样的bean定义在遗留代码中调用静态工厂。
下面是一个bean定义的例子,声明这个bean要通过factory-method指定的方法创建。注意这个bean定义并没有指定返回对象的类型,只指定包含工厂方法的类。在这个例子中,createInstance 必须是static方法 .
<bean id="exampleBean"
class="examples.ExampleBean2"
factory-method="createInstance"/>至于为工厂方法提供(可选的)参数,以及对象实例被工厂方法创建后设置实例属性,将会在后面叙述.
3.2.3.3. 通过实例工厂方法创建bean
使用一个实例工厂方法(非静态的)创建bean和使用静态工厂方法非常类似,调用一个已存在的bean(这个bean应该是工厂类型)的工厂方法来创建新的bean。
使用这种机制,class属性必须为空,而且factory-bean属性必须指定一个bean的名字,这个bean一定要在当前的bean工厂或者父bean工厂中,并包含工厂方法。 而工厂方法本身仍然要通过factory-method属性设置。
下面是一个例子:
<!-- The factory bean, which contains a method called
createInstance -->
<bean id="myFactoryBean"
class="...">
...
</bean>
<!-- The bean to be created via the factory bean -->
<bean id="exampleBean"
factory-bean="myFactoryBean"
factory-method="createInstance"/>虽然我们要在后面讨论设置bean的属性,但是这个方法意味着工厂bean本身能够被容器通过依赖注射来管理和配置
3.2.4. Bean的标志符 (id与name)
每一个bean都有一个或多个id(也叫作标志符,或名字;这些名词说的是一回事)。这些id在管理bean的BeanFactory或ApplicationContext中必须是唯一的。 一个bean差不多总是只有一个id,但是如果一个bean有超过一个的id,那么另外的那些本质上可以认为是别名。
在 一个XmlBeanFactory中(包括ApplicationContext的形式), 你可以用id或者name属性来指定bean的id(s),并且在这两个或其中一个属性中至少指定一个id。 id属性允许你指定一个id,并且它在XML DTD(定义文档)中作为一个真正的XML元素的ID属性被标记, 所以XML解析器能够在其他元素指回向它的时候做一些额外的校验。正因如此,用id属性指定bean的id是一个比较好的方式。 然而,XML规范严格限定了在XML ID中合法的字符。通常这并不是真正限制你, 但是如果你有必要使用这些字符(在ID中的非法字符),或者你想给bean增加其他的别名, 那么你可以通过name属性指定一个或多个id(用逗号,或者分号;分隔)。
3.2.5. Singleton的使用与否
Beans被定义为两种部署模式中的一种:singleton或non-singleton。 (后一种也别叫作prototype,尽管这个名词用的不精确因为它并不是非常适合)。 如果一个bean是singleton形态的,那么就只有一个共享的实例存在, 所有和这个bean定义的id符合的bean请求都会返回这个唯一的、特定的实例。
如果bean以non-singleton,prototype模式部署的话,对这个bean的每次请求都会创建一个新的bean实例。这对于例如每个user需要一个独立的user对象这样的情况是非常理想的。
Beans 默认被部署为singleton模式,除非你指定。要记住把部署模式变为non-singletion(prototype)后,每一次对这个bean的 请求都会导致一个新创建的bean,而这可能并不是你真正想要的。所以仅仅在绝对需要的时候才把模式改成prototype。
在下面这个例子中,两个bean一个被定义为singleton,而另一个被定义为non-singleton(prototype)。客户端每次向BeanFactory请求都会创建新的exampleBean,而AnotherExample仅仅被创建一次;在每次对它请求都会返回这个实例的引用。
<bean id="exampleBean"
class="examples.ExampleBean" singleton="false"/>
<bean name="yetAnotherExample"
class="examples.ExampleBeanTwo" singleton="true"/>注 意:当部署一个bean为prototype模式,这个bean的生命周期就会有稍许改变。 通过定义,Spring无法管理一个non-singleton/prototype bean的整个生命周期, 因为当它创建之后,它被交给客户端而且容器根本不再跟踪它了。当说起non-singleton/prototype bean的时候, 你可以把Spring的角色想象成“new”操作符的替代品。从那之后的任何生命周期方面的事情都由
上一篇: Spring学习笔记之Bean基本管理(BeanFactory,ApplicationContext
下一篇: ibatis The error occurred while applying a parameter map
推荐阅读
-
Spring 的 BeanFactory 和 FactoryBean 傻傻分不清?
-
BeanFactory和IOC控制反转
-
2021-08-10spring boot启动报错Error starting ApplicationContext(application和前端问题)
-
为什么大多数IOC容器使用ApplicationContext,而不用BeanFactory
-
Spring学习笔记之Bean基本管理(BeanFactory,ApplicationContext
-
Beans, BeanFactory和ApplicationContext
-
Spring框架源码分析(IoC):BeanFactory和ApplicationContext容器家族
-
Spring5源码 - 08 BeanFactory和FactoryBean 源码解析 & 使用场景
-
使用SSM框架开发applicationContext.xml和jdbc.properties配置文件的使用
-
Spring中BeanFactory和ApplicationContext的作用和区别(推荐)