空間站

          北極心空

            BlogJava :: 首頁 :: 聯系 :: 聚合  :: 管理
            15 Posts :: 393 Stories :: 160 Comments :: 0 Trackbacks

          3.3.5. 自動裝配協作對象

          BeanFactory能夠自動裝配合作bean之間的關系。這就意味著,讓Spring通過檢查BeanFactory的內容來自動裝配你的bean的合作者(也就是其他的bean)。自動裝配功能有5種模式。自動裝配可以指定給每一個bean,因此可以給一些bean使用而其他的bean不自動裝配。通過使用自動裝配,可以 減少(或消除)指定屬性(或構造函數參數)的需要,顯著節省鍵入工作。 [1] 在XmlBeanFactory中,使用bean元素的autowire屬性來指定bean定義的自動裝配模式。以下是允許的值.

          表 3.2. 自動裝配模式

          模式 解釋
          no 不使用自動裝配。Bean的引用必須通過ref元素定義。這是默認的配置,在較大的部署環境中不鼓勵改變這個配置,因為明確的指定合作者能夠得到更多的控制和清晰性。從某種程度上說,這也是系統結構的文檔形式。
          byName 通過屬性名字進行自動裝配。這個選項會會檢查BeanFactory,查找一個與將要裝配的屬性同樣名字的bean 。比如,你有一個bean的定義被設置為通過名字自動裝配,它包含一個master屬性(也就是說,它有一個setMaster(...)方法),Spring就會查找一個叫做master的bean定義,然后用它來設置master屬性。
          byType 如果BeanFactory中正好有一個同屬性類型一樣的bean,就自動裝配這個屬性。如果有多于一個這樣的bean,就拋出一個致命異常,它指出你可能不能對那個bean使用byType的自動裝配。如果沒有匹配的bean,則什么都不會發生,屬性不會被設置。如果這是你不想要的情況(什么都不發生),通過設置dependency-check="objects"屬性值來指定在這種情況下應該拋出錯誤。
          constructor 這個同byType類似,不過是應用于構造函數的參數。如果在BeanFactory中不是恰好有一個bean與構造函數參數相同類型,則一個致命的錯誤會產生。
          autodetect 通過對bean 檢查類的內部來選擇constructorbyType。如果找到一個缺省的構造函數,那么就會應用byType。

          注意:顯式的指定依賴,比如propertyconstructor-arg元素,總會覆蓋自動裝配。自動裝配的行為可以和依賴檢查結合使用,依賴檢查會在自動裝配完成后發生。

          注意:正如我們已經提到過的,對于大型的應用,自動裝配不鼓勵使用,因為它去除了你的合作類的透明性和結構。

          3.3.6. 依賴檢查

          對于部署在BeanFactory的bean的未解決的依賴,Spring有能力去檢查它們的存在性。 這些依賴要么是bean的JavaBean式的屬性,在bean的定義中并沒有為它們設置真實的值, 要么是通過自動裝配特性被提供。

          當你想確保所有的屬性(或者某一特定類型的所有屬性)都被設置到bean上面的時候, 這項特性就很有用了。當然,在很多情況下一個bean類的很多屬性都會有缺省的值, 或者一些屬性并不會應用到所有的應用場景,那么這個特性的作用就有限了 。 依賴檢查能夠分別對每一個bean應用或取消應用,就像自動裝配功能一樣。缺省的是 檢查依賴關系。 依賴檢查可以以幾種不同的模式處理。在XmlBeanFactory中, 通過bean定義中的 dependency-check 屬性來指定依賴檢查,這個屬性有以下的值。

          表 3.3. 依賴檢查模式

          模式 解釋
          none 不進行依賴檢查。沒有指定值的bean屬性僅僅是沒有設值。
          simple 對基本類型和集合(除了合作者外,比如其他的bean,所有東西)進行依賴檢查。
          object 對合作者進行依賴檢查。
          all 對合作者,基本類型和集合都進行依賴檢查。

          3.4. 自定義bean的本質特征

          3.4.1. 生命周期接口

          Spring提供了一些標志接口,用來改變BeanFactory中的bean的行為。 它們包括InitializingBeanDisposableBean。 實現這些接口將會導致BeanFactory調用前一個接口的afterPropertiesSet()方法, 調用后一個接口destroy()方法,從而使得bean可以在初始化和析構后做一些特定的動作。

          在內部,Spring使用BeanPostProcessors 來處理它能找到的標志接口以及調用適當的方法。 如果你需要自定義的特性或者其他的Spring沒有提供的生命周期行為, 你可以實現自己的 BeanPostProcessor。關于這方面更多的內容可以看這里: 第 3.7 節 “使用BeanPostprocessors定制bean”

          所有的生命周期的標志接口都在下面敘述。在附錄的一節中,你可以找到相應的圖, 展示了Spring如何管理bean;那些生命周期的特性如何改變你的bean的本質特征以及它們如何被管理。

          3.4.1.1. InitializingBean / init-method

          實現org.springframework.beans.factory.InitializingBean 接口允許一個bean在它的所有必須的屬性被BeanFactory設置后, 來執行初始化的工作。InitializingBean接口僅僅制定了一個方法:

              * Invoked by a BeanFactory after it has set all bean properties supplied
          * (and satisfied BeanFactoryAware and ApplicationContextAware).
          * <p>This method allows the bean instance to perform initialization only
          * possible when all bean properties have been set and to throw an
          * exception in the event of misconfiguration.
          * @throws Exception in the event of misconfiguration (such
          * as failure to set an essential property) or if initialization fails.
          */
          void afterPropertiesSet() throws Exception;

          注意:通常InitializingBean接口的使用是能夠避免的(而且不鼓勵,因為沒有必要把代碼同Spring耦合起來)。Bean的定義支持指定一個普通的初始化方法。在使用XmlBeanFactory的情況下,可以通過指定init-method屬性來完成。 舉例來說,下面的定義:

          <bean   init-method="init"/>
          public class ExampleBean {
          public void init() {
          // do some initialization work
          }
          }

          同下面的完全一樣:

          <bean  />
          public class AnotherExampleBean implements InitializingBean {
          public void afterPropertiesSet() {
          // do some initialization work
          }
          }

          但卻不把代碼耦合于Spring。

          3.4.1.2. DisposableBean / destroy-method

          實現org.springframework.beans.factory.DisposableBean接口允許一個bean, 可以在包含它的BeanFactory銷毀的時候得到一個回調。DisposableBean也只指定了一個方法:

              /**
          * Invoked by a BeanFactory on destruction of a singleton.
          * @throws Exception in case of shutdown errors.
          * Exceptions will get logged but not rethrown to allow
          * other beans to release their resources too.
          */
          void destroy() throws Exception;
          

          注意:通常DisposableBean接口的使用能夠避免的(而且是不鼓勵的,因為它不必要地將代碼耦合于Spring)。 Bean的定義支持指定一個普通的析構方法。在使用XmlBeanFactory使用的情況下,它是通過 destroy-method屬性完成。 舉例來說,下面的定義:

          <bean   destroy-method="destroy"/>
          public class ExampleBean {
          public void cleanup() {
          // do some destruction work (like closing connection)
          }
          }

          同下面的完全一樣:

          <bean  />
          public class AnotherExampleBean implements DisposableBean {
          public void destroy() {
          // do some destruction work
          }
          }

          但卻不把代碼耦合于Spring。

          重要的提示:當以portotype模式部署一個bean的時候,bean的生命周期將會有少許的變化。 通過定義,Spring無法管理一個non-singleton/prototype bean的整個生命周期, 因為當它創建之后,它被交給客戶端而且容器根本不再留意它了。 當說起non-singleton/prototype bean的時候,你可以把Spring的角色想象成“new”操作符的替代品。 從那之后的任何生命周期方面的事情都由客戶端來處理。BeanFactory中bean的生命周期將會在第 3.4.1 節 “生命周期接口” 一節中有更詳細的敘述 .

          3.4.2. 了解自己

          3.4.2.1. BeanFactoryAware

          對于實現了org.springframework.beans.factory.BeanFactoryAware接口的類, 當它被BeanFactory創建后,它會擁有一個指向創建它的BeanFactory的引用。

          public interface BeanFactoryAware {
          /**
          * Callback that supplies the owning factory to a bean instance.
          * <p>Invoked after population of normal bean properties but before an init
          * callback like InitializingBean's afterPropertiesSet or a custom init-method.
          * @param beanFactory owning BeanFactory (may not be null).
          * The bean can immediately call methods on the factory.
          * @throws BeansException in case of initialization errors
          * @see BeanInitializationException
          */
          void setBeanFactory(BeanFactory beanFactory) throws BeansException;
          }

          這允許bean可以以編程的方式操控創建它們的BeanFactory, 既可以直接使用 org.springframework.beans.factory.BeanFactory接口, 也可以將引用強制將類型轉換為已知的子類型從而獲得更多的功能。這個特性主要用于編程式地取得其他bean。 雖然在一些場景下這個功能是有用的,但是一般來說它應該避免使用,因為它使代碼與Spring耦合在一起, 而且也不遵循反向控制的風格(合作者應當作屬性提供給bean)。

          3.4.2.2. BeanNameAware

          如果一個bean實現了org.springframework.beans.factory.BeanNameAware接口, 并且被部署到一個 BeanFactory中,那么BeanFactory就會通過這個接口來調用bean,以便通知這個bean它被部署的id 。 這個回調發生在普通的bean屬性設置之后,在初始化回調之前,比如InitializingBeanafterPropertiesSet方法(或者自定義的init- method)。

          3.4.3. FactoryBean

          接口org.springframework.beans.factory.FactoryBean 一般由本身是工廠類的對象實現。BeanFactory接口提供了三個方法:

          • Object getObject(): 必須返回一個這個工廠類創建的對象實例。這個實例可以是共享的(取決于這個工廠返回的是singleton還是prototype)。

          • boolean isSingleton(): 如果Factory返回的對象是singleton,返回true,否則返回false

          • Class getObjectType(): 返回getObject()方法返回的對象的類型,如果類型不是預先知道的,則返回null

          3.5. 子bean定義

          一個bean定義可能會包含大量的配置信息,包括容器相關的信息(比如初始化方法,靜態工廠方法名等等)以及構造函數參數和屬性的值。一個子bean定義是一個能夠從父bean定義繼承配置數據的bean定義。 它可以覆蓋一些值,或者添加一些其他需要的值。使用父和子的bean定義可以節省很多的輸入工作。實際上,這就是一種模版形式。

          當以編程的方式使用一個BeanFactory,子bean定義用ChildBeanDefinition類表示。 大多數的用戶從來不需要以這個方式使用它們,而是在類似XmlBeanFactory的BeanFactory 中以聲明的方式配置bean定義。在一個XmlBeanFactory的bean定義中,使用parent屬性指出一個子bean定義,而父bean則作為這個屬性的值。

          <bean  >
          <property ><value>parent</value></property>
          <property ><value>1</value></property>
          </bean>
          <bean
          parent="inheritedTestBean" init-method="initialize">
          <property ><value>override</value></property>
          <!-- age should inherit value of 1 from parent -->
          </bean>
          bean > <property ><value>parent</value></property> <property ><value>1</value></property> </bean> <bean parent="inheritedTestBeanWithoutClass" init-method="initialize"> <property ><value>override</value></property> <!-- age should inherit value of 1 from parent --> </bean>
          
          

          這個父bean就無法自己實例化;它實際上僅僅是一個純模版或抽象bean,充當子定義的父定義。 若要嘗試單獨使用這樣的父bean(比如將它作為其他bean的ref屬性而引用,或者直接使用這個父 bean的id調用getBean()方法),將會導致一個錯誤。同樣地,容器內部的preInstantiateSingletons方法會完全忽略這種既沒有parent屬性也沒有class屬性的bean定義,因為它們是不完整的。

          特別注意:這里并沒有辦法顯式地聲明一個bean定義為抽象的。 如果一個bean確實有一個class屬性定義,那么它就能夠被實例化。而且要注意 XmlBeanFactory默認地將會預實例化所有的singleton的bean。 因此很重要的一點是:如果你有一個(父)bean定義指定了class屬性,而你又想僅僅把它當作模板使用, 那么你必須保證將lazy-init屬性設置為true(或者將bean標記為non-singleton),否則 XmlBeanFactory(以及其他可能的容器)將會預實例化它。

          3.6. BeanFactory之間的交互

          BeanFactory本質上不過是高級工廠的接口,它維護不同bean和它們所依賴的bean的注冊。 BeanFactory使得你可以利用 bean工廠讀取和訪問bean定義。 當你使用BeanFactory的時候,你可以象下面一樣創建并且讀入一些XML格式的bean定義:

          InputStream is = new FileInputStream("beans.xml");
          XmlBeanFactory factory = new XmlBeanFactory(is);

          基本上這就足夠了。使用getBean(String)你可以取得你的bean的實例。 如果你將它定義為一個singleton(缺省的)你將會得到同一個bean的引用, 如果你將singleton設置為false,那么你將會每次得到一個新的實例。 在客戶端的眼里BeanFactory是驚人的簡單。BeanFactory接口僅僅為客戶端調用提供了5個方法:

          • boolean containsBean(String): 如果BeanFactory包含一個與所給名稱匹配的bean定義,則返回true

          • Object getBean(String): 返回一個以所給名字注冊的bean的實例。返回一個singleton的共享的實例還是一個新創建的實例, 這取決于bean在BeanFactory配置中如何被配置的。一個BeansException將會在下面兩種情況中拋出:bean沒有被找到(在這種情況下,拋出的是NoSuchBeanDefinitionException),或者在實例化和準備bean的時候發生異常

          • Object getBean(String,Class): 返回一個以給定名字注冊的bean。返回的bean將會被強制類型轉換成給定的Class。 如果bean不能被類型轉換,相應的異常將會被拋出(BeanNotOfRequiredTypeException)。 此外getBean(String)的所有規則也同樣適用這個方法(同上)

          • boolean isSingleton(String): 判斷以給定名字注冊的bean定義是一個singleton還是一個prototype。 如果與給定名字相應的bean定義沒有被找到,將會拋出一個異常(NoSuchBeanDefinitionException

          • String[] getAliases(String):如果給定的bean名字在bean定義中有別名,則返回這些別名

          3.6.1. 獲得一個FactoryBean而不是它生成的bean

          有些時候我們需要向BeanFactory請求實際的FactoryBean實例本身, 而不是它生產出來的bean。在調用BeanFactory (包括ApplicationContext)的getBean方法的時候, 在傳入的參數bean id前面加一個“&”符號,就可以做到這一點。所以,對于一個id為getBean的FactoryBean, 在BeanFactory上調用getBean("myBean")將會返回FactoryBean的產品,而調用getBean("&myBean")將會返回這個FactoryBean實例本身。

          3.7. 使用BeanPostprocessors定制bean

          一個bean post-processor是一個實現了org.springframework.beans.factory.config.BeanPostProcessor的類,它包含兩個回調方法。當這樣的一個類作為BeanFactory的post-processor注冊時,對于這個BeanFactory創建的每一個bean實例,在任何初始化方法(afterPropertiesSet和用init-method屬性聲明的方法)被調用之前和之后,post-processor將會從BeanFactory分別得到一個回調。post-processor可以對這個bean做任何操作事情,包括完全忽略這個回調。一個bean post-processor通常用來檢查標記接口,或者做一些諸如將一個bean包裝成一個proxy的事情。一些Spring的助手類就是作為bean post-processor而實現的。

          有一點很重要,BeanFactory和ApplicationContext對待bean post-processor有少許不同。 一個ApplicationContext會自動監測到任何部署在它之上的實現了 BeanPostProcessor接口的bean, 并且把它們作為post-processor注冊,然后factory就會在bean創建的時候恰當地調用它們。 部署一個post-processor同部屬一個其他的bean并沒有什么區別。而另一方面,當使用普通的BeanFactory的時候, 作為post-processor的bean必須通過類似下面的代碼來手動地顯式地注冊:

          ConfigurableBeanFactory bf = new .....;     // create BeanFactory
          ...                       // now register some beans
          // now register any needed BeanPostProcessors
          MyBeanPostProcessor pp = new MyBeanPostProcessor();
          bf.addBeanPostProcessor(pp);
          // now start using the factory
          ...

          因為手工的注冊不是很方便,而且ApplicationContext是BeanFactory功能上擴展,所以通常建議當需要post-processor的時候最好使用ApplicationContext。

          3.8. 使用BeanFactoryPostprocessors定制bean工廠

          一個bean factory post-processor是一個實現了org.springframework.beans.factory.config.BeanFactoryPostProcessor接口的類。它將會被手動執行(BeanFactory的時候)或自動執行(ApplicationContext的時候),在BeanFactory構造出來后, 對整個BeanFactory做某種修改。Spring包含很多已存在的bean factory post-processor, 比如PropertyResourceConfigurerPropertyPlaceHolderConfigurer(這兩個將在下面介紹),以及BeanNameAutoProxyCreator對其他bean進行事務包裝或者用其他的proxy進行包裝,將會在后面敘述。BeanFactoryPostProcessor也能用來添加自定義的editor(可參見第 4.3.2 節 “內建的(PropertyEditors)和類型轉換 ”)。

          在BeanFactory中,使用BeanFactoryPostProcessor是手動的,將類似于下面:

          XmlBeanFactory factory = new XmlBeanFactory(new FileSystemResource("beans.xml"));
          // create placeholderconfigurer to bring in some property
          // values from a Properties file
          PropertyPlaceholderConfigurer cfg = new PropertyPlaceholderConfigurer();
          cfg.setLocation(new FileSystemResource("jdbc.properties"));
          // now actually do the replacement
          cfg.postProcessBeanFactory(factory);

          ApplicationContext將會檢測它所部署的實現了BeanFactoryPostProcessor 接口的bean,然后在適當的時候將它們作為bean factory post-processor而使用。部署這些post-processor與部署其他的bean并沒有什么區別。

          因為這個手動的步驟并不方便,而且ApplicationContext是BeanFactory的功能擴展,所以當需要使用bean factory post-processor的時候通常建議使用ApplicationContext

          3.8.1. PropertyPlaceholderConfigurer

          PropertyPlaceholderConfigurer作為一個bean factory post-processor實現,可以用來將BeanFactory定義中的屬性值放置到另一個單獨的Java Properties格式的文件中。 這使得用戶不用對BeanFactory的主XML定義文件進行復雜和危險的修改,就可以定制一些基本的屬性(比如說數據庫的urls,用戶名和密碼)。

          考慮一個BeanFactory定義的片斷,里面用占位符定義了DataSource:

          在下面這個例子中,定義了一個datasource,并且我們會在一個外部Porperties文件中配置一些相關屬性。 在運行時,我們為BeanFactory提供一個PropertyPlaceholderConfigurer,它將用Properties文件中的值替換掉這個datasource的屬性值:

          <bean   destroy-method="close">
          <property ><value>${jdbc.driverClassName}</value></property>
          <property ><value>${jdbc.url}</value></property>
          <property ><value>${jdbc.username}</value></property>
          <property ><value>${jdbc.password}</value></property>
          </bean>

          真正的值來自于另一個Properties格式的文件:

          jdbc.driverClassName=org.hsqldb.jdbcDriver
          jdbc.url=jdbc:hsqldb:hsql://production:9002
          jdbc.username=sa
          jdbc.password=root

          如果要在BeanFactory中使用,bean factory post-processor必須手動運行:

          XmlBeanFactory factory = new XmlBeanFactory(new FileSystemResource("beans.xml"));
          PropertyPlaceholderConfigurer cfg = new PropertyPlaceholderConfigurer();
          cfg.setLocation(new FileSystemResource("jdbc.properties"));
          cfg.postProcessBeanFactory(factory);

          注意,ApplicationContext能夠自動辨認和應用在其上部署的實現了BeanFactoryPostProcessor的bean。這就意味著,當使用ApplicationContext的時候應用PropertyPlaceholderConfigurer會非常的方便。由于這個原因,建議想要使用這個或者其他bean factory postprocessor的用戶使用ApplicationContext代替BeanFactroy。

          PropertyPlaceHolderConfigurer不僅僅在你指定的Porperties文件中查找屬性, 如果它在其中沒有找到你想使用的屬性,它還會在Java的系統properties中查找。 這個行為能夠通過設置配置中的systemPropertiesMode 屬性來定制。這個屬性有三個值, 一個讓配置總是覆蓋,一個讓它永不覆蓋,一個讓它僅在properties文件中找不到的時候覆蓋。 請參考 PropertiesPlaceholderConfigurer的JavaDoc獲得更多信息。

          3.8.2.  PropertyOverrideConfigurer

          另一個bean factory post-processor,PropertyOverrideConfigurer,類似于PropertyPlaceholderConfigurer,但是與后者相比,前者對于bean屬性可以有缺省值或者根本沒有值。如果起覆蓋作用的 Properties文件沒有某個bean屬性的內容,那么缺省的上下文定義將被使用。

          注意:bean 工廠的定義并 不會意識到被覆蓋,所以僅僅察看XML定義文件并不能立刻明顯地知道覆蓋配置是否被使用了。在有多個PorpertyOverrideConfigurer對用一個bean屬性定義了不同的值的時候,最后一個將取勝(取決于覆蓋的機制)。

          Properties文件的一行配置應該是如下的格式:

          beanName.property=value

          一個properties文件的例子將會是下面這樣的:

          dataSource.driverClassName=com.mysql.jdbc.Driver
          dataSource.url=jdbc:mysql:mydb

          這個例子可以用在包含dataSource的bean的BeanFactory中,這個bean有driverurl屬性。

          3.9. 注冊附加的定制PropertyEditor

          當用字符串值設置bean的屬性時,BeanFactory實質上使用了標準的JavaBeans 的PropertyEditor將這些String轉換為屬性的復雜類型。Spring預先注冊了很多定制的PropertyEditor(比如,將一個字符串表示的classname轉換成真正的Class對象)。另外,一個類的PropertyEditor如果被恰當地命名并且放在與它提供支持的類同一個包內,那么Java標準的JavaBeans PropertyEditor查找機制就會自動找到這個PropertyEditor。

          如果需要注冊其他定制的PropertyEditor,這里也有幾個可用的機制。

          最手動的方法,也是通常不方便和不推薦的,就是簡單地使用ConfigurableBeanFactory接口的registerCustomEditor()方法,假定你已經有一個BeanFactory引用。

          比較方便的機制是,使用一個特殊的叫CustomEditorConfigurer的bean factory post-precessor。盡管bean factory post-processor能夠和BeanFactory半手動地使用,但是它有一個嵌套的屬性設置。所以強烈建議,就象這里描述的,它能夠和ApplicationContxt一起使用,這樣它就可以以其它bean的方式被部署,并且被自動監測和應用。

          3.10. 介紹ApplicationContext

          beans包提供了以編程的方式管理和操控bean的基本功能,而context包增加了ApplicationContext,它以一種更加面向框架的方式增強了BeanFactory的功能。多數用戶可以以一種完全的聲明式方式來使用ApplicationContext,甚至不用去手動創建它,但是卻去依賴象ContextLoader的支持類,在J2EE的的web應用的啟動進程中用它啟動ApplicationContext。當然,這種情況下還是可以以編程的方式創建一個ApplicationContext。

          Context包的基礎是位于org.springframework.context包中的ApplicationContext接口。它是由BeanFactory接口集成而來,提供BeanFactory所有的功能。為了以一種更向面向框架的方式工作,context包使用分層和有繼承關系的上下文類,包括:

          • MessageSource, 提供對i18n消息的訪問

          • 資源訪問, 比如URL和文件

          • 事件傳遞給實現了ApplicationListener接口的bean

          • 載入多個(有繼承關系)上下文類,使得每一個上下文類都專注于一個特定的層次,比如應用的web層

          因為ApplicationContext包括了BeanFactory所有的功能,所以通常建議先于BeanFactory使用,除了有限的一些場合比如在一個Applet中,內存的消耗是關鍵的,每kb字節都很重要。接下來的章節將敘述ApplicationContext在BeanFactory的基本能力上增建的功能。

          3.11. ApplicationContext中增加的功能

          正如上面說明的那樣,ApplicationContext有幾個區別于BeanFactory的特性。讓我們一個一個地討論它們。

          3.11.1. 使用MessageSource

          ApplicationContext接口繼承MessageSource接口,所以提供了messaging功能(i18n或者國際化)。同NestingMessageSource一起使用,就能夠處理分級的信息,這些是Spring提供的處理信息的基本接口。讓我們很快瀏覽一下這里定義的方法:

          • String getMessage (String code, Object[] args, String default, Locale loc):這個方法是從MessageSource取得信息的基本方法。如果對于指定的locale沒有找到信息,則使用默認的信息。傳入的參數args被用來代替信息中的占位符,這個是通過Java標準類庫的MessageFormat實現的。

          • String getMessage (String code, Object[] args, Locale loc):本質上和上一個方法是一樣的,除了一點區別:沒有默認值可以指定;如果信息找不到,就會拋出一個NoSuchMessageException

          • String getMessage(MessageSourceResolvable resolvable, Locale locale):上面兩個方法使用的所有屬性都是封裝到一個叫做MessageSourceResolvable的類中,你可以通過這個方法直接使用它。

          當ApplicationContext被加載的時候,它會自動查找在context中定義的MessageSource bean。這個bean必須叫做messageSource。如果找到了這樣的一個bean,所有對上述方法的調用將會被委托給找到的message source。如果沒有找到message source,ApplicationContext將會嘗試查它的父親是否包含這個名字的bean。如果有,它將會把找到的bean作為MessageSource。如果它最終沒有找到任何的信息源,一個空的StaticMessageSource將會被實例化,使它能夠接受上述方法的調用。

          Spring目前提供了兩個MessageSource的實現。它們是ResourceBundleMessageSourceStaticMessageSource。兩個都實現了NestingMessageSource以便能夠嵌套地解析信息。StaticMessageSource很少被使用,但是它提供以編程的方式向source增加信息。ResourceBundleMessageSource用得更多一些,我們將提供它的一個例子:

          <beans>
          <bean
          >
          <property >
          <list>
          <value>format</value>
          <value>exceptions</value>
          <value>windows</value>
          </list>
          </property>
          </bean>
          </beans> 

          這段配置假定你在classpath有三個resource bundle,分別叫做fformatexceptionswindows。使用JDK通過ResourceBundle解析信息的標準方式,任何解析信息的請求都會被處理。TODO:舉一個例子

          3.11.2. 事件傳遞

          ApplicationContext中的事件處理是通過ApplicationEvent類和ApplicationListener接口來提供的。如果上下文中部署了一個實現了ApplicationListener接口的bean,每次一個ApplicationEvent發布到ApplicationContext時,那個bean就會被通知。實質上,這是標準的Observer設計模式。Spring提供了三個標準事件:

          表 3.4. 內置事件

          事件 解釋
          ContextRefreshedEvent 當ApplicationContext已經初始化或刷新后發送的事件。這里初始化意味著:所有的bean被裝載,singleton被預實例化,以及ApplicationContext已準備好
          ContextClosedEvent 當使用ApplicationContext的close()方法結束上下文的時候發送的事件。這里結束意味著:singleton被銷毀
          RequestHandledEvent 一個與web相關的事件,告訴所有的bean一個HTTP請求已經被響應了(這個事件將會在一個請求結束被發送)。注意,這個事件只能應用于使用了Spring的DispatcherServlet的web應用

          同樣也可以實現自定義的事件。通過調用ApplicationContext的publishEvent()方法,并且指定一個參數,這個參數是你自定義的事件類的一個實例。我們來看一個例子。首先是ApplicationContext:

          <bean  >
          <property >
          <list>
          <value>black@list.org</value>
          <value>white@list.org</value>
          <value>john@doe.org</value>
          </list>
          </property>
          </bean>
          <bean  >
          <property >
          <value>spam@list.org</value>
          </property>
          </bean>

          然后是實際的bean:

          public class EmailBean implements ApplicationContextAware {
          /** the blacklist */
          private List blackList;
          public void setBlackList(List blackList) {
          this.blackList = blackList;
          }
          public void setApplicationContext(ApplicationContext ctx) {
          this.ctx = ctx;
          }
          public void sendEmail(String address, String text) {
          if (blackList.contains(address)) {
          BlackListEvent evt = new BlackListEvent(address, text);
          ctx.publishEvent(evt);
          return;
          }
          // send email
          }
          }
          public class BlackListNotifier implement ApplicationListener {
          /** notification address */
          private String notificationAddress;
          public void setNotificationAddress(String notificationAddress) {
          this.notificationAddress = notificationAddress;
          }
          public void onApplicationEvent(ApplicationEvent evt) {
          if (evt instanceof BlackListEvent) {
          // notify appropriate person
          }
          }
          }

          當然,這個特定的例子或許可以用更好的方式實現(或許使用AOP特性),但是它用來說明基本的事件機制是足夠了。

          3.11.3. 在Spring中使用資源

          很多應用程序都需要訪問資源。資源可以包括文件,以及象web頁面或NNTP newsfeeds的東西。Spring提供了一個清晰透明的方案,以一種協議無關的方式訪問資源。ApplicationContext接口包含一個方法(getResource(String))負責這項工作。

          Resource類定義了幾個方法,這幾個方法被所有的Resource實現所共享:

          表 3.5. 資源功能

          方法 解釋
          getInputStream() 用InputStream打開資源,并返回這個InputStream。
          exists() 檢查資源是否存在,如果不存在返回false
          isOpen() 如果這個資源不能打開多個流將會返回true。因為除了基于文件的資源,一些資源不能被同事多次讀取,它們就會返回false。
          getDescription() 返回資源的描述,通常是全限定文件名或者實際的URL。

          Spring提供了幾個Resource的實現。它們都需要一個String表示的資源的實際位置。依據這個String,Spring將會自動為你選擇正確的Resource實現。當向ApplicationContext請求一個資源時,Spring首先檢查你指定的資源位置,尋找任何前綴。根據不同的ApplicationContext的實現,不同的Resource實現可被使用的。Resource最好是使用ResourceEditor來配置,比如XmlBeanFactory。

          3.12. 在ApplicationContext中定制行為

          BeanFactory已經提供了許多機制來控制部署在其中的bean的生命周期(比如象InitializingBeanDisposableBean的標志接口,它們的配置效果和XmlBeanFactory配置中的init-methoddestroy-method屬性以及bean post-processor是相同的。在ApplicationContext中,這些也同樣可以工作,但同時也增加了一些用于定制bean和容器行為的機制。

          3.12.1. ApplicationContextAware標記接口

          所有BeanFactory中可用的標志接口這里也可以使用。ApplicationContext又增加了一個bean可以實現的標志接口:org.springframework.context.ApplicationContextAware。如果一個bean實現了這個接口并且被部署到了context中,當這個bean創建的時候,將使用這個接口的setApplicationContext()方法回調這個bean,為這個bean提供一個指向當前上下文的引用,這個引用將被存儲起來以便bean以后與上下文發生交互。

          3.12.2. BeanPostProcessor

          Bean post-processor(實現了org.springframework.beans.factory.config.BeanPostProcessor的java類)在上面已經敘述過了。還是值得再次提到,post-processor在ApplicationContext中使用要比在普通的BeanFactory中使用方便得多。在一個ApplicationContext中,一個實現了上述標記接口的bean將會被自動查找,并且作為一個bean post-processor被注冊,在創建工廠中的每一個bean時都會被適當地調用。

          3.12.3. BeanFactoryPostProcessor

          Bean factory post-processor(實現了org.springframework.beans.factory.config.BeanFactoryPostProcessor接口的java類)在前面已經介紹過。這里也值得再提一下,bean factory post-processor在ApplicationContext中使用也要比在普通的BeanFactory中使用方便得多。在一個ApplicationContext中,一個實現了上述標記接口的bean將會被自動查找,并且作為一個bean factory post-processor被注冊,在適當的時候被調用。

          3.12.4. PropertyPlaceholderConfigurer

          PropertyPlaceholderConfigurer在BeanFactory中的使用已經敘述過了。這里仍然值得再次提一下,它在ApplicationContext中的使用要更加方便一些,因為當它像其它bean一樣部署的時候,上下文會自動識別和應用任何的bean factory post-processor,比如這個。這時候就沒有必要手動地運行它。

          <!-- property placeholder post-processor -->
          <bean
          >
          <property ><value>jdbc.properties</value></property>
          </bean>

          3.13. 注冊附加的定制PropertyEditors

          正如前面曾提到的,Spring使用標準的JavaBeans的PropertyEditor將字符串形式表示的屬性值轉換為屬性真實的復雜的類型。CustomEditorConfigurer,這個bean factory post-processor可以使ApplicationContext很方便地增加對額外的PropertyEditor的支持。

          考慮一個用戶類ExoticType,以及另外一個需要ExoticType作為屬性的DependsOnExoticType類:

          public class ExoticType {
          private String name;
          public ExoticType(String name) {
          this.name = name;
          }
          }
          public class DependsOnExoticType {
          private ExoticType type;
          public void setType(ExoticType type) {
          this.type = type;
          }
          }

          當設置好的時候,我們希望能夠以字符串的形式指定屬性,同時背后的PropertyEditor會將這個字符串轉換為一個真正的Exotic類型的對象:

          <bean  >
          <property ><value>aNameForExoticType</value></property>
          </bean>

          而這個PorpertyEditor看起來象下面這樣:

          // converts string representation to ExoticType object
          public class ExoticTypeEditor extends PropertyEditorSupport {
          private String format;
          public void setFormat(String format) {
          this.format = format;
          }
          public void setAsText(String text) {
          if (format != null && format.equals("upperCase")) {
          text = text.toUpperCase();
          }
          ExoticType type = new ExoticType(text);
          setValue(type);
          }
          }

          最后,我們用CustomEditorConfigurer將新的PropertyEditor注冊到ApplicationContext上,然后ApplicationContext就可以在需要的時候使用這個PropertyEditor了:

          <bean
          >
          <property >
          <map>
          <entry key="example.ExoticType">
          <bean >
          <property >
          <value>upperCase</value>
          </property>
          </bean>
          </entry>
          </map>
          </property>
          </bean>

          3.14. 用方法調用的返回值來設置bean的屬性

          有些時候,需要用容器中另一個bean的方法返回值來設置一個bean的屬性,或者使用其它任意類(不一定是容器中的bean)的靜態方法的返回值來設置。此外,有些時候,需要調用一個靜態或非靜態的方法來執行某些初始化工作。對于這兩個目的,可以使用MethodInvokingFactoryBean助手類。它是一個FactoryBean,可以返回一個靜態或非靜態方法的調用結果。

          下面是一個基于XML的BeanFactory的bean定義的例子,它使用那個助手類調用靜態工廠方法:

          <bean  >
          <property ><value>com.whatever.MyClassFactory.getInstance</value></property>
          </bean>

          下面這個例子先調用一個靜態方法,然后調用一個實例方法,來獲得一個Java System的屬性。雖然有點羅嗦,但是可以工作:

          <bean  >
          <property ><value>java.lang.System</value></property>
          <property ><value>getProperties</value></property>
          </bean>
          <bean  >
          <property ><ref local="sysProps"/></property>
          <property ><value>getProperty</value></property>
          <property >
          <list>
          <value>java.version</value>
          </list>
          </property>
          </bean>

          注意,實際上這個類多半用來訪問工廠方法,所以MethodInvokingFactoryBean默認以singleton方式進行操作。經由容器的第一次請求工廠生成對象將會引起調用特定的工廠方法,它的返回值將會被緩存并且返回供這次請求和以后的請求使用。這個工廠的一個內部singleton屬性可以被設置為false,從而導致每次對象的請求都會調用那個目標方法。

          通過設置targetMethod屬性為一個靜態方法名的字符串來指定靜態目標方法,而設置targetClass為定義靜態方法的類。或者,通過設置targetObject屬性目標對象,設置targetMethod屬性要在目標對象上調用的方法名,來指定目標實例方法。方法調用的參數可以通過設置args屬性來指定。

          3.15. 從一個web應用創建ApplicationContext

          與BeanFactory總是以編程的方式創建相反,ApplicationContext可以通過使用比如ContextLoader聲明式地被創建。當然你也可以用ApplicationContext的任一種實現來以編程的方式創建它。首先,我們來看看ContextLoader以及它的實現。

          ContextLoader有兩個實現:ContextLoaderListenerContextLoaderServlet。它們兩個有著同樣的功能,除了listener不能在Servlet 2.2兼容的容器中使用。自從Servelt 2.4規范,listener被要求在web應用啟動后初始化。很多2.3兼容的容器已經實現了這個特性。使用哪一個取決于你自己,但是如果所有的條件都一樣,你大概會更喜歡ContextLoaderListener;關于兼容方面的更多信息可以參照ContextLoaderServlet的JavaDoc。

          你可以象下面這樣用ContextLoaderListener注冊一個ApplicationContext:

          <context-param>
          <param-name>contextConfigLocation</param-name>
          <param-value>/WEB-INF/daoContext.xml /WEB-INF/applicationContext.xml</param-value>
          </context-param>
          <listener>
          <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
          </listener>
          <!-- OR USE THE CONTEXTLOADERSERVLET INSTEAD OF THE LISTENER
          <servlet>
          <servlet-name>context</servlet-name>
          <servlet-class>org.springframework.web.context.ContextLoaderServlet</servlet-class>
          <load-on-startup>1</load-on-startup>
          </servlet>
          -->

          這個listener需要檢查contextConfigLocation參數。如果不存在的話,它將默認使用/WEB-INF/applicationContext.xml。如果它存在,它就會用預先定義的分隔符(逗號,分號和空格)分開分割字符串,并將這些值作為應用上下文將要搜索的位置。ContextLoaderServlet可以用來替換ContextLoaderListener。這個servlet像listener那樣使用contextConfigLocation參數。

          3.16. 粘合代碼和罪惡的singleton

          一個應用中的大多數代碼最好寫成依賴注射(反向控制)的風格,這樣這些代碼就和BeanFactory容器或者ApplicationContext容器無關,它們在被創建的時候從容器處得到自己的依賴,并且完全不知道容器的存在。然而,對于少量需要與其它代碼粘合的粘合層代碼來說,有時候就需要以一種singleton(或者類似singleton)的方式來訪問BeanFactory或ApplicationContext。舉例來說,第三方的代碼可能想要(以Class.forName()的方式)直接構造一個新的對象,卻沒辦法從BeanFactory中得到這些對象。如果第三方代碼構造的對象只是一個小的stub或proxy,并且使用singleton方式訪問BeanFactroy/ApplicationContext來獲得真正的對象,大多數的代碼(由BeanFactory產生的對象)仍然可以使用反向控制。因此大多數的代碼依然不需要知道容器的存在,或者容器是如何被訪問的,并保持和其它代碼解耦,有著所有該有的益處。EJB也可以使用這種stub/proxy方案代理到一個普通的BeanFactory產生的java實現的對象。雖然理想情況下BeanFactory不需要是一個singleton,但是如果每個bean使用它自己的non-singleton的BeanFactory,對于內存使用或初始化次數都是不切實際。

          另一個例子,在一個多層的復雜的J2EE應用中(比如有很多JAR,EJB,以及WAR打包成一個EAR),每一層都有自己的ApplicationContext定義(有效地組成一個層次結構),如果頂層只有一個web-app(WAR)的話,比較好的做法是創建一個由不同層的XML定義文件組成的組合ApplicationContext。所有的ApplicationContext變體都可以從多個定義文件以這種方式構造出來。但是,如果在頂層有多個兄弟web-apps,為每一個web-app創建一個ApplicationContext,但是每個ApplicationContext都包含大部分相同的底層的bean定義,這就會因為內存使用而產生問題,因為創建bean的多個copy會花很長時間初始化(比如Hibernate SessionFactory),以及其它可能產生的副作用。作為替換,諸如ContextSingletonBeanFactoryLocatorSingletonBeanFactoryLocator的類可以在需要的時候以有效的singleton方式,加載多個層次的(比如一個是另一個的父親)BeanFactory或ApplicationContext,這些將會作為web-app ApplicationContext的parents。這樣做的結果就是,底層的bean定義只在需要的時候加載并且只被加載一次。

          3.16.1. 使用SingletonBeanFactoryLocator和ContextSingletonBeanFactoryLocator

          你可以查看SingletonBeanFactoryLocatorContextSingletonBeanFactoryLocator的JavaDoc來獲得詳細的使用例子。

          正如在EJB那一章提到的,Spring為EJB提供的方便的基類一般使用一個non-singleton的BeanFactoryLocator實現,這個可以在需要的時候被SingletonBeanFactoryLocatorContextSingletonBeanFactoryLocator替換。

          posted on 2008-07-17 10:34 蘆葦 閱讀(564) 評論(0)  編輯  收藏 所屬分類: Spring
          主站蜘蛛池模板: 呈贡县| 唐海县| 郴州市| 辽中县| 宁夏| 乾安县| 云林县| 宜春市| 邮箱| 嵊泗县| 石棉县| 靖江市| 儋州市| 洛浦县| 长春市| 鄂托克旗| 宜阳县| 平罗县| 平和县| 凌海市| 常熟市| 黎川县| 临漳县| 马公市| 惠来县| 株洲县| 平原县| 如东县| 山阳县| 卢氏县| 眉山市| 伊金霍洛旗| 临澧县| 萝北县| 大新县| 犍为县| 武川县| 黄平县| 浦江县| 连江县| 崇阳县|