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>a@a.com</value>
<value>b@a.com</value>
</list>
</property>
</bean>
Ø 如果是Map对象
<bean id="emailsMap" class="org.springframework.beans.factory.config.MapFactoryBean">
<property name="sourceMap">
<map>
<entry key="a" value="a@a.com"></entry>
<entry key="b" value="a@a.com"></entry>
</map>
</property>
</bean>
Ø 如果是Set对象
<bean id="emailsSet" class="org.springframework.beans.factory.config.SetFactoryBean">
<property name="sourceSet">
<set>
<value>a@a.com</value>
<value>b@a.com</value>
</set>
</property>
</bean>
Ø 如果是Properties对象
<bean id="emailsProperties" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
<property name="properties">
<props>
<prop key="a">a@a.com</prop>
<prop key="b">b@b.com</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">a@a.com</prop>
<prop key="b">b@a.com</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\u5F55
运行结果:
用户 张怡宁 于 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>