1.基本Bean配置
1.1Bean容器
有两种类型的Bean容器,Bean工厂(BeanFactory)和应用上下文(ApplicationContext)。BeanFactory延迟载入所有的Bean,直到getBean()方法被调用时Bean才被创建。而ApplicationContext启动后预载入所有的单实例Bean。
1.2 Bean工厂-BeanFactory
最常使用的BeanFactory的实现是org.springframework.beans.factory.xml.XmlBeanFactory。要创建XmlBeanFactory,需要传递一个org.springframework.core.io.Resource实例给构造函数。Resource类的作用是为工厂提供xml文件。
BeanFactory实例的创建:
BeanFactory factory=new XmlBeanFactory(new FileSystemResource("c:/beans.xml"));
| Resource实现 | 目的 |
|---|---|
| org.springframework.core.io.ByteArray.ByteArrayResource | 由一组字节给定的资源 |
| org.springframework.core.io.ClassPathResource | 从classpath提取的资源 |
| org.springframework.core.io.DescriptiveResource | 定义包含资源描述符但是实际没有可读资源的资源 |
| org.springframework.core.io.FileSystemResource | 从文件系统提取的资源 |
| org.springframework.core.io.InputStreamResource | 从输入流提取的资源 |
| org.springframework.web.portlet.context.PortletContextResource | 在portlet上下文中的资源 |
| org.springframework.web.context.support.ServletContextResource | 在servlet上下文中的资源 |
| org.springframework.core.io.UrlResource | 从给定URL提取的资源 |
1.3 应用上下文-ApplicationContext
ApplicationContext实现了BeanFactory接口,因此,ApplicationContext包含了BeanFactory所有的功能,并提供了其他更多的功能:
- 应用上下文提供了文本信息解析工具,包括对国际化(I18N)的支持
- 应用上下文提供了载入文件资源的通用方法,如载入图片
- 应用上下文可以向注册为监听器的Bean发送事件
ApplicationContext有三个经常使用的实现:
- ClassPathXmlApplicationContext——从类路径中的XML文件载入上下文定义信息,把上下文定义文件当成类路径资源。
- FileSystemXmlApplicationContext——从文件系统中的XML文件载入上下文定义信息
- XmlWebApplicationContext——从Web系统中的XML文件载入上下文定义信息
ApplicationContext实例的创建示例:ApplicationContext context=new FileSystemXmlApplicationContext("c:/foo.xml");
//or
ApplicationContext context=new ClassPathXmlApplicationContext("foo.xml");
1.4Bean的生命周期
| 步骤 | 说明 |
| 1.实例化 | Spring实例化Bean |
| 2.设置属性 | Spring注入Bean的属性 |
| 3.设置Bean名称 | 如果Bean实现了BeanNameAware接口,Spring传递Bean工厂给setBeanFactory() |
| 4.预处理(在初始化之前) | 如果有多个BeanPostProcessor,Spring将调用postProcessBeforeInitialization()方法 |
| 5.初始化Bean | 如果Bean实现InitializingBean,其afterPropertiesSet()方法将被调用。如果Bean声明了自定义的初始化方法,那么将调用指定的初始化方法 |
| 6.预处理(在初始化之后) | 如果有多个BeanPostProcessor,Spring将调用postProcessAfterInitialization()方法 |
| 7.Bean已经准备好 | 此时Bean已经准备好,可以使用,并且将一直保留在Bean工厂中,直到不再需要它 |
| 8.销毁Bean | 如果Bean实现了DisposableBean,将调用destroy()方法 如果Bean有自定义的销毁方法,将调用指定的方法 |
1.5 Bean的创建
1.5.1 通过构造函数注入
<beans ...>
<bean id="sonnet29" class="com.springinaction.springidol.Sonnet29" />
<bean id="duke" class="com.springinaction.springido1.PoeticJuggler">
<constructor-arg value="15" /> <!--注入基本类型和String类型-->
<constructor-arg ref="sonnet29" /> <!--注入声明的一个bean对象-->
</bean>
</beans>
1.5.2 通过setter方法注入
<beans ...>
<bean id="kenny" class="com.springinaction.springido1.Instrumentalist">
<property name="song" value="Jingle Bells" /> <!--注入基本类型或String -->
<property name="instrument" ref="saxophone" /> <!--注入已经声明的bean -->
</bean>
</beans>
注入内部Bean:
注入内部Bean:
<beans ...>
<bean id="kenny" class="com.springinaction.springido1.Instrumentalist">
<property name="song" value="Jingle Bells" /> <!--注入基本类型或String -->
<property name="instrument">
<bean class="org.springinaction.springido1.Saxophone" /> <!--不需要声明id属性 -->
</property>
</bean>
</beans>
1.5.3 注入集合
Spring通过value属性配置基本类型,通过ref属性配置已经声明的bean,但value和ref只有在你的属性是单一的时候才有效。当属性是复数(也就是集合)时,Spring提供了4种类型的集合配置元素来配置:
| 集合元素 | 用途 |
|---|---|
| <list> | 装配一列值,允许有重复值,可以与任意类型java.util.Collection或数组的属性交换 |
| <set> | 装配值集,确保无重复值,可以与任意类型java.util.Collection中的属性交换 |
| <map> | 装配键值对的集合,键和值可以是任意类型 |
| <props> | 装配键值对的集合,键和值都是String类型 |
1.5.3.1 <list>和<set>配置示例:
<bean id="bank" class="com.springinaction.springido1.OneManBand">
<property name="instruments">
<list><!--可以换用<set>元素 -->
<ref bean="guitar" />
<ref bean="cymbal" />
<ref bean="harmonica" />
<ref bean="harmonica" />
</list>
</property>
</bean>
1.5.3.4 <map>配置示例:
<bean id="hank" class="com.springinaction.springido1.OneManBand">
<property name="instruments">
<map>
<entry key="GUITAR" value-ref="guitar" />
<entry key="CYMBAL" value-ref="cymbal" />
<entry key="HARMONICA" value-ref="harmonica" />
</map>
</property>
</bean>
| 属性 | 目的 |
|---|---|
| key | 指定map项的键为String |
| key-ref | 指定map项的键为Spring上下文中Bean的引用 |
| value | 指定map项的值为String |
| value-ref | 指定map项的值为Spring上下文中Bean的引用 |
1.5.3.5 <props>配置示例:
<bean id="hank" class="com.springinaction.springido1.OneManBand">
<property name="instruments">
<props>
<prop key="GUITAR">STRUM STRUM STRUM</prop>
<prop key="CYMBAL">CRASH CRASH CRASH</prop>
<prop key="HARMONICA">HUM HUM HUM</prop>
</props>
</property>
</bean>
1.5.4 装配空值
<property name="someNonNullProperty"><null /></property>
1.6 自动装配
在<bean>中添加autowire属性,为bean增加自动装配功能
1.6.1 四种自动装配类型
- byName——试图在容器中寻找和需要自动装配的属性名相同的Bean(或ID)。如果没有找到相符的Bean,这个属性就没有被装配上。
- byType——试图在容器中寻找一个与需要自动配置的属性类型相同的Bean。如果没有找到相符的Bean,这个属性就没有被装配。如果找到超过一个相符的Bean,会抛出org.springframework.beans.factory.UnsatisfiedDependencyException异常
- constructor——试图在容器中查找与需要自动装配的Bean的构造函数参数一致的一个或多个Bean。如果存在不确定Bean或构造函数,容器会抛出异常org.springframework.beans.factory.UnsatisfiedDependencyException异常
- autodetect——首先尝试使用constructor来自动装配,然后使用byType方式。不确定性的处理与constructor方式和byType方式一样。
自动装配示例:
<bean id="kenny" class="com.springinaction.springido1.Instrumentalist" autowire="byName" >
<property name="song" value="Jingle Bells" />
<!--当没有添加autowire的时候,需要显示地声明instrument属性的值,现在添加了自动装配功能,那么instrument属性便会自动地使用id为instrument的bean来装配instrument属性 -->
<!--
<property name="instrument" ref="instrument" />
-->
</bean>
1.6.2 默认自动装配
默认情况下,Bean不会被自动装配,除非你设置了autowire属性。然而,通过在Spring配置文件的根元素<beans>中设置default autowire就可以将所有的Bean设置为自动装配,如:
<beans default-autowire="byName">
...
</beans>
1.7控制Bean创建
1.7.1 Bean范围
默认时,所有Spring Bean都是单一的,意思是在整个Spring应用中,Bean的实例只有一个。可以在<bean>中添加scope属性来修改这个默认值。scope属性可用的值如下表:
| 范围 | 完成任务 |
|---|---|
| singleton | 定义Bean的范围为每个Spring容器一个实例(默认值) |
| prototype | 允许Bean可以被多次实例化(使用一次就创建一个实例) |
| request | 定义Bean的范围是HTTP请求。只有在使用有Web能力的Spring上下文时才有效 |
| session | 定义Bean的范围是HTTP会话。只有在使用有Web能力的Spring上下文时才有效 |
| global-session | 定义Bean的范围是全局HTTP会话。只有在portlet上下文中有效 |
1.7.2 利用工厂方法创建Bean
<bean>元素中有一个factory-method属性,通过该属性来设置工厂方法。示例:<bean id="theStage" class="com.springinaction.springido1.Stage" factory-method="getInstance" />
1.7.3 初始化和销毁Bean
通过<bean>的init-method、destroy-method属性来声明初始化和销毁Bean的方法。比如,在演奏乐器之前需要调整乐器(tuneInstrument方法),演奏完成后清理乐器(cleanInstrument方法),示例如下:
<bean id="kenny" class="com.springinaction.springido1.Instrumentalist" init-method="tuneInstrument" destroy-method="cleanInstrument" >
<property name="song" value="Jingle Bells" />
<property name="instrument" ref="saxophone" />
</bean>
默认的初始化和销毁方法
默认的初始化和销毁方法
可以在<beans>元素中添加 default-init-method、default-destroy-method来声明默认的初始化、销毁方法。示例:
<beans default-init-method="init" default-destroy-method="destroy">
...
</beans>
另外,还可以通过实现InitializingBean和DisposableBean来初始化和销毁Bean
2 高级Bean装配
2.1 Bean的继承
<bean>元素提供了两个特殊的属性:abstract和parent来配置继承关系。
- parent:指明Bean的id。它对于<bean>的作用就相当于关键字extends对于Java类的作用。
- abstract:如果设置为true,就表示<bean>声明是抽象的,不能被Spring实例化。
示例如下:
<bean id="baseSaxophonist" class="com.springinaction.springido1.Instrumentalist" abstract="true">
<property name="instrument" ref="saxophone" />
<property name="song" value="Jingle Bells" />
</bean>
<bean id="kenny" parent="baseSaxophonist" />
<bean id="david" parent="baseSaxophonist">
<property name="song" value="Mary had a little lamb" /><!--覆盖song属性,不继承baseSaxophonist中的song属性-->
</bean>
2.1.1 抽象共同属性
子Bean不必具有相同的父类型。两个class属性值完全不同的<bean>仍然可以从一个父bean继承一组相同的属性。示例:
<bean id="basePerformer" abstract="true">
<property name="song" value="Somewhere Over the Rainbow" />
</bean>
<bean id="taylor" class="com.springinaction.springidol.Vocalist" parent="basePerformer">
<property name="instrument" ref="guitar" />
</bean>
<bean id="stevie" class="com.springinaction.springido1.Instrumentalist" parent="basePerformer">
<property name="instrument" ref="saxophone" />
</bean>
2.2 方法注入
Spring支持两种形式的方法注入:
- 方法替换:可以在运行时用新的实现替换现有方法(抽象或具体的)。
- 获取器注入:可以在运行时用新的实现代替现有方法(抽象或具体的),从Spring上下文中返回特定的Bean。
2.2.1 基本方法替换
下面引用魔术师和他的魔法盒的例子来阐述这个功能。
package com.springinaction.springido1;
//魔术师
public class Magician implements Performer{
public Magician(){
}
public void perform() throws PerformanceException{
System.out.println(magicWords);
System.out.println("The magic box contains...");
System.out.println(magicBox.getContents());
}
private MagicBox magicBox;
public void setMagicBox(MagicBox magicBox){
this.magicBox=magicBox;
}
private String magicWords;
public void setMagicWords(String magicWords){
this.magicWords=magicWords;
}
}package com.springinaction.springido1;
//魔法盒——魔法盒里永远都是一位美丽的助手
public class MagicBoxImpl implements MagicBox{
public MagicBoxImpl(){}
public String getContents(){
return "A beautiful assistant";
}
}当前状态下,这位魔法师永远只能从魔法盒中变出一位美丽的助手,而不能变出一头老虎。如何让魔法师能变出老虎呢?这就需要用到Spring的基本方法替换了。
package com.springinaction.springido1;
import java.lang.reflect.Method;
import org.springframework.beans.factory.support.MethodReplacer;
public class TigerReplacer implements MethodReplacer{
public Object reimplement(Object target,Method method,Object[] args) throws Throwable{
return "A ferocious tiger";
}
}这里TigerReplacer实现了Spring的MethodReplacer接口,该接口只需要实现reimplement方法。reimplement方法包含有三个参数:要替换方法的目标对象、要被替换的方法、传递给方法的参数。下面就是通过xml配置来实现基本方法替换:
<bean id="magicBox" class="com.springinaction.springido1.MagicBoxImpl">
<replaced-method name="getContents" replacer="tigerReplacer" />
</bean>
<bean id="tigerReplacer" class="com.springinaction.springido1.TigerReplacer" />
现在美丽的助手变成了凶猛的老虎了。这里的getContent()是个具体的方法,但它也可以是个抽象的方法。
2.2.2 获取器注入(getter 注入)
获取器注入实际就是setter注入的反面。现在有一个新的Instrumentalist类,它抽象了getter方法。
package com.springinaction.springido1;
public abstract class Instrumentalist implements Performer{
public Instrumentalist(){}
public void perform() throws PerformanceException{
System.out.print("Playing "+song+":");
//使用注入的getInstrument()方法
getInstrument().play();
}
private String song;
public void setSong(String song){
this.song=song;
}
public abstract Instrument getInstrument();//注入getInstrument()
}那么,现在就可以使用<bean>下的<lookup-method>子元素来声明抽象的getter方法返回的是哪个bean,如下<bean id="stevie" class="com.springinaction.springido1.Instrumentalist"> <!-- name属性指示的是要被替换的方法,bean属性指示的是该方法返回的是上下文中的哪个bean --> <lookup-method name="getInstrument" bean="guitar" /> <property name="song" value="Greensleeves" /> </bean>
2.3 向非Spring Bean注入
当有些对象不是通过Spring实例化(像自定义的JSP标记、ORM对象等)的时候,而我们又需要向这些对象注入一些值的时候,就需要用到这方面的功能了。下面是钢琴手的配置内容:
<bean id="pianist" class="com.springinaction.springido1.Instrumentalist" abstract="true">
<property name="song" value="Chopsticks" />
<property name="instrument">
<bean class="com.springinaction.springido1.Piano" />
</property>
</bean>
另外,还得给Instrumentalist类级别上添加Configurable注解:package com.springinaction.springido1; import org.springframework.beans.factory.annotaion.Configurable; @Configurable("pianist") public class Instrumentalist implements Performer{ ... }
另外,还得给Instrumentalist类级别上添加Configurable注解:package com.springinaction.springido1; import org.springframework.beans.factory.annotaion.Configurable; @Configurable("pianist") public class Instrumentalist implements Performer{ ... }
注解@Configurable的作用有两个:
- 第一,它表示Instrumentalist实例即使是在Spring之外创建的,仍然可以由Spring进行配置
- 第二,它把Instrumentalist类与id为pianist的Bean关联起来。当Spring企图配置Instumentalist实例时,会以pianist Bean作为模板。
最后,在Spring配置中添加如下内容,告诉Spring有一些Bean需要配置:<aop:spring-configured />
2.4 注册自定义属性编辑器
这个功能实际上是利用简单的String值设置复杂的属性。比如常见的通过一个String值来配置一个java.net.URL对象:<property name="wsdlDocumentUrl" value="http://www.xmethods.net/sd/BabelFishService.wsdl" />
实际上,这个功能并不是由Spring提供的,而是来自原始JavaBeans API的一个鲜为人知的特性。Java.beans.PropertyEditor接口提供了一种手段,让我们能够自定义String如何映射到非String值。java.beans.PropertyEditorSupport是这个接口的一种简便实现方式,它有两个方法比较吸引人:
实际上,这个功能并不是由Spring提供的,而是来自原始JavaBeans API的一个鲜为人知的特性。Java.beans.PropertyEditor接口提供了一种手段,让我们能够自定义String如何映射到非String值。java.beans.PropertyEditorSupport是这个接口的一种简便实现方式,它有两个方法比较吸引人:
- getAsText()——返回属性值的String表达形式。
- setAsText(String value)——把传递来的String值设置给Bean的属性
Spring具有多个基于PropertyEditorSupport的自定义编辑器,比如org.springframework.beans.propertyeditors.URLEditor,它实现了Strings与java.net.URL对象之间的转化。下表列出来Spring的自定义编辑器集合:
| 属性编辑器 | 功能 |
|---|---|
| ClassEditor | 从一个String值设置java.lang.Class属性,前者包含一个完整描述的类名 |
| CustomDateEditor | 从一个String值设置java.util.Date属性,前者使用自定义的java.text.DateFormat对象 |
| FileEditor | 从一个String值设置java.io.File属性,前者包含文件的路径 |
| LocalEditor | 从一个String值设置java.util.Locale属性,前者包含地域的文本表示(比如en_US) |
| StringArrayPropertyEditor | 把逗号分隔的String转化为一个String数组属性 |
| StringTrimmerEditor | 对String属性进行自动裁剪;设置一个选项后可以把空的String值转化为null |
| URLEditor | 从一个String值设置java.net.URL属性,前者包含一个URL |
public class PhoneNumber{
private String areaCode;
private String prefix;
private String number;
//getter and setter method
}自定义PhoneEditor:public class PhoneEditor extends java.beans.PropertyEditorSupport{
public void setAsText(String textValue){
String stripped=stripNonNumeric(textValue);
String areaCode=stripped.substring(0,3);
String prefix=stripped.substring(3,6);
String number=stripped.substring(6);
PhoneNumber phone=new PhoneNumber(areaCode,prefix,number);
setValue(phone);
}
private String stripNonNumeric(String original){
//...
}
}
最后,我们得让Spring在装配Bean属性时能够知道我们的自定义编辑器,为此,需要使用Spring的CustomEditorConfigurer,它是一个BeanFactoryPostProcessor,通过调用registerCustomEditor()方法把自定义编辑器加载到BeanFactory。(或者,在得到了Bean工厂的实例之后,我们可以自己在代码里调用registerCustomEditor()方法。)下面是XML的配置:
最后,我们得让Spring在装配Bean属性时能够知道我们的自定义编辑器,为此,需要使用Spring的CustomEditorConfigurer,它是一个BeanFactoryPostProcessor,通过调用registerCustomEditor()方法把自定义编辑器加载到BeanFactory。(或者,在得到了Bean工厂的实例之后,我们可以自己在代码里调用registerCustomEditor()方法。)下面是XML的配置:
<bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
<property name="customEditors">
<map>
<entry key="com.springinaction.chapter03.propeditor.PhoneNumber">
<bean id="phoneEditor" class="com.springinaction.chapter03.propeditor.PhoneEditor"></bean>
</entry>
</map>
</property>
</bean>
现在,我们就可以用简单的String值来配置Contact对象的phoneNumber属性。
现在,我们就可以用简单的String值来配置Contact对象的phoneNumber属性。
2.5 使用Spring的特殊Bean
通过实现特定的接口,我们可以让Spring以特殊方式对待Bean——把它当作Spring框架本身的一部分。利用这些特殊的Bean,可以得到下面的好处:
- 通过对Bean配置的后处理来介入Bean的创建和Bean工厂的生命周期
- 从外部属性文件加载配置信息
- 从属性文件加载文本消息,包括国际化的消息
- 监听和响应由其他Bean和Spring容器本身发布的程序事件
- 知道它们在Spring容器里的身份
2.5.1 后处理Bean(BeanPostProcessor)
BeanPostProcessor接口为我们提供了两个机会,可以在Bean创建和装配之后来修改Bean:
public interface BeanPostProcessor{
Object postProcessBeforeInitialization(Object bean,String name) throws BeansException;
Object postProcessAfterInitialization(Object bean,String name) throws BeansException;
}下面是对BeanPostProcessor应用的一个例子:
public class Fuddifier implements BeanPostProcessor{
public Object postProcessAfterInitialization(Object bean,String name) throws BeansException{
Field[] fields = bean.getClass().getDeclaredFields();
try{
for(int i=0;i<fields.length;i++){
if(fields[i].getType().equals(java.lang.String.class)){
fields[i].setAccessible(true);
String original=(String)fields[i].get(bean);
fields[i].set(bean,fuddify(original));
}
}
}catch(IllegalAccessException e){
e.printStackTrace();
}
return bean;
}
private String fuddify(String orig){
//...
}
public Object postProcessBeforeInitialization(Object bean,String name) throws BeansException{
return bean;
}
}
最后,还得注册Bean后处理器。
最后,还得注册Bean后处理器。
如果程序运行在BeanFactory内,那么通过BeanFactory的addBeanPostProcessor()方法来注册每个BeanPostProcessor:
BeanPostProcessor fuddifier=new Fuddifier();
factory.addBeanPostProcessor(fuddifier);
如果程序运行在ApplicationContext,那么只需要通过XML配置就可以自动注册BeanPostProcessor:<bean class="com.springinaction.chapter03.postprocessor.Fuddifier" />
如果程序运行在ApplicationContext,那么只需要通过XML配置就可以自动注册BeanPostProcessor:<bean class="com.springinaction.chapter03.postprocessor.Fuddifier" />
Spring自己的后处理器
Spring框架在私下使用了BeanPostProcessor的多个实现。比如ApplicationContextAwareProcessor就是个BeanPostProcessor,它把ApplicationContext设置到实现了ApplicationContextAware接口的Bean。我们不需要注册ApplicationContextAwareProcessor,程序容器本身对它进行了预注册。
2.5.2 Bean工厂的后处理(BeanFactoryPostProcessor)
BeanFactoryPostProcessor只能应用于ApplicationContext,不能用于BeanFactory。BeanFactoryPostProcessor接口定义如下:public interface BeanFactoryPostProcessor{
void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
}在全部Bean定义被加载之后,但在任何一个Bean被实例化之前(包括BeanPostProcessor Bean),Spring容器会调用postProcessBeanFactory()方法。
注册BeanFactoryPostProcessor与声明一个普通的Bean一样简单:
<bean id="beanFactoryPostProcessor" class="beanFactoryPostProcessorImpl" />
2.5.3 配置属性的外在化——PropertyPlaceholderConfigurer
如果使用ApplicationContext作为Spring容器,属性外在化是很容易的。Spring使用PropertyPlaceholderConfigurer从外部属性文件加载特定的配置。为了启用这个特性,需要在xml配置文件上添加如下Bean:
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="jdbc.properties" />
</bean>
jdbc.properties的配置信息如下:
jdbc.properties的配置信息如下:
database.url=jdbc:hsqldb:training
database.driver=org.hsqldb.jdbcDriver
...
如果需要把配置分散到多个属性文件里,应该使用PropertyPlaceholderConfigurer的locations属性来设置属性文件的List:<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> <property name="locations"> <list> <value>jdbc.properties</value> <value>security.properties</value> <value>application.properties</value> </list> </property> </bean>
如果需要把配置分散到多个属性文件里,应该使用PropertyPlaceholderConfigurer的locations属性来设置属性文件的List:<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> <property name="locations"> <list> <value>jdbc.properties</value> <value>security.properties</value> <value>application.properties</value> </list> </property> </bean>
现在我们就可以用占位变量替代硬编码了:
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="url" value="${database.url}" />
...
</bean>
2.5.4 国际化
Spring的ApplicationContext可以通过MessageSource接口把国际化消息提供给容器。Spring提供了MessageSource的一个实现——ResourceBundleMessageSource就是使用java自己的java.util.ResourceBundle来提取消息。配置如下:
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource"> <property name="basename"> <value>trainingtext</value> </property> </bean>Notic:这个Bean的名称必须是messageSource,因为ApplicationContext在建立其内部消息源时,会查找这个名称的Bean。我们可以通过ApplicationContext.getMessage()方法来访问消息。 Local local=...; String text=context.getMessage("computer",new Object[0],locale);
在Spring的web模块中,可以使用Spring的JSP标记<spring:message>获取消息。 <spring:message code="computer" />
2.5.5 程序事件的解耦
在Spring里,容器中的任何Bean都可以作为事件监听器、事件发布者或两者都是。
2.5.5.1 发布事件
首先,定义一个自定义事件上,比如下面这个CourseFullEvent:
public class CourseFullEvent extends ApplicationEvent{
private Course course;
public CourseFullEvent(Object source,Course course){
super(source);
this.course=course;
}
public Course getCourse(){
return course;
}
}
接着通过ApplicationContext接口的publishEvent()方法可以发布ApplicationEvents。在程序上下文里注册的任何一个ApplicationLIstener都会由它的onApplicationEvent()方法接收并处理事件:
接着通过ApplicationContext接口的publishEvent()方法可以发布ApplicationEvents。在程序上下文里注册的任何一个ApplicationLIstener都会由它的onApplicationEvent()方法接收并处理事件:
ApplicationContext context=...;
Course course=...;
context.publishEvent(new CourseFullEvent(this,course));
为了发布事件,Bean需要访问ApplicationContext,这意味着Bean必须了解其运行所在的容器。
为了发布事件,Bean需要访问ApplicationContext,这意味着Bean必须了解其运行所在的容器。
2.5.5.2 监听事件
Spring容器本身在程序运行期间也会发布一些事件,它们都是抽象类org.springframework.context.APplicationEvent的子类。下面是这种程序事件的三个范例:
- ContextCloseEvent:程序上下文被关闭时发布。
- ContextRefreshedEvent:程序上下文被初始化或刷新时发布。
- RequestHandleEvent:当一个请求被处理时,在Web程序上下文里发布。
如果想让Bean响应程序事件,无论它是由另一个Bean还是容器发布的,需要做的事情就是实现org.springframework.context.ApplicationListener接口中的onApplicationEvent()方法来响应事件。public class RefreshListener implements ApplicationListener{
public void onApplicationEvent(ApplicationEvent event){
...
}
}要注册监听器,只需要在xml中配置这个Bean:
<bean id="refreshListener" class="com.springinaction.foo.RefreshListener" />
这时,当有事件发布时便会调用onApplicationEvent()方法
这时,当有事件发布时便会调用onApplicationEvent()方法
2.6 让Bean知道更多
2.6.1 让Bean知道自己的名称
Spring容器通过BeanNameAware接口告诉Bean它自己的名称。这个接口只有一个setBeanName()方法。它接受在Bean装配文件里id或name的属性的String类型参数。
public interface BeanNameAware{
void setBeanName(String name);
}
2.6.2 让Bean知道自己所在的容器
Spring的ApplicationContextAware和BeanFactoryAware接口能够让Bean知道自己所在的容器。
2.7 脚本化的Bean
略