常言笑的家

          Spring, Hibernate, Struts, Ajax, RoR

          Spring Aop Step-By-Step 學習筆記(下)

          五. Spring-Aop 引入的介紹
           
              下面我們介紹一種通知“引入”,關于引入,如同它的名字一樣,給對象添加方法和屬性。呵呵,好厲害吧。它是通過CBLIB來動態生成類的,所以自己用的時候別忘了加載這個包。
              
               代碼:
               購物時候放東西的包包;
          public interface CustomerBag {
               void addBag(Object obj);
           
               void clean();
           
               int getCount();
          }
           
          我們要給別的對象類添加的Mixin,嘿嘿。
          public class CustomerMixin extends DelegatingIntroductionInterceptor implements CustomerBag {
           
               private static final long serialVersionUID = 5296015143432822715L;
              
               private ArrayList bag = new ArrayList();
           
               public void addBag(Object obj) {
                    bag.add(obj);
               }
           
               public void clean() {
                    bag = new ArrayList();
               }
           
               public ArrayList getBag() {
                    return bag;
               }
           
               public int getCount() {
                    return bag.size();
               }
           
          }
           
               我們的xml文件:
          <?xml version="1.0" encoding="UTF-8"?>
          <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
           
          <beans>
              
               <bean id="customer" class="demo.Customer" singleton="false" />
           
               <bean id="customerMixin" class="demo.CustomerMixin" singleton="false" />
           
           
               <bean id="customerMixinAdvisor" class="org.springframework.aop.support.DefaultIntroductionAdvisor"
                    singleton="false">
                    <constructor-arg>
                         <ref bean="customerMixin" />
                    </constructor-arg>
               </bean>
           
               <bean id="customerBean" class="org.springframework.aop.framework.ProxyFactoryBean">
                    <property name="proxyTargetClass" value="true" />
                    <property name="singleton" value="false" />
                    <property name="interceptorNames">
                         <list>
                              <value>customerMixinAdvisor</value>
                         </list>
                    </property>
                    <property name="target">
                         <ref bean="customer" />
                    </property>
               </bean>
           
          </beans>
           
          可以看到singleton="false"處處可見,因為我們要每個新的對象都有自己引入的狀態,不可能只有一個對象來維持,那個我們肯定是不希望的。
          修改我們的RunDemo類,添加一個方法:
               /**
                * 創建引用通知;
                */
               public static void customer() {
           
                    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("demo/customer.xml");
           
                    // 這個類是有CGLIB動態生成的;
                    Customer bag = (Customer) context.getBean("customerBean");
                    CustomerBag bag2 = (CustomerBag) bag;
                    bag2.addBag(new Object());
           
                    System.out.println(bag.getName());
                    System.out.println(bag2.getCount());
               }
           
                    在main中調用這個方法,運行,結果如下:
                        
                         悠~游!
          1
           
                    在這里我要說明一下,關于引入這個通知的使用我僅僅是看了一眼,具體的例子可能有不恰當之處還請高手們指點。
           
          六. Spring-Aop 之 BeanNameAutoProxyCreator
           
          BeanNameAutoProxyCreator 看其名,大概知道其意。根據bean的名字自動匹配攔截代理,讓我們看看它能帶來什么?
          代碼:
          public class PerformanceThresholdInterceptor implements MethodInterceptor {
           
               private final long thresholdInMillis;
           
               PerformanceThresholdInterceptor(long time) {
                    thresholdInMillis = time;
           
               }
           
               public Object invoke(MethodInvocation invocation) throws Throwable {
                   
                    System.out.println(" 開始測試時間.");
                    long t = System.currentTimeMillis();
                    Object o = invocation.proceed();
                    t = System.currentTimeMillis() - t;
                    if (t > thresholdInMillis) {
                         System.out.println(" 警告:調用的方法所耗費的時間超過預警時間(" + thresholdInMillis + " 微秒)");
                    }
                    System.out.println(" 測試時間結束.");
                    return o;
               }
          }
          這個又是自定義的,呵呵。我們繼承MethodInterceptor這換類,實現我們自己的方法過濾器。具體要實現的功能就是檢驗我們要調用的方法,和OnePerCustomerInterceptor這個類一樣。
          接下來是我們的xml文件:
          <?xml version="1.0" encoding="UTF-8"?>
          <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
           
          <beans>
               <bean id="kwikEMartTarget" class="demo.ApuKwikEMart"></bean>
              
               <bean id="performanceThresholdInterceptor" class="demo.advice.PerformanceThresholdInterceptor">
                    <constructor-arg>
                         <value>5000</value>
                    </constructor-arg>
               </bean>
           
               <bean id="beanNameAutoProxyCreator" class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
                    <property name="beanNames">
                         <list>
                              <value>*Target</value>
                         </list>
                    </property>
                    <property name="interceptorNames">
                         <value>performanceThresholdInterceptor</value>
                    </property>
               </bean>
           
          </beans>
           
          在main方法中加入我們的方法:
             /**
              * 創建beanNameAutoProxyCreator動態代理;
              */
             public static void beanNameProxy() {
           
                  ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("demo/beanNameProxy.xml");
           
                  KwikEMart akem = (KwikEMart) context.getBean("kwikEMartTarget");
                  try {
                       akem.buyCheese(new Customer());
                  } catch (KwikEMartException e) {
                       e.printStackTrace();
                  }
             }
           
               運行我們的例子,結果如下:
              
          開始測試時間.
          -- 我想買:奶酪!
          測試時間結束.
              
               看到了么?Spring aop自動尋找Bean的名字為* Target 的類,進行方法過濾。呵呵,可能你會說這個有什么用?自己寫不也一樣么?其實如果系統變得龐大的話,自己配置也是十分耗費精力的。
           
          七. Spring-Aop DefaultAdvisorAutoProxyCreator
           
               接下來我們將介紹更加強大的一個代理器:DefaultAdvisorAutoProxyCreator。
               DefaultAdvisorAutoProxyCreator BeanNameAutoProxyCreator不同的是,DefaultAdvisorAutoProxyCreator只和Advisor 匹配,所以我們寫一個Advisor到xml文檔中去。
               XML 文檔如下:
          <?xml version="1.0" encoding="UTF-8"?>
          <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
           
          <beans>
           
               <bean id="kwikEMartTarget" class="demo.ApuKwikEMart"></bean>
               <bean id="performanceThresholdInterceptor" class="demo.advice.PerformanceThresholdInterceptor">
                    <constructor-arg>
                         <value>5000</value>
                    </constructor-arg>
               </bean>
           
               <!-- 使用RegexpMethodPointcutAdvisor來匹配切入點完成個一個Advisor; -->
               <bean id="regexpFilterPointcutAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
                    <property name="pattern">
                         <!--    
                              匹配的名字為方法名;
                         -->
                         <value>.*buy.*</value>
                    </property>
                    <property name="advice">
                         <ref bean="performanceThresholdInterceptor"/>
                    </property>
               </bean>
           
               <bean id="defaultAdvisorAutoProxyCreator"
               class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" />
           
          </beans>
              
               添加下面方法調用main方法中去:
          /**
          * 創建defaultAdvisorAutoProxyCreator動態代理;
          */
             public static void defaultAdvisorProxy() {
           
                  ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("demo/defaultAdvisorProxy.xml");
           
                  KwikEMart akem = (KwikEMart) context.getBean("kwikEMartTarget");
                  try {
                       akem.buyCheese(new Customer());
                  } catch (KwikEMartException e) {
                       e.printStackTrace();
                  }
             }
               運行,結果如下:
                   
          開始測試時間.
          -- 我想買:奶酪!
          測試時間結束.
           
           
          八. Spring-Aop TargetSources 介紹
           
          1. 可熱交換的目標源
           
          可熱交換的目標源主要是在你程序運行中切換目標對象,而此時調用者引用的對象也會自動切換。具體的概念你可以參考Spring-Reference關于它的介紹,我們主要在程序中體會它給我們帶來的改變。
          修改我們的xml成為下面的樣子:
          <?xml version="1.0" encoding="UTF-8"?>
          <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
           
           
          <beans>
           
               <bean id="kwikEMartTarget" class="demo.ApuKwikEMart" ></bean>
              
               <bean id="swapApuKwikEMart" class="demo.SwapApuKwikEMart" singleton="false"></bean>
              
               <bean id="onePerCustomerInterceptor" class="demo.advice.OnePerCustomerInterceptor" />
           
               <bean id="nameMatchfilterPointcut" class="org.springframework.aop.support.NameMatchMethodPointcut">
                    <property name="mappedName">
                         <value>buy*</value>
                    </property>
               </bean>
           
               <bean id="runDemofilterPointcutAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">
                    <property name="pointcut">
                         <ref bean="nameMatchfilterPointcut" />
                    </property>
                    <property name="advice">
                         <ref bean="onePerCustomerInterceptor" />
                    </property>
               </bean>
           
               <bean id="swappable" class="org.springframework.aop.target.HotSwappableTargetSource">
                    <constructor-arg><ref local="kwikEMartTarget"/></constructor-arg>
               </bean>
           
               <bean id="kwikEMart" class="org.springframework.aop.framework.ProxyFactoryBean">
                    <property name="proxyInterfaces" value="demo.KwikEMart" />
                    <property name="interceptorNames">
                         <list>
                              <value>runDemofilterPointcutAdvisor</value>
                         </list>
                    </property>
                    <property name="targetSource">
                         <ref bean="swappable" />
                    </property>
               </bean>
           
          </beans>
           
          代碼:
          切換后的對象
          public class SwapApuKwikEMart implements KwikEMart {
               private boolean cheeseIsEmpty = false;
           
               private boolean pepperIsEmpty = false;
           
               private boolean squishIsEmpty = false;
           
               public Cheese buyCheese(Customer customer) throws KwikEMartException {
           
                    if (cheeseIsEmpty) {
                         throw new NoMoreCheeseException();
                    }
           
                    Cheese s = new Cheese();
                    System.out.println("-- 我不是ApuKwikEMart,我想買:" + s);
                    return s;
           
               }
           
               public Pepper buyPepper(Customer customer) throws KwikEMartException {
           
                    if (pepperIsEmpty) {
                         throw new NoMorePepperException();
                    }
           
                    Pepper s = new Pepper();
                    System.out.println("-- 我不是ApuKwikEMart,我想買:" + s);
                    return s;
           
               }
           
               public Squish buySquish(Customer customer) throws KwikEMartException {
           
                    if (squishIsEmpty) {
                         throw new NoMoreSquishException();
                    }
           
                    Squish s = new Squish();
                    System.out.println("-- 我不是ApuKwikEMart,我想買:" + s);
                    return s;
           
               }
           
               public void setCheeseIsEmpty(boolean cheeseIsEmpty) {
                    this.cheeseIsEmpty = cheeseIsEmpty;
               }
           
               public void setPepperIsEmpty(boolean pepperIsEmpty) {
                    this.pepperIsEmpty = pepperIsEmpty;
               }
           
               public void setSquishIsEmpty(boolean squishIsEmpty) {
                    this.squishIsEmpty = squishIsEmpty;
               }
           
          }
          添加下面代碼的引用到我們的main中。
           /**
            * 熱源切換;
            */
           public static void swapKwikEMart() {
           
                ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("demo/swapKwikmart.xml");
           
                // 如果你想通過類來引用這個的話,就要用到CGLIB.jar了,同時在代理工廠里面設置:
                //<property name="proxyTargetClass" value="true" />
                KwikEMart akem = (KwikEMart) context.getBean("kwikEMart");
               
                try {
                     akem.buySquish(new Customer());
                     akem.buyPepper(new Customer());
                     akem.buyCheese(new Customer());
                } catch (KwikEMartException e) {
                     // 異常已經被截獲了,不信你看控制臺!~;
                }
               
                HotSwappableTargetSource swap = (HotSwappableTargetSource) context.getBean("swappable");
                swap.swap(context.getBean("swapApuKwikEMart"));
               
                try {
                     akem.buySquish(new Customer());
                     akem.buyPepper(new Customer());
                     akem.buyCheese(new Customer());
                } catch (KwikEMartException e) {
                     // 異常已經被截獲了,不信你看控制臺!~;
                }
           }
           
          運行,結果如下:
           
          店員:悠~游! ,Can I help you ?
          -- 我想買:果醬!
          店員:OK! 悠~游!.give you!
          店員:悠~游! ,Can I help you ?
          -- 我想買:胡椒粉!
          店員:OK! 悠~游!.give you!
          店員:悠~游! ,Can I help you ?
          -- 我想買:奶酪!
          店員:OK! 悠~游!.give you!
          店員:悠~游! ,Can I help you ?
          -- 我不是ApuKwikEMart,我想買:果醬!
          店員:OK! 悠~游!.give you!
          店員:悠~游! ,Can I help you ?
          -- 我不是ApuKwikEMart,我想買:胡椒粉!
          店員:OK! 悠~游!.give you!
          店員:悠~游! ,Can I help you ?
          -- 我不是ApuKwikEMart,我想買:奶酪!
                                      店員:OK! 悠~游!.give you!
           
          可以看到,我們切換后的對象已經被置換了。注意 singleton="false" ,通常情況下需要設置為false,以保證Spring在必要的時候可以創建一個新的目標實例。
           
          2. 支持池的目標源
          使用支持目標池的源提供了一種和無狀態 session Ejb 類似的編程方式,在無狀態的 Session Ejb 中,維護了一個相同實例的池,提供從池中獲取可用對象的方法。
          這次我們用到了 Commons pool 這個包,同時在運行時候還需要 commons-collections.jar, 需要把它們加載到環境變量中。
          xml 文件如下:
           
          <?xml version="1.0" encoding="UTF-8"?>
          <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
           
           
          <beans>
           
               <bean id="kwikEMartTarget" class="demo.ApuKwikEMart" singleton="false"></bean>
           
               <bean id="commonsPool" class="org.springframework.aop.target.CommonsPoolTargetSource">
                    <property name="targetBeanName">
                         <value>kwikEMartTarget</value>
                    </property>
                    <property name="maxSize">
                         <value>10</value>
                    </property>
               </bean>
           
               <bean id="methodInvokingFactoryBeanAdvisor"
                    class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
                    <property name="targetObject">
                         <ref bean="commonsPool" />
                    </property>
                    <property name="targetMethod">
                         <value>getPoolingConfigMixin</value>
                    </property>
           
               </bean>
               <bean id="kwikEMart" class="org.springframework.aop.framework.ProxyFactoryBean">
                    <property name="proxyInterfaces" value="demo.KwikEMart" />
                    <property name="interceptorNames">
                         <list>
                              <value>methodInvokingFactoryBeanAdvisor</value>
                         </list>
                    </property>
                    <property name="targetSource">
                         <ref bean="commonsPool" />
                    </property>
               </bean>
           
          </beans>
           
          同時,我們還需要 添加下面代碼的引用到我們的main中。
          代碼:
           
                 /**
                  * 調用池對象;
                  */
                 public static void getCommonPoolObject() {
           
                        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("demo/kwikemart.xml");
           
                        KwikEMart akem = (KwikEMart) context.getBean("kwikEMart");
                        try {
                               akem.buyCheese(new Customer());
                        } catch (KwikEMartException e) {
                               e.printStackTrace();
                        }
                       
                        PoolingConfig pool=(PoolingConfig)akem;
                        System.out.println(" 池的大小為: "+pool.getMaxSize());
                 }
           
          運行,結果如下:
           
          -- 我想買:奶酪!
          池的大小為:10
           
          同時在我們的控制臺里可以看到這句話:
          信息: Creating Commons object pool
          你可以得到對象,也可以銷毀對象。但是必須你的目標對象實現了 DisposableBean 接口,重寫銷毀的方法。然后通過下面方法調用:
           
          CommonsPoolTargetSource comPool = (CommonsPoolTargetSource) context.getBean("commonsPool");
           
          try {
                 comPool.destroyObject(akem);
          } catch (Exception e) {
                 e.printStackTrace();
                        }
          銷毀池則用:
          comPool.destroy()
                       
           
          九. Spring-Aop 相關及其他
           
          其他相關的比較重要的是org.springframework.aop.framework.ProxyFactoryBean 類,幾乎我們上篇都用到了這個類,簡單介紹一下:
           

          ProxyFactoryBean

          屬性
          描述
          target
          代理的目標對象
          proxyInterfaces
          代理實現的接口
          interceptorNames
          在應用到的目標對象上添加的Advice的名字,可以是攔截器、advisor或者其他通知類型的名字(順序很重要哦)
           
          其他還有singleton,proxyTargetClass。
          singleton :每次調用getBean()的時候返回一個新的實例,例如我們使用引入的時候,有狀態的bean要設置為false哦。
               proxyTargetClass :是否代理目標類,而不是實現接口。只能在使用CBLIB的時使用。
              
          proxyTargetClass 重點說一下,什么意思呢?白話說的意思就是:如果你不設置proxyInterfaces這個,就必須設置這個方法,并且方法值為True。就是告訴CBLIB你要動態創建一個代理類來引用我們的目標。
                   
          Spring-Reference 中,提到了事務代理,我想那個相對于持久化處理時候在了解比較合適。 對于元數據的支持,因為我還沒有精力讀到哪里。所以這個暫且擱下,有興趣的讀者可以自己查閱參考。
           
          非常感謝你能讀完正篇文章,將不足的地方通過郵件傳遞給我,我將盡快改正。最后我希望,能夠讓每一個讀過的人都有所獲得,讓我們享受Spring給我們帶來的樂趣吧。
           
             
             
              參考資料;
                         Spring-Reference 中文版 第五章 Spring 之面向方面編程  
                                                                               http://www.jactiongroup.net/reference/html/
                  
          Spring AOP 中文教程                                http://spring.jactiongroup.net/viewtopic.php?t=478
              
          Spring In Action 中文版 第三章
              
          AOP 入門

          posted on 2006-12-19 13:17 常言笑 閱讀(732) 評論(0)  編輯  收藏 所屬分類: JAVA/J2EE

          My Links

          Blog Stats

          常用鏈接

          留言簿(5)

          隨筆分類

          隨筆檔案

          搜索

          積分與排名

          最新評論

          閱讀排行榜

          評論排行榜

          主站蜘蛛池模板: 海宁市| 呼玛县| 海阳市| 荣昌县| 右玉县| 贵定县| 林西县| 丹棱县| 青阳县| 县级市| 吴江市| 尤溪县| 襄樊市| 潮安县| 大足县| 嘉禾县| 昭平县| 桃源县| 惠东县| 彭泽县| 汾西县| 泽普县| 永登县| 巴南区| 临海市| 环江| 平远县| 固安县| 泾阳县| 泰州市| 柯坪县| 卫辉市| 刚察县| 永安市| 眉山市| 南江县| 盐津县| 屏东市| 宁城县| 盱眙县| 广宁县|