3.1 Bean基本原理
BeanFactory 负责读取Bean的定义文件:管理对象的加载,生成,维护,与对象之间的依赖关系。提供的功能比较简单。
ApplicationContext 提供一些特色以及高级的容器功能。大概有3个实现类:
l FileSystemXmlApplicationContext
l ClassPathXmlApplicationContext
l XmlWebApplicationContext
当需要多个Bean定义文件,Spring建议使用ApplicationContext的方法读取,好处
Bean定义文件之间嗜独立的。
u 一个替代的方式是使用<import>标签,如:
<beans …>
<import resource=”dao-config.xml”/>
<import resource=”resources/messageSource.xml”/>
……
</beans>
u 另外的方法如下:
ApplicationContext context = new ClassPathXmlApplicationContext(
new String[]{“beans-config.xml”, ”beans2-config.xml”});
使用file:/,classpath:甚至http://等url前致,或者classpath*: 表示
classpath前致路径都匹配。
ApplicationContext context =
new ClassPathXmlApplicationContext(“classpath*:beans-config.xml”);
制定*字符,以下的例子可读取ClassPath下所有以”beans”开头的xml配置文件,但耀注意的是此方法只在实际的文件系统中有用,如果是.jar文件,以下的指定无效:
ApplicationContext context = new ClassPathXmlApplicationContext(“beans*.xml”);
XmlWebApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(this.getServletConfig().getServletContext());
l Bean的实例化
1) 最简单的最基本的,也就是没有参数的构造方法。
<bean id=”writer” class=”com.test.FloppyWriter”/>
2) 有构造方法的,代码如下:
public class HelloBean {
private String name;
private String helloWorld;
public HelloBean(){}
public HelloBean(String name, String helloWorld) {
this.name = name;
this.helloWorld = helloWorld;
}
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getHelloWorld() { return helloWorld; }
public void setHelloWorld(String hel) { this.helloWorld = hel; }
public static void main(String args[]) {
AbstractApplicationContext context =
new ClassPathXmlApplicationContext("beans.xml");
HelloBean hello = (HelloBean)context.getBean("hellBean");
System.out.println(hello.getHelloWorld()+","+hello.getName());
}
}
<bean id="hellBean" class="com.spring.ch3.HelloBean">
<constructor-arg index="0">
<value>大家好</value>
</constructor-arg>
<constructor-arg index="1">
<value>Hello</value>
</constructor-arg>
</bean>
3) 通过静态构造方法来取得某个对象,好处是调用静态工厂方法的对象不用了解对象建立的细节,例子如下:
interface IMusicBox {public void play();}
class MusicBoxFactory {
public static IMusicBox createMusicBox() {
return new IMusicBox() {
public void play() {
System.out.println("弹奏吉他的声音...");
}
};
}
public static void main(String args[]) {
AbstractApplicationContext context =
new ClassPathXmlApplicationContext("beans.xml");
IMusicBox box = (IMusicBox)context.getBean("musicbox");
Box.play();
}
}
<bean id="musicbox" class="com.spring.ch3.MusicBoxFactory"
factory-method="createMusicBox" />
或者
问题 <bean id="factoryBean" class="com.spring.ch3.MusicBoxFactory" /> <bean id="musicbox" factory-bean="factoryBean" factory-method="createMusicBox" /> 为什么不行 |
l Bean的scope
Spring中取得的实例默认为Singleton,也就是默认每一个Bean名称只维持一个实例。也就是说getBean(“xxx”)的时候实际上都是同一个对象。使用Singleton模式产生的单一实例,对单线程的程序来说不会有什么问题,单对于多线程的程序,必须注意Thread-safe的问题,防止多个线程同时存取公用资源所引发的问题,通常Singleton的Bean都是无状态的(Stateless)。可以通过设置,每次从BeanFactory或者ApplicationContext中制定别名取得Bean都产生一个新的实例,如:
<bean id=”helloBean” class=”XXXXX” scope=”prototype”>
...
</bean>
在Spring中,”scope”属性预设为”singleton”,通过将其设置为”prototype”,使得每次制定名称来取得Bean时,都会产生一个新的实例; 也可以设置<bean>的”singleton”属性为”true”或”false”,来设置是否Singleton的方式来产生实例,不过这个主要是为了和前面的版本兼容而保留的。在Spring中,”Scope”除了可以设置为”singleton”和”prototype”,
针对Web应用程序,还可以设置”request”,”session”,”GlobalSession”,分别表示请求阶段,会话阶段,和基于Protlet的Web应用程序会话阶段.
l Bean的生命周期
一个Bean从建立到销毁,会经历几个执行阶段,如果使用BeanFactoyr来生成,管理Bean,会尽量支持以下的生命周期。
1) Bean的建立
由BeanFactory读取定义Bean定义文件,并生成各个Bean实例。
2) 属性注入
执行相关的Bean属性依赖注入。
3) BeanNameAware的setBeanName()
如果Bean类有实现BeanNameAware接口,则执行它的setBeanName()。
4) BeanFactoryAware的setBeanFactory()
如果Bean类有实现BeanFactoryAware接口,则执行它的setBeanFactory()。
5) BeanPostProcessors的processBeforeInitialization()。
如果有任何的BeanPostProcessors实例与Bean实例关联,则执行BeanPostProcessors实例的processBeforeInitialization()方法。
6) InitializingBean的afterPropertiesSet()
如果Bean有实现InitializingBean,则执行他的afterPropertiesSet()方法。
7) Bean定义文件中的init-method
可以在Bean定义文件中使用”init-method”属性设置方法名称,如:
...
<bean id=”helloBean” class=”XXXXX” init-method=”initBean”>
...
8) BeanPostProcessors的processaAfterInitialization()
如果有任何的BeanPostProcessors实例与Bean实例关联,则执行BeanPostProcessors实例的processaAfterInitialization()方法。
9) DisposableBean的destroy()
在容器关闭的时候,如果Bean类有实现DisposableBean接口,则执行他的destroy()方法。
10) Bean定义文件中定义的destroy-method
在容器关闭时,可以在Bean定义文件使用”destroy-method”属性设置方法名称,如:
...
<bean id=”helloBean” class=”XXXXX” destroy-method=”destroyBean”>
...
如果有以上的设置,当代码运行到这个阶段的时候,就会执行的destroyBean方法。
定义<bean>的”init-method”属性,其实与实现InitializingBean的afterPropertiesSet()意义是相同的。采用前者,可以定义Bean的初始化方法而不用耦合至Spring的API。定义<bean>的”destroy-method”属性,与实现DisposableBean接口的destroy()方法的意义也是相同的。
11) 另外
可以在<beans>上定义”default-init-method”和”default-destroy-method”属性,Spring会自动执行每个Bean上所定义的init()和destroy()方法,例如:
<beans default-init-method=”init” default-destroy-method=”destroy”>
...
</beans>
如果使用BeanFactory,只用在使用getBean()取得Bean时,才会做实例化的动作。如果使用ApplicationContext,则会预先针对Bean定义文件的内容,将所有的Bean实例化。在<bean>上设置”lazy-int”属性为”true”,ApplicationContext 就不会在启动针对该Bean做实例化动作,例如:
<bean id=”helloBean” class=”XXXXX” lazy-init=”true”>
...
</bean>
l Bean的生命周期
如果Bean定义文件的内容不断的增加,而你发现有些Bean的定义其实有所重复。如,有好几个Bean定义都有”name”和”age”等属性,而大部分设置都是相同的值,只有几个Bean会有不同的设置,则可以考虑继承某个Bean定义,这样可以省去许多设置的功夫。
class SomeBean { private String name; private int age; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public static void main(String args[]) { AbstractApplicationContext context = new ClassPathXmlApplicationContext("beans.xml"); SomeBean bean = (SomeBean)context.getBean("some"); System.out.println("SomeBean->"+bean.getName()); System.out.println("SomeBean->"+bean.getAge()); } } <bean id="inheritedSomeBean" abstract="true"> <property name="name"><value>Guest</value></property> <property name="age"><value>18</value></property> </bean> <bean id="some" class="com.spring.ch2.SomeBean" parent="inheritedSomeBean"> <property name="name"><value>测试人员</value></property> </bean> 运行结果为: SomeBean->测试人员 SomeBean->18 |
3.2 Bean的依赖设置
当构造方法上的参数个数相同时,Spring会自动解析构造方法上的参数类型及所设置的依赖注入,用来决定要使用哪个构造方法,例如:
public class HelloBean { private String name; private Date date; public HelloBean(Date date) { this.date = date; } public HelloBean(String name) { this.name = name; } public HelloBean(String name, Date date) { this.name = name; this.date = date; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Date getDate() { return date; } public void setDate(Date date) { this.date = date; } public static void main(String args[]) { AbstractApplicationContext context = new ClassPathXmlApplicationContext("beans.xml"); HelloBean bean = (HelloBean)context.getBean("helloBean"); System.out.println("SomeBean->"+bean.getName()); System.out.println("SomeBean->"+bean.getDate().toString()); } } <bean id="date" class="java.util.Date"/> <bean id="helloBean" class="com.spring.ch3.HelloBean"> <constructor-arg><ref bean="date"/></constructor-arg> </bean> 运行结果为: SomeBean->null SomeBean->Thu Aug 21 11:33:12 CST 2008
可以设置”type”属性来明确制定要使用哪个类型,如: <bean id="date" class="java.util.Date"/> <bean id="helloBean" class="com.spring.ch3.HelloBean"> <constructor-arg type=”java.util.Date”><ref bean="date"/></constructor-arg> </bean> |
l 依赖的值设置与参考
1) 设置例子1:
<bean id="helloBean2" class="com.spring.ch3.HelloBean">
<constructor-arg><value>大家好</value></constructor-arg>
<property name="date"><null/></property>
</bean>
注意: <property name="date"><value></value></property>是把属性设置为空字符串,而不是设这为null
2) 设置例子2:
<bean id="helloBean2" class="com.spring.ch3.HelloBean">
<constructor-arg value=”大家好”></constructor-arg>
<property name="name" value=”XXX”></property>
</bean>
3) 如果Bean定义文件中已经有一个定义的Bean实例,则可以直接让某个属性参考这个实例,例如:
<bean id=”helloBean” class=”XXXXX”>
<constructor-arg><ref bean=”date”/></constructor-arg>
<property name=”other”><ref bean=”otherBean”/></property>
</bean>
另一个比较简介的写法,使用”ref”属性来制定,例如:
<bean id=”helloBean” class=”XXXXX”/>
<constructor-arg ref=”date”/>
<property name=”other” ref=”otherBean”/>
</bean>
4) 如果希望使用<ref>参考其他Bean实例时,所定义的Bean必须是同一个设置文件中,且可以指定”local”属性,例如:
<bean id=”helloBean”>
<property name=”other”><ref local=”otherBean”/></property>
</bean>
5) 如果某个Bean实例只被某个属性参考过一次,之后在定义文件中再也不被参考,则可以直接在属性定义的时候使用<Bean>标签,并且需要指定其”class”属性即可,例如:
<bean id=”helloBean” class=”XXXXX”>
<property name=”helloWorld” value=”hello”></property>
<property name=”date”><bean class=”java.util.Date”/></property>
</bean>
6) 在取得某个Bean之前,如果它依赖于另一个Bean,Spring就会先去实例化被依赖的Bean并进行依赖注入。如果某个Bean在生成之前要求另一个Bean必须先实例化,则可以指定”depends-on”属性来设置,如果有2个以上的Bean要设置在”depends-on”中,则以都好隔开,例如:
<bean id=”beanOne” class=”XXXXX” depends-on=”beanTwo,beanThree”/>
<bean id=”beanTwo” class=”XXXXX”/>
<bean id=”beanThree” class=”XXXXX”/>
l 自动绑定
除了在Bean定义文件中使用<value>指定字符串以及基本类型值,使用<ref>直接指定参考其他Bean的实例,或是使用<bean>标签指定”class”属性,用来指定依赖对象外,Spring也支持隐式的自动绑定。可以通过(byType)或者名称(byName),将某个Bean实例绑定到其他Bean对应的属性上,例如:
public class HelloBean { private String name; private Date date; public HelloBean() {} public HelloBean(Date date) { this.date = date; } public HelloBean(String name) { this.name = name; } public HelloBean(String name, Date date) { this.name = name; this.date = date; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Date getDate() { return date; } public void setDate(Date date) { this.date = date; } public static void main(String args[]) { AbstractApplicationContext context = new ClassPathXmlApplicationContext("beans.xml"); HelloBean bean = (HelloBean)context.getBean("helloBean"); System.out.println("SomeBean->"+bean.getName()); System.out.println("SomeBean->"+bean.getDate().toString()); } } <bean id="date" class="java.util.Date"/> <bean id="helloBean" class="com.spring.ch3.HelloBean" autowire="byType"> <property name="name" value="哈哈呵呵"></property> </bean> 运行结果为: SomeBean->哈哈呵呵 SomeBean->Thu Aug 21 13:30:15 CST 2008 |
在定义文件中,并没有指定”helloBean”的”date”属性,而是通过自动绑定,在”autowire”属性上指定了”byType”,所以会根据”helloBean”的setDate()方法所接受的类型来判断在Bean定义文件中是否定义了累世的类型对象,并将之设置给”helloBean”的setDate()。使用自动绑定时,如果”byType”无法完成绑定,则丢出异常。
可以通过指定”byName”来绑定,则Spring会判断Bean定义时的”id”属性上指定的别名与Setter名称是否一致来进行自动绑定。如果使用”byName”无法完成绑定,则对应的Setter仅保持未绑定的状态。
也可以在使用Type3 Dependency Injection时套用自动绑定,也就是在构造方法上也可以尝试自动绑定,实质上是根据byType来绑定,例如:
<bean id="test" class="java.util.Date"/> <bean id="helloBean" class="com.spring.ch3.HelloBean" autowire="constructor"> <property name="name" value="哈哈呵呵"></property> </bean> |
如果还想再偷懒,可以设置未”autodetect”,一切都让spring来判断。当”autowire”被设置未”autodected”来处理依赖关系的时候,Spring会先尝试”construtor”,如果没有能建立依赖关系,则在尝试”byType”的方式来建立依赖关系。
在以上介绍的隐式自动绑定中,由于没有办法从定义文件中清楚看到是否每个属性都完成了设置,为了确定某些依赖关系确实建立,可以建立依赖检查,在<bean>标签使用设置”dependency-check”,可以有4种依赖检查方式:”simple”, “objects”, “all”, “none”。
”simple”只检查简单的属性是否完成依赖关系,像是原生(primitive)数据类型或字符串对象;”objects”则检查对象类型的属性是否完成依赖关系;”all”检查全部的属性是否完成依赖关系;”none”是默认值,表示不检查依赖性。例如:
<bean id="test" class="java.util.Date"/> <bean id="helloBean" class="com.spring.ch3.HelloBean" autowire="constructor" dependency-check="all"> <property name="name" value="哈哈呵呵"></property> <!—如果该属性没有设置的话,则会有异常 --> <property name="date" ref="test"></property> </bean> |
l 集合对象
对于数组,List, Set, Map等集合对象,在注入前必须填充入一些对象到集合中,然后再将集对象注入
所需要的Bean,也可以由Spring的Ioc容器来自动维护或者生成结合对象,并完成依赖注入。
这里直接举个完成的应用程序做示范,例如有个SomeBean类定义,如下所示:
public class SomeBean { private String[] someStrArr; private Some[] someObjArr; private List<Object> someList; private Set<Object> someSet; private Map<Object, Object> someMap; private Properties properties; public Properties getProperties() { return properties; } public void setProperties(Properties properties) { this.properties = properties; } public String[] getSomeStrArr() { return someStrArr; } public void setSomeStrArr(String[] someStrArr) { this.someStrArr = someStrArr; } public Some[] getSomeObjArr() { return someObjArr; } public void setSomeObjArr(Some[] someObjArr) { this.someObjArr = someObjArr; } public List<Object> getSomeList() { return someList; } public void setSomeList(List<Object> someList) { this.someList = someList; } public Map<Object, Object> getSomeMap() { return someMap; } public void setSomeMap(Map<Object, Object> someMap) { this.someMap = someMap; } public Set<Object> getSomeSet() { return someSet; } public void setSomeSet(Set<Object> someSet) { this.someSet = someSet; } public static void main(String args[]) { AbstractApplicationContext context = new ClassPathXmlApplicationContext("beans.xml"); SomeBean bean = (SomeBean)context.getBean("someBean"); System.out.println(bean.getSomeList().getClass()); System.out.println(bean.getSomeObjArr()[0].getName()); System.out.println(bean.getSomeMap().get("some2")); Iterator<Object> iter = bean.getSomeSet().iterator(); while (iter.hasNext()) { System.out.println(iter.next()); } System.out.println(bean.getProperties().get("key1")); } } <bean id="some1" class="com.spring.ch3.Some"> <property name="name" value="张三"></property> </bean> <bean id="some2" class="com.spring.ch3.Some"> <property name="name" value="李四"></property> </bean> <bean id="someBean" class="com.spring.ch3.SomeBean"> <property name="someStrArr"> <list> <value>Hello</value> <value>Welcome</value> </list> </property> <property name="someObjArr"> <list> <ref bean="some1"/> <ref bean="some2"/> </list> </property> <property name="someList"> <list> <value>ListTest</value> <ref bean="some1"/> <ref bean="some2"/> </list> </property> <property name="someMap"> <map> <entry key="MapTest" value="Hello!张三"/> <entry key="someKey1"> <ref bean="some1"/> </entry> <entry> <key><ref bean="some1"/></key> <ref bean="some2"/> </entry> <entry key-ref="some2" value="some1"></entry> <entry key="some2" value="some1"></entry> </map> </property> <property name="someSet"> <set> <value>a set element</value> <ref bean="some1"/> <ref bean="some1"/> </set> </property> <property name="properties"> <props> <prop key="key1">keyValue1</prop> <prop key="key2">keyValue2</prop> </props> </property> </bean> |
如果集合对象不只注入一个对象,则要考虑为集合对象设置”id”名称,例子如下:
Ø 如果是List对象 <bean id="emailsList" class="org.springframework.beans.factory.config.ListFactoryBean"> <property name="sourceList"> <list> <value>[email protected]</value> <value>[email protected]</value> </list> </property> </bean> Ø 如果是Map对象 <bean id="emailsMap" class="org.springframework.beans.factory.config.MapFactoryBean"> <property name="sourceMap"> <map> <entry key="a" value="[email protected]"></entry> <entry key="b" value="[email protected]"></entry> </map> </property> </bean> Ø 如果是Set对象 <bean id="emailsSet" class="org.springframework.beans.factory.config.SetFactoryBean"> <property name="sourceSet"> <set> <value>[email protected]</value> <value>[email protected]</value> </set> </property> </bean> Ø 如果是Properties对象 <bean id="emailsProperties" class="org.springframework.beans.factory.config.PropertiesFactoryBean"> <property name="properties"> <props> <prop key="a">[email protected]</prop> <prop key="b">[email protected]</prop> </props> </property> </bean> Ø 使用"location"属性,指定.properties文件的位置,从中读取Properties资料 <bean id="businessConfig" class="org.springframework.beans.factory.config.PropertiesFactoryBean"> <property name="location" value="classpath:com/spring/config.properties"></property> </bean> Ø Spring2.0中,还支持集合对象的合并,和Bean的继承类似 <bean id="parent" abstract="true" class="com.spring.ch3.Some"> <property name="someProperties"> <props> <prop key="key1">keyValue1</prop> <prop key="key2">keyValue2</prop> </props> </property> </bean> <bean id="child" parent="parent"> <property name="someProperties"> <props merge="true"> <prop key="key3">keyValue3</prop> <prop key="key4">keyValue4</prop> </props> </property> </bean> |
l Spring中的<util>标签
在Spring2.0中如果使用基于XML Schema的XML定义文件进行设置,则可以加入新增的<util>标签扩充。
<util>标签在设置Bean定义时更为方便,对于XML配置文件的简化很有帮助,要使用<util>标签,必须在XML中加入util 名称空间(namespace):
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:util="http://www.springframework.org/schema/util" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-2.5.xsd " default-init-method="init" default-destroy-method="destroy"> |
例如上面的SomeBean的例子可以定义为:
<bean id="some1" class="com.spring.ch3.Some"> <property name="name" value="张三"></property> </bean> <bean id="some2" class="com.spring.ch3.Some"> <property name="name" value="李四"></property> </bean> <util:list id="strArr"> <value>你们好</value> <value>欢迎</value> </util:list> <util:list id="objArr"> <ref bean="some1"/> <ref bean="some2"/> </util:list> <util:list id="list" list-class="java.util.ArrayList"> <value>ListTest</value> <ref bean="some1"/> <ref bean="some2"/> </util:list> <util:map id="map" map-class="java.util.HashMap"> <entry key="mapTest" value="你好!日本"></entry> <entry key="someKey1" value="你好!猪"></entry> </util:map> <util:properties id="property"> <prop key="a">[email protected]</prop> <prop key="b">[email protected]</prop> </util:properties> <util:set id="set" set-class="java.util.HashSet"> <value>set1</value> <value>set2</value> </util:set> <bean id="someBean2" class="com.spring.ch3.SomeBean"> <property name="someStrArr" ref="strArr"></property> <property name="someObjArr" ref="objArr"></property> <property name="someList" ref="list"></property> <property name="someMap" ref="map"></property> <property name="someSet" ref="set"></property> <property name="properties" ref="property"></property> </bean> |
除了这里介绍的<util>标签之外,还有<util:constant>可用来把Bean的某个成员设置成为其他类的静态变量/常量,而免于设置FieldRetrievingFactoryBean,如(把ERROR_MESSAGE给testStaticSet赋值):
<property name="testStaticSet"> <util:constant static-field="javax.swing.JOptionPane.ERROR_MESSAGE"/> </property> |
还可以用<util:property-path>标签为某个Bean的属性成员设置”id”名称(也就是说把某个bean的成员拿出来当作单独的bean以供调用),免于设置PropertyPathFactoryBean,例如:
<util:property-path id="testStaticSet" path="someBean2.testStaticSet"/> 调用如下: System.out.println(">>>"+context.getBean("testStaticSet")); |
l Lookup Method Injection
假设现在要设计一个Singleton的MessageManager,当调用display()方法时,会取得一个系统新建立的Message对象并显示,例如:
public abstract class MessageManager { public void display() { Message message = createMessage(); System.out.println(message); } protected abstract Message createMessage(); public static void main(String args[]) { AbstractApplicationContext context = new ClassPathXmlApplicationContext("beans.xml"); MessageManager manger = (MessageManager)context.getBean("messageManager"); manger.display(); } }class Message { private String sysMessage; public Message() { sysMessage = "系统信息: " + new Date().toString(); } public String toString() { return sysMessage; } } <bean id="sysMessage" class="com.spring.ch3.Message" scope="prototype"></bean> <bean id="messageManager" class="com.spring.ch3.MessageManager"> <lookup-method name="createMessage" bean="sysMessage"/> </bean> 执行结果: 系统信息: Fri Aug 22 11:38:36 CST 2008 |
3.3 Bean高级管理
l 非XML定义文件的配置方式(忽略)
l Aware相关接口
Spring中提供一些Aware相关接口,实现这些Aware接口的Bean类在被初始之后,可以取得一些Spring所提供的资源或使用某些功能。如:
Ø org.springframework.beans.factory.BeanNameAware
如果实现了BeanNameAware接口的Bean类,在设置依赖关系之后、初始化方法之前(如InitializingBean的afterPropertiesSet()方法或者自定义的init方法),会将Bean在定义文件中的名称通过setBeanName()方法设置给Bean。
Ø org.springframework.beans.factory.BeanFactoryAware
如果实现了BeanFactoryAware接口的Bean类,在设置依赖关系后、初始化方法之前,Spring容器将会注入BeanFactory的实例。
Ø org.springframework.context.ApplicationContextAware
如果实现了ApplicationContextAware接口的Bean类,在Bean类被初始后,将会被注入ApplicationContext的实例。
Ø org.springframework.context.ResourceLoaderAware
如果实现了该接口,可以让Bean取得ResourceLoader实例,并进一步取得相关的资源文件。
像这样用来取得Spring资源的Bean,设计上应该说是Spring框架的一部分,是应用程序与Spring框架的桥梁。
l BeanPostProcessor
在Bean的依赖关系由Spring容器建立并设置之后,还有机会定义一些Bean的修正动作来修正相关的属性,方法是让Bean类实现org.springframework.beans.factory.config.BeanPostProcessor接口。(注意:该类会对定义文件中所有的Bean都进行该接口中的方法调用,使用时请小心)
该接口定义了如下的2个方法:
Ø postProcessBeforeInitialization()
该方法会在Bean类被初始化之前(如InitializingBean的afterPropertiesSet()方法或者自定义的init方法)被执行。
Ø postProcessAfterInitialization()
该方法会在Bean类被初始化之后被执行。
class BeanPostProcessorHelloBean { private String helloWord; public String getHelloWord() { return helloWord; } public void setHelloWord(String helloWord) { this.helloWord = helloWord; } } public class BeanPostProcessorExample implements BeanPostProcessor { public Object postProcessAfterInitialization(Object bean, String name) throws BeansException { Field[] fields = bean.getClass().getDeclaredFields(); for (int i = 0; i < fields.length; i++) { Field f = fields[i]; if (f.getType().equals(String.class)) { try { f.setAccessible(true); String orginal = (String)f.get(bean); f.set(bean, "(" + orginal.toUpperCase()+"->AfterInit)"); } catch(Exception e) { System.out.println(e.getMessage()); } } } return bean; } public Object postProcessBeforeInitialization(Object bean, String name) throws BeansException { Field[] fields = bean.getClass().getDeclaredFields(); for (Field f : fields) { if (f.getType().equals(String.class)) { f.setAccessible(true); try { String orginal = (String)f.get(bean); System.out.println("-->"+orginal); } catch(Exception e) {} } } return bean; } public static void main(String args[]) { AbstractApplicationContext context = new ClassPathXmlApplicationContext("beans.xml"); BeanPostProcessorHelloBean bean = (BeanPostProcessorHelloBean)context.getBean("beanPostProcessorHello"); System.out.println("->"+bean.getHelloWord()); } } <bean id="beanPostProcessorExample" class="com.spring.ch3.BeanPostProcessorExample"> </bean> <bean id="beanPostProcessorHello" class="com.spring.ch3.BeanPostProcessorHelloBean"> <property name="helloWord" value="HelloWorld"></property> </bean> |
l BeanFactoryPostProcessor
在BeanFactory载入Bean定义文件的所有内容,但还没有正式产生Bean实例前,可以对该BeanFactory进行一些处理,方式是让Bean类实现org.springframework.beans.factory.config.BeanFactoryPostProcessor接口。接口定义的方法如下:
Ø postProcessBeanFactory()
如果有类SomeBean实现了BeanFactoryPostProcessor接口,则可以在Bean定义文件中定义它,在使用ApplicationContext时,ApplicationContext会自动使用这些类的实例。
在Spring中提供有几个BeanFactoryPostProcessor接口的实现类,像
org.springframework.beans.factory.config.PropertyPlaceholderConfigurer
org.springframework.beans.factory.config.PropertyOverrideConfigurer
org.springframework.beans.factory.config.CustomEditorConfigurer
l PropertyPlaceholderConfigurer
通过这个类,可以将一些配置设置信息移出到一个或者多个.property文件中。可以让XML定义文件负责系统的相关设置,而.properties文件可以让客户根据实际应用的需求,自定义一些相关数据(也就是可以在Bean定义文件中直接引用.properties文件中的定义值)。
<bean id="configBean" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> <property name="location" value="classpath:com/spring/config.properties"></property> </bean> <bean id="beanPostProcessorHello" class="com.spring.ch3.BeanPostProcessorHelloBean"> <property name="helloWord" value="${com.spring.helloword}"></property> </bean> |
如果有多个.properties文件,则可以通过“locations”属性来设置,例如:
<bean id="configBean2" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> <property name="locations"> <list> <value>classpath:com/spring/config.properties</value> </list> </property> </bean> |
l CustomEditorConfigurer
这个类实现了接口BeanFactoryPostProcessor。这个类可以读取实现java.beans.PropertyEditor接口的类,并按其中的实现,将字符串值转换为指定类型的对象。
举个例子来说,假设现在设计了两个类:User类和HelloBean类。
<bean id="userHelloBean" class="com.spring.ch3.UserHelloBean"> <property name="helloWord" value="你好!"></property> <property name="user" value="张怡宁,8888"></property> </bean> <bean id="configBean" class="org.springframework.beans.factory.config.CustomEditorConfigurer"> <property name="customEditors"> <map> <entry key="com.spring.ch3.User"> <bean id="userEditor" class="com.spring.ch3.UserEditor"></bean> </entry> </map> </property> </bean> class User { private String name; private int number; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getNumber() { return number; } public void setNumber(int number) { this.number = number; } } class UserHelloBean { private String helloWord; private User user; public String getHelloWord() { return helloWord; } public void setHelloWord(String helloWord) { this.helloWord = helloWord; } public User getUser() { return user; } public void setUser(User user) { this.user = user; } } class UserEditor extends PropertyEditorSupport { public void setAsText(String text) { String[] strs = text.split(","); int number = Integer.parseInt(strs[1]); User user = new User(); user.setName(strs[0]+"@-@"); user.setNumber(number); setValue(user); } public static void main(String args[]) { AbstractApplicationContext context = new ClassPathXmlApplicationContext("beans.xml"); UserHelloBean hello = (UserHelloBean)context.getBean("userHelloBean"); System.out.println(hello.getHelloWord()); System.out.println("名字: "+hello.getUser().getName()); System.out.println("数字: "+hello.getUser().getNumber()); } } 运行结果: 你好! 名字: 张怡宁@-@ 数字: 8888
|
3.4 资源、消息、事件
l 资源的获得
Spring提供了对资源文件的泛型存取,ApplicationContext继承了org.springframework.core.io.ResourceLoader接口,你可以使用getResource()方法并制定资源文件的URL来取得一个实现Resource接口的实例,例如:
Resource resource = context.getResource(“classpath:admin.properties”);
也可以制定标准的URL,像”file:”或者”http:”,例如:
Context.getResource(“file:/workspace/springtest/conf/admin.properties”);
这会回传一个org.springframework.core.io.FileSystemResource的实例,或者你可以按如下来指定回传一个ServletContextResource:
Context.getResource(“WEB-INF/conf/admin.properties”);
l 解析文字消息
ApplicationContext继承org.springframework.context.MessageSource接口,你可以使用getMessage的方法来取得文件消息的资源文件,从而实现国际化的目的。
Object[] ags = new Object[] {"张怡宁", Calendar.getInstance().getTime()}; System.out.println(context.getMessage("userLogin", ags, Locale.TAIWAN)); System.out.println(context.getMessage("userLogin", ags, Locale.US)); <bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource"> <property name="basename" value="message"></property> </bean> 其中: message_en_US.properties userLogin =User {0} login at {1} message_zh_TW.properties userLogin =\u7528\u6237 {0} \u4E8E {1} \u767B\u 5F 55
运行结果: 用户 张怡宁 于 2008/8/24 下午 10:56 登录 User 张怡宁 login at 8/24/08 10:56 PM
|
l 监听事件
在Spring程序执行期间,ApplicationContext本省就会发布一连串的事件,所有发布的事件的抽象类org.springframework.context.ApplicationEvent的子类,例如:
Ø ContextClosedEvent
在ApplicationContext关闭时发布事件。
Ø ContextRefreshedEvent
在ApplicationContext初始或者Refresh时发布事件。
Ø RequestHandledEvent
在Web程序中,当请求被处理时,ApplicationContext会发布此事件。如果你对这些事件有兴趣,则可以实现org.springframework.context.ApplicationContext接口,并在定义文件中定义实现该接口的一个Bean实例:
public interface ApplicationListener extends EventListener { void onApplicationEvent(ApplicationEvent event); } |
l 事件传播
如果打算发布事件通知ApplicationListener的实例(例如在程序中定义发布事件),可以使用ApplicationContext的publishEvent()方法,如:
class SomeEvent extends ApplicationEvent { public SomeEvent(Object source) { super(source); } }
class SomeListener implements ApplicationListener { public void onApplicationEvent(ApplicationEvent event) { if (event instanceof SomeEvent) { System.out.println(event.getClass()); } } } <bean id="consoleListener" class="com.spring.ch3.SomeListener"> </bean>
context.publishEvent(new SomeEvent(context));
运行结果: class com.spring.ch3.SomeEvent |
</script>