posts - 495,  comments - 11,  trackbacks - 0

          6.1 兩種后處理器

          Spring 框架提供了很好的擴展性,除了可以與各種第三方框架良好整合外,其IoC容器也允許開發者進行擴展。這種擴展并不是通過實現BeanFactory或ApplicationContext的子類,而是通過兩個后處理器對IoC容器進行擴展。Spring提供了兩種常用的后處理器:

          ?? ● Bean后處理器,這種后處理器會對容器中特定的Bean進行定制,例如功能的??? 加強。

          ?? ● 容器后處理器,這種后處理器對IoC容器進行特定的后處理。

          下面將介紹這兩種常用的后處理器以及兩種后處理器相關知識。

          6.1.1 Bean后處理器

          Bean后處理器是一種特殊的Bean,這種特殊的Bean并不對外提供服務,它無須id屬性,但它負責對容器中的其他Bean執行后處理,例如為容器中的目標Bean生成代理。這種Bean可稱為Bean后處理器,它在Bean實例創建成功后,對其進行進一步的加強???? 處理。

          Bean后處理器必須實現BeanPostProcessor接口。

          BeanPostProcessor接口包含兩個方法:

          ?? ● Object postProcessBeforeInitialization(Object bean, String name)throws BeansExce- ption,該方法的第一個參數是系統即將初始化的Bean實例,第二個參數是Bean實例的名字。

          ?? ● Object postProcessAfterInitialization(Object bean, String name)throws BeansExce- ption,該方法的第一個參數是系統剛完成初始化的Bean實例,第二個參數是Bean實例的名字。

          實現該接口的Bean必須實現這兩個方法,這兩個方法會對容器的Bean進行后處理。兩個方法會在目標Bean初始化之前和初始化之后分別調用。這兩個方法用于對系統完成的默認初始化進行加強。

          注意:Bean后處理器是對IoC容器一種極好的擴展,Bean后處理器可以對容器中的Bean進行后處理,這種后處理完全由開發者決定。

          下面將定義一個簡單的Bean后處理器,該Bean后處理器將對容器中其他Bean進行后處理。Bean后處理器的代碼如下:

          //自定義Bean后處理器,負責后處理容器中所有的Bean

          public class MyBeanPostProcessor implements BeanPostProcessor

          {

          ??? //在初始化bean之前,調用該方法

          ??? public Object postProcessBeforeInitialization(Object bean , String
          ??? beanName)throws BeansException

          ??? {

          ??????? //僅僅打印一行字符串

          ??????? System.out.println("系統正在準備對" + beanName + "進行初始化...");

          ??????? return bean;

          ??? }

          ??? //在初始化bean之后,調用該方法

          ??? public Object postProcessAfterInitialization(Object bean , String
          ??? beanName)throws BeansException

          ??? {

          ??????? System.out.println("系統已經完成對" + beanName + "的初始化");

          ??????? //如果系統剛完成初始化的bean是Chinese

          ??????? if (bean instanceof Chinese)

          ??????? {

          ??????????? //為Chinese實例設置name屬性

          ??????????? Chinese c = (Chinese)bean;

          ??????????? c.setName("wawa");

          ????? ?? }

          ??????? return bean;

          ??? }

          }

          下面是Chinese的源代碼,該類實現了InitializingBean接口,還額外提供了一個初始化方法,這兩個方法都由Spring容器控制回調。

          public class Chinese implements Person,InitializingBean

          {

          ??? private Axe axe;

          ??? private String name;

          ??? public Chinese()

          ??? {

          ??????? System.out.println("Spring實例化主調bean:Chinese實例...");

          ??? }

          ??? public void setAxe(Axe axe)

          ??? {

          ??????? System.out.println("Spring執行依賴關系注入...");

          ??????? this.axe = axe;

          ??? }

          ??? public void setName(String name)

          ??? {

          ??????? this.name = name;

          ??? }

          ??? public void useAxe()

          ??? {

          ??????? System.out.println(name + axe.chop());

          ??? }

          ??? public void init()

          ??? {

          ??????? System.out.println("正在執行初始化方法?? init...");

          ??? }

          ?? public void afterPropertiesSet() throws Exception

          ??? {

          ?????? System.out.println("正在執行初始化方法 afterPropertiesSet...");

          ??? }

          }

          配置文件如下:

          <?xml version="1.0" encoding="gb2312"?>

          <!-- 指定Spring 配置文件的dtd>

          <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"

          ??? "http://www.springframework.org/dtd/spring-beans.dtd">

          <!-- Spring配置文件的根元素 -->

          <beans>

          ??? <!-- 配置bean后處理器,可以沒有id屬性,此處id屬性為了后面引用 -->

          ??? <bean id="beanPostProcessor" class="lee.MyBeanPostProcessor"/>

          ??? <bean id="steelAxe" class="lee.SteelAxe"/>

          ??? <bean id="chinese" class="lee.Chinese" init-method="init">

          ??????? <property name="axe" ref="steelAxe"/>

          ??? </bean>

          </beans>

          本應用的chinese具有兩個初始化方法:

          ?? ● init-method指定初始化方法。

          ?? ● 實現InitializingBean接口,提供了afterPropertiesSet初始化方法。

          MyBeanPostProcessor類實現了BeanPostProcessor接口,并實現了該接口的兩個方法,這兩個方法分別在初始化方法調用之前和之后得到回調。

          注意:上面的配置文件配置Bean后處理器時,依然為Bean處理器指定了id屬性,指定id屬性是為了方便程序通過該id屬性訪問Bean后處理器。大部分時候,程序無須手動訪問該Bean后處理器,因此無須為其指定id屬性。

          主程序如下:

          public class BeanTest

          {

          ??? public static void main(String[] args)throws Exception

          ??? {

          ??????? //CLASSPATH路徑下的bean.xml文件創建Resource對象

          ??????? ClassPathResource isr = new ClassPathResource("bean.xml");

          ??????? //以Resource對象作為參數,創建BeanFactory的實例

          ??????? XmlBeanFactory factory = new XmlBeanFactory(isr);

          ??????? //獲取Bean后處理器實例

          ??????? MyBeanPostProcessor beanProcessor =

          ??????????? (MyBeanPostProcessor)factory.getBean("beanPostProcessor");

          ??????? //注冊BeanPostProcessor實例

          ??????? factory.addBeanPostProcessor(beanProcessor);

          ??????? System.out.println("程序已經實例化BeanFactory...");

          ??????? Person p = (Person)factory.getBean("chinese");

          ??????? System.out.println("程序中已經完成了chinese bean的實例化...");

          ??????? p.useAxe();

          ??? }

          }

          如果使用BeanFactory作為Spring容器,必須手動注冊Bean后處理器,因此在程序中先獲取Bean后處理器實例,然后手動注冊——這就是在配置文件中指定Bean后處理器id屬性的原因。通過BeanFactory的addBeanPostProcessor可以注冊BeanPostProcessor實例。程序執行結果如下:

          [java] 程序已經實例化BeanFactory...

          [java] Spring實例化主調bean:Chinese實例...

          [java] Spring實例化依賴bean:SteelAxe實例...

          [java] 系統正在準備對steelAxe進行初始化...

          [java] 系統已經完成對steelAxe的初始化

          [java] Spring執行依賴關系注入...

          [java] 系統正在準備對chinese進行初始化...

          [java] 正在執行初始化方法 afterPropertiesSet...

          [java] 正在執行初始化方法?? init...

          [java] 系統已經完成對chinese的初始化

          [java] 程序中已經完成了chinese bean的實例化...

          [java] wawa鋼斧砍柴真快

          在配置文件中配置chinese實例時,并未指定name屬性值。但程序執行時,name屬性有了值,這就是Bean后處理器完成的,在Bean后處理器中判斷Bean是否是Chinese實例,然后設置它的name屬性。

          容器中一旦注冊了Bean后處理器,Bean后處理器會自動啟動,在容器中每個Bean創建時自動工作,完成加入Bean后處理器需要完成的工作。

          實現BeanPostProcessor接口的Bean后處理器可對Bean進行任何操作,包括完全忽略這個回調。BeanPostProcessor通常用來檢查標記接口或將Bean包裝成一個Proxy的事情。Spring的很多工具類,就是通過Bean后處理器完成的。

          從主程序中看到,采用BeanFactory作為Spring容器時,必須手動注冊BeanPost- Processor。而對于ApplicationContext,則無須手動注冊。ApplicationContext可自動檢測到容器中的Bean后處理器,自動注冊。Bean后處理器會在Bean實例創建時,自動啟動。即主程序采用如下代碼,效果完全一樣:

          public class BeanTest

          {

          ??? public static void main(String[] args)throws Exception

          ??? {

          ??????? ApplicationContext ctx = new ClassPathXmlApplicationContext
          ??????? ("bean.xml");

          ??????? Person p = (Person)factory.getBean("chinese");

          ??????? System.out.println("程序中已經完成了chinese bean的實例化...");

          ??????? p.useAxe();

          ??? }

          }

          使用ApplicationContext作為容器,無須手動注冊BeanPostProcessor。因此,如果需要使用Bean后處理器,Spring容器建議使用ApplicationContext,而不是BeanFactory。

          6.1.2 Bean后處理器的用處

          上一節介紹了一個簡單的Bean后處理器,上面的Bean后處理器負責對容器中的Chinese Bean進行后處理,不管Chinese Bean如何初始化,總是將Chinese Bean的name屬性設置為wawa。這種后處理看起來作用并不是特別大。

          實際上,Bean后處理器完成的工作更加實際,例如生成Proxy。Spring框架本身提供了大量的Bean后處理器,這些后處理器負責對容器中的Bean進行后處理。

          下面是Spring提供的兩個常用的后處理器:

          ?? ● BeanNameAutoProxyCreator,根據Bean實例的name屬性,創建Bean實例的代理。

          ?? ● DefaultAdvisorAutoProxyCreator,根據提供的Advisor,對容器中所有的Bean實例創建代理。

          上面提供的兩個Bean后處理器,都用于根據容器中配置的攔截器創建目標Bean代理,目標代理就在目標Bean的基礎上修改得到。

          注意:如果需要對容器中某一批Bean進行特定的處理,可以考慮使用Bean后處理器。

          6.1.3 容器后處理器

          除了上面提供的Bean后處理器外,Spring還提供了一種容器后處理器。Bean后處理器負責后處理容器生成的所有Bean,而容器后處理器則負責后處理容器本身。

          容器后處理器必須實現BeanFactoryPostProcessor接口。實現該接口必須實現如下一個方法:

          void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)

          實現該方法的方法體就是對Spring容器進行的處理,這種處理可以對Spring容器進行任意的擴展,當然也可以對Spring容器不進行任何處理。

          類似于BeanPostProcessor,ApplicationContext可自動檢測到容器中的容器后處理器,并且自動注冊容器后處理器。但若使用BeanFactory作為Spring容器,則必須手動注冊后處理器。

          下面定義了一個容器后處理器,這個容器后處理器實現BeanFactoryPostProcessor接口,但并未對Spring容器進行任何處理,只是打印出一行簡單的信息。該容器后處理器的代碼如下:

          public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor

          {

          ??? //容器后處理器對容器進行的處理在該方法中實現

          ??? public void postProcessBeanFactory(ConfigurableListableBeanFactory
          ??? beanFactory)

          ??????? throws BeansException

          ??? {

          ??????? System.out.println("程序對Spring所做的BeanFactory的初始化沒有意
          ??????? 見...");

          ??? }

          }

          將該Bean作為普通Bean部署在容器中,然后使用ApplicationContext作為容器,容器會自動調用BeanFactoryPostProcessor處理Spring容器。程序執行效果如下:

          [java] 程序對Spring所做的BeanFactory的初始化沒有意見...

          實現BeanFactoryPostProcessor接口的Bean后處理器不僅可對BeanFactory執行后處理,也可以對ApplicationContext容器執行后處理。容器后處理器還可用來注冊額外的屬性編輯器。

          注意:Spring沒有提供ApplicationContextPostProcessor。也就是說,對于Application- Context容器,一樣使用BeanFactoryPostProcessor作為容器后處理器。

          Spring已提供如下兩個常用的容器后處理器,包括:

          ?? ● PropertyResourceConfigurer,屬性占位符配置器。

          ?? ● PropertyPlaceHolderConfigurer,另一種屬性占位符配置器。

          下面將詳細介紹這兩種常用的容器后處理器。

          6.1.4 屬性占位符配置器

          Spring提供了PropertyPlaceholderConfigurer,它是一個容器后處理器,負責讀取Java屬性文件里的屬性值,并將這些屬性值設置到Spring容器定義中。

          通過使用PropertyPlaceholderConfigurer后處理器,可以將Spring配置文件中的部分設置放在屬性文件中設置。這種配置方式當然有其優勢:可以將部分相似的配置(如數據庫的urls、用戶名和密碼)放在特定的屬性文件中,如果只需要修改這部分配置,則無須修改Spring配置文件,修改屬性文件即可。

          下面的配置文件配置了PropertyPlaceholderConfigurer后處理器,在配置數據源Bean時,使用了屬性文件中的屬性值。配置文件的代碼如下:

          <?xml version="1.0" encoding="GBK"?>

          <!-- beans是Spring配置文件的根元素,并且指定了Schema信息 -->

          <beans xmlns="http://www.springframework.org/schema/beans"

          ?????? xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

          ?????? xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

          ??? <!-- 配置一個容器后處理器Bean -->

          ??? <bean id="propertyConfigurer"

          ??????? class="org.springframework.beans.factory.config.
          ??????? PropertyPlaceholderConfigurer">

          ??????? <!-- locations屬性指定屬性文件的位置 -->

          ??????? <property name="locations">

          ??????????? <list>

          ??????????????? <value>dbconn.properties</value>

          ??????????????? <!-- 如果有多個屬性文件,依次在下面列出來 -->

          ??????????? </list>

          ??????? </property>

          ??? </bean>

          ??? <!-- 定義數據源Bean,使用C3P0數據源實現 -->

          ??? <bean id="dataSource" class="com.mchange.v2.c3p0.
          ??? ComboPooledDataSource" destroy-method="close">

          ??????? <!-- 指定連接數據庫的驅動 -->

          ??????? <property name="driverClass" value="${jdbc.driverClassName}"/>

          ??????? <!-- 指定連接數據庫的URL -->

          ??????? <property name="jdbcUrl" value="${jdbc.url}"/>

          ??????? <!-- 指定連接數據庫的用戶名 -->

          ??????? <property name="user" value="${jdbc.username}"/>

          ??????? <!-- 指定連接數據庫的密碼 -->

          ??????? <property name="password" value="${jdbc.password}"/>

          ??? </bean>

          </beans>

          在上面的配置文件中,配置driverClass和jdbcUrl等信息時,并未直接設置這些屬性的屬性值,而是設置了${jdbc.driverClassName}和${jdbc.url}屬性值。這表明Spring容器將從propertyConfigurer指定屬性文件中搜索這些key對應的value,并為該Bean的屬性值設置這些value值。

          如前所述,ApplicationContext會自動檢測部署在容器的容器后處理器,無須額外的注冊,容器自動注冊。因此,只需提供如下Java Properties文件:

          jdbc.driverClassName=com.mysql.jdbc.Driver

          jdbc.url=jdbc:mysql://localhost:3306/j2ee

          jdbc.username=root

          jdbc.password=32147

          通過這種方法,可從主XML配置文件中分離出部分配置信息。如果僅需要修改數據庫連接屬性,則無須修改主XML配置文件,只需要修改屬性文件即可。采用屬性占位符的配置方式,可以支持使用多個屬性文件。通過這種方式,可將配置文件分割成多個屬性文件,從而降低修改配置的風險。

          注意:對于數據庫連接等信息集中的配置,可以將其配置在Java屬性文件中,但不要過多地將Spring配置信息抽離到Java屬性文件中,否則可能會降低Spring配置文件的可讀性。

          6.1.5 另一種屬性占位符配置器(PropertyOverrideConfigurer)

          PropertyOverrideConfigurer是Spring提供的另一個容器后處理器,這個后處理器的額作用與上面介紹的容器后處理器作用大致相同。但也存在些許差別:PropertyOverride- Configurer使用的屬性文件用于覆蓋XML配置文件中的定義。即PropertyOverride- Configurer允許XML配置文件中有默認的配置信息。

          如果PropertyOverrideConfigurer的屬性文件有對應配置信息,XML文件中的配置信息被覆蓋;否則,直接使用XML文件中的配置信息。使用PropertyOverrideConfigurer的屬性文件,應是如下的格式:

          beanName.property=value

          beanName是屬性占位符試圖覆蓋的Bean名,property是試圖覆蓋的屬性名??慈缦屡渲梦募?/p>

          <?xml version="1.0" encoding="GBK"?>

          <!-- beans是Spring配置文件的根元素,并且指定了Schema信息 -->

          <beans xmlns="http://www.springframework.org/schema/beans"

          ?????? xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

          ?????? xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

          ??? <!-- 配置一個屬性占位符Bean。ApplictionContext能自動識別
          ??? PropertyPlaceholderConfigurer Bean -->

          ??? <bean id="propertyOverrider"

          ??????? class="org.springframework.beans.factory.config.
          ??????? PropertyOverrideConfigurer">

          ??????? <property name="locations">

          ??????????? <list>

          ??? ??????????? <value>dbconn.properties</value>

          ??????????????? <!-- 如果有多個屬性文件,依次在下面列出來 -->

          ??????????? </list>

          ??????? </property>

          ??? </bean>

          ??? <!-- 定義數據源Bean,使用C3P0數據源實現 -->

          ??? <bean id="dataSource" class="com.mchange.v2.c3p0.
          ??? ComboPooledDataSource" destroy-method="close">

          ??????? <!-- 指定連接數據庫的驅動 -->

          ??????? <property name="driverClass" value="dd"/>

          ??????? <!-- 指定連接數據庫的URL -->

          ??????? <property name="jdbcUrl" value="xx"/>

          ??????? <!-- 指定連接數據庫的用戶名 -->

          ??????? <property name="user" value="dd"/>

          ??????? <!-- 指定連接數據庫的密碼 -->

          ??????? <property name="password" value="xx"/>

          ??? </bean>

          </beans>

          上面的配置文件中,指定數據源Bean的各種屬性值時,只是隨意指定了幾個屬性值,很明顯通過這幾個屬性值無法連接到數據庫服務。

          但因為Spring容器中部署了一個PropertyOverrideConfigurer的容器后處理器,而且Spring容器使用ApplicationContext作為容器,它會自動檢測容器中的容器后處理器,無須額外的注冊,容器自動注冊該后處理器。

          PropertyOverrideConfigurer后處理器讀取dbconn.properties文件中的屬性,用于覆蓋目標Bean的屬性。因此,如果屬性文件中有dataSource Bean屬性的設置,則配置文件中指定的屬性值將沒有任何作用。

          dbconn.properties屬性文件如下:

          dataSource.driverClassName=com.mysql.jdbc.Driver

          dataSource.url=jdbc:mysql://wonder:3306/j2ee

          dataSource.username=root

          dataSource.password=32147

          注意屬性文件的格式必須是:

          beanName.property=value

          也就是說,dataSource必須是容器中真實存在的bean名,否則程序將出錯。

          注意:程序無法知道BeanFactory定義是否被覆蓋。僅僅通過察看XML配置文件,無法知道配置文件的配置信息是否被覆蓋。如有多個PorpertyOverrideConfigurer對同一Bean屬性定義了覆蓋,最后一個覆蓋獲勝。

          posted on 2009-07-19 10:12 jadmin 閱讀(122) 評論(0)  編輯  收藏

          只有注冊用戶登錄后才能發表評論。


          網站導航:
           
          主站蜘蛛池模板: 大冶市| 永吉县| 明溪县| 许昌市| 农安县| 乌拉特前旗| 隆安县| 长治县| 桐庐县| 浦东新区| 云梦县| 梅州市| 定日县| 泸州市| 印江| 屏山县| 安仁县| 绩溪县| 襄樊市| 宁城县| 鹿泉市| 赤壁市| 乌兰察布市| 微山县| 和静县| 罗田县| 汾阳市| 长沙市| 阜城县| 蒙阴县| 福贡县| 沙河市| 北流市| 旬邑县| 包头市| 铜山县| 宁海县| 漯河市| 屏南县| 弋阳县| 济阳县|