《Spring In action》學習筆記——依賴注入

索引
1.基本Bean配置
   1.1Bean容器
   1.2Bean工廠-BeanFactory
   1.3應用上下文-ApplicationContext
   1.4Bean生命週期
   1.5Bean的創建
        1.5.1通過構造函數注入
        1.5.2通過setter方法注入
        1.5.3注入集合
        1.5.4注入空值
   1.6自動裝配
        1.6.1四種自動裝配類型
        1.6.2默認自動裝配
   1.7控制Bean創建
         1.7.1Bean範圍
         1.7.2利用工廠方法創建Bean
         1.7.3初始化和銷燬Bean
2.高級Bean裝配
   2.1Bean繼承
         2.1.1抽象共同屬性
   2.2方法注入
         2.2.1基本方法替換
         2.2.2獲取器注入(getter方法注入)
   2.3向非Spring Bean注入
   2.4註冊自定義屬性編輯器
   2.5使用Spring的特殊Bean
        2.5.1後處理Bean(BeanPostProcessor)
        2.5.2BeanFactory的後處理(BeanFactoryProcessor)
        2.5.3配置屬性外在化——PropertyPlaceholderConfiguror
        2.5.4國際化
        2.5.5程序事件解耦
   2.6讓Bean知道更多
        2.6.1讓Bean知道自己的名稱
        2.6.2讓Bean知道自己所在的容器
   2.7腳本化Bean         

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"));


1.1 Resource的各種實現類
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的生命週期


2.1Bean生命週期明細
步驟 說明
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:
<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{
...
}

註解@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是這個接口的一種簡便實現方式,它有兩個方法比較吸引人:
  • getAsText()——返回屬性值的String表達形式。
  • setAsText(String value)——把傳遞來的String值設置給Bean的屬性
Spring具有多個基於PropertyEditorSupport的自定義編輯器,比如org.springframework.beans.propertyeditors.URLEditor,它實現了Strings與java.net.URL對象之間的轉化。下表列出來Spring的自定義編輯器集合:
Spring自定義編輯器,它們能自動把注入的String值轉化爲更復雜的類型
屬性編輯器 功能
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
除了Spring提供的自定義編輯器外,我們還可以擴展PropertyEditorSupport類來編寫自己的自定義編輯器。比如說要實現把一個String值轉換成一個自定義的bean——PhoneNumber(電話號碼):
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的配置:
<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屬性。

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後處理器。
如果程序運行在BeanFactory內,那麼通過BeanFactory的addBeanPostProcessor()方法來註冊每個BeanPostProcessor:

BeanPostProcessor fuddifier=new Fuddifier();
factory.addBeanPostProcessor(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的配置信息如下:
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>

現在我們就可以用佔位變量替代硬編碼了:
<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 context=...;
Course course=...;
context.publishEvent(new CourseFullEvent(this,course));

爲了發佈事件,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()方法

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





發佈了30 篇原創文章 · 獲贊 31 · 訪問量 42萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章