常言笑的家

          Spring, Hibernate, Struts, Ajax, RoR

          Spring Aop Step-By-Step 學(xué)習(xí)筆記(下)

          五. Spring-Aop 引入的介紹
           
              下面我們介紹一種通知“引入”,關(guān)于引入,如同它的名字一樣,給對象添加方法和屬性。呵呵,好厲害吧。它是通過CBLIB來動態(tài)生成類的,所以自己用的時(shí)候別忘了加載這個(gè)包。
              
               代碼:
               購物時(shí)候放東西的包包;
          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"處處可見,因?yàn)槲覀円總€(gè)新的對象都有自己引入的狀態(tài),不可能只有一個(gè)對象來維持,那個(gè)我們肯定是不希望的。
          修改我們的RunDemo類,添加一個(gè)方法:
               /**
                * 創(chuàng)建引用通知;
                */
               public static void customer() {
           
                    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("demo/customer.xml");
           
                    // 這個(gè)類是有CGLIB動態(tài)生成的;
                    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中調(diào)用這個(gè)方法,運(yùn)行,結(jié)果如下:
                        
                         悠~游!
          1
           
                    在這里我要說明一下,關(guān)于引入這個(gè)通知的使用我僅僅是看了一眼,具體的例子可能有不恰當(dāng)之處還請高手們指點(diǎn)。
           
          六. Spring-Aop 之 BeanNameAutoProxyCreator
           
          BeanNameAutoProxyCreator 看其名,大概知道其意。根據(jù)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(" 開始測試時(shí)間.");
                    long t = System.currentTimeMillis();
                    Object o = invocation.proceed();
                    t = System.currentTimeMillis() - t;
                    if (t > thresholdInMillis) {
                         System.out.println(" 警告:調(diào)用的方法所耗費(fèi)的時(shí)間超過預(yù)警時(shí)間(" + thresholdInMillis + " 微秒)");
                    }
                    System.out.println(" 測試時(shí)間結(jié)束.");
                    return o;
               }
          }
          這個(gè)又是自定義的,呵呵。我們繼承MethodInterceptor這換類,實(shí)現(xiàn)我們自己的方法過濾器。具體要實(shí)現(xiàn)的功能就是檢驗(yàn)我們要調(diào)用的方法,和OnePerCustomerInterceptor這個(gè)類一樣。
          接下來是我們的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方法中加入我們的方法:
             /**
              * 創(chuàng)建beanNameAutoProxyCreator動態(tài)代理;
              */
             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();
                  }
             }
           
               運(yùn)行我們的例子,結(jié)果如下:
              
          開始測試時(shí)間.
          -- 我想買:奶酪!
          測試時(shí)間結(jié)束.
              
               看到了么?Spring aop自動尋找Bean的名字為* Target 的類,進(jìn)行方法過濾。呵呵,可能你會說這個(gè)有什么用?自己寫不也一樣么?其實(shí)如果系統(tǒng)變得龐大的話,自己配置也是十分耗費(fèi)精力的。
           
          七. Spring-Aop DefaultAdvisorAutoProxyCreator
           
               接下來我們將介紹更加強(qiáng)大的一個(gè)代理器:DefaultAdvisorAutoProxyCreator。
               DefaultAdvisorAutoProxyCreator BeanNameAutoProxyCreator不同的是,DefaultAdvisorAutoProxyCreator只和Advisor 匹配,所以我們寫一個(gè)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來匹配切入點(diǎn)完成個(gè)一個(gè)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>
              
               添加下面方法調(diào)用main方法中去:
          /**
          * 創(chuàng)建defaultAdvisorAutoProxyCreator動態(tài)代理;
          */
             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();
                  }
             }
               運(yùn)行,結(jié)果如下:
                   
          開始測試時(shí)間.
          -- 我想買:奶酪!
          測試時(shí)間結(jié)束.
           
           
          八. Spring-Aop TargetSources 介紹
           
          1. 可熱交換的目標(biāo)源
           
          可熱交換的目標(biāo)源主要是在你程序運(yùn)行中切換目標(biāo)對象,而此時(shí)調(diào)用者引用的對象也會自動切換。具體的概念你可以參考Spring-Reference關(guān)于它的介紹,我們主要在程序中體會它給我們帶來的改變。
          修改我們的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");
           
                // 如果你想通過類來引用這個(gè)的話,就要用到CGLIB.jar了,同時(shí)在代理工廠里面設(shè)置:
                //<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) {
                     // 異常已經(jīng)被截獲了,不信你看控制臺!~;
                }
               
                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) {
                     // 異常已經(jīng)被截獲了,不信你看控制臺!~;
                }
           }
           
          運(yùn)行,結(jié)果如下:
           
          店員:悠~游! ,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!
           
          可以看到,我們切換后的對象已經(jīng)被置換了。注意 singleton="false" ,通常情況下需要設(shè)置為false,以保證Spring在必要的時(shí)候可以創(chuàng)建一個(gè)新的目標(biāo)實(shí)例。
           
          2. 支持池的目標(biāo)源
          使用支持目標(biāo)池的源提供了一種和無狀態(tài) session Ejb 類似的編程方式,在無狀態(tài)的 Session Ejb 中,維護(hù)了一個(gè)相同實(shí)例的池,提供從池中獲取可用對象的方法。
          這次我們用到了 Commons pool 這個(gè)包,同時(shí)在運(yùn)行時(shí)候還需要 commons-collections.jar, 需要把它們加載到環(huán)境變量中。
          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>
           
          同時(shí),我們還需要 添加下面代碼的引用到我們的main中。
          代碼:
           
                 /**
                  * 調(diào)用池對象;
                  */
                 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());
                 }
           
          運(yùn)行,結(jié)果如下:
           
          -- 我想買:奶酪!
          池的大小為:10
           
          同時(shí)在我們的控制臺里可以看到這句話:
          信息: Creating Commons object pool
          你可以得到對象,也可以銷毀對象。但是必須你的目標(biāo)對象實(shí)現(xiàn)了 DisposableBean 接口,重寫銷毀的方法。然后通過下面方法調(diào)用:
           
          CommonsPoolTargetSource comPool = (CommonsPoolTargetSource) context.getBean("commonsPool");
           
          try {
                 comPool.destroyObject(akem);
          } catch (Exception e) {
                 e.printStackTrace();
                        }
          銷毀池則用:
          comPool.destroy()
                       
           
          九. Spring-Aop 相關(guān)及其他
           
          其他相關(guān)的比較重要的是org.springframework.aop.framework.ProxyFactoryBean 類,幾乎我們上篇都用到了這個(gè)類,簡單介紹一下:
           

          ProxyFactoryBean

          屬性
          描述
          target
          代理的目標(biāo)對象
          proxyInterfaces
          代理實(shí)現(xiàn)的接口
          interceptorNames
          在應(yīng)用到的目標(biāo)對象上添加的Advice的名字,可以是攔截器、advisor或者其他通知類型的名字(順序很重要哦)
           
          其他還有singleton,proxyTargetClass。
          singleton :每次調(diào)用getBean()的時(shí)候返回一個(gè)新的實(shí)例,例如我們使用引入的時(shí)候,有狀態(tài)的bean要設(shè)置為false哦。
               proxyTargetClass :是否代理目標(biāo)類,而不是實(shí)現(xiàn)接口。只能在使用CBLIB的時(shí)使用。
              
          proxyTargetClass 重點(diǎn)說一下,什么意思呢?白話說的意思就是:如果你不設(shè)置proxyInterfaces這個(gè),就必須設(shè)置這個(gè)方法,并且方法值為True。就是告訴CBLIB你要?jiǎng)討B(tài)創(chuàng)建一個(gè)代理類來引用我們的目標(biāo)。
                   
          Spring-Reference 中,提到了事務(wù)代理,我想那個(gè)相對于持久化處理時(shí)候在了解比較合適。 對于元數(shù)據(jù)的支持,因?yàn)槲疫€沒有精力讀到哪里。所以這個(gè)暫且擱下,有興趣的讀者可以自己查閱參考。
           
          非常感謝你能讀完正篇文章,將不足的地方通過郵件傳遞給我,我將盡快改正。最后我希望,能夠讓每一個(gè)讀過的人都有所獲得,讓我們享受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 常言笑 閱讀(733) 評論(0)  編輯  收藏 所屬分類: JAVA/J2EE

          My Links

          Blog Stats

          常用鏈接

          留言簿(5)

          隨筆分類

          隨筆檔案

          搜索

          積分與排名

          最新評論

          閱讀排行榜

          評論排行榜

          主站蜘蛛池模板: 太仓市| 陈巴尔虎旗| 台南市| 连城县| 平遥县| 溆浦县| 津南区| 政和县| 柳江县| 崇左市| 乌什县| 项城市| 通城县| 平塘县| 三穗县| 浦北县| 株洲县| 应城市| 柘荣县| 镶黄旗| 紫云| 桃园市| 冷水江市| 漯河市| 彰化市| 商丘市| 晋城| 崇信县| 衡山县| 临洮县| 襄樊市| 凤翔县| 宜黄县| 错那县| 怀仁县| 宝清县| 博罗县| 勐海县| 嘉义市| 霍州市| 奎屯市|