常言笑的家

          Spring, Hibernate, Struts, Ajax, RoR

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

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

          ProxyFactoryBean

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

          posted on 2006-12-19 13:17 常言笑 閱讀(736) 評(píng)論(0)  編輯  收藏 所屬分類(lèi): JAVA/J2EE

          My Links

          Blog Stats

          常用鏈接

          留言簿(5)

          隨筆分類(lèi)

          隨筆檔案

          搜索

          積分與排名

          最新評(píng)論

          閱讀排行榜

          評(píng)論排行榜

          主站蜘蛛池模板: 丰都县| 确山县| 固阳县| 原平市| 商都县| 金山区| 陆丰市| 车险| 虹口区| 罗平县| 惠水县| 和静县| 罗城| 阿图什市| 乐清市| 云阳县| 江西省| 临澧县| 屏南县| 英超| 读书| 眉山市| 东乌珠穆沁旗| 平利县| 长汀县| 景洪市| 定陶县| 玛沁县| 南雄市| 景宁| 咸丰县| 阿合奇县| 永嘉县| 罗江县| 阿瓦提县| 合肥市| 平武县| 蒙自县| 遵义市| 岑溪市| 巫溪县|