zoukankan      html  css  js  c++  java
  • Spring Aop StepByStep 学习笔记(下)

    下面我们介绍一种通知“引入”,关于引入,如同它的名字一样,给对象添加方法和属性。呵呵,好厉害吧。它是通过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中,维护了一个相同实例的池,提供从池中获取可用对象的方法。
    这次我们用到了Commonspool 这个包,同时在运行时候还需要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中,提到了事务代理,我想那个相对于持久化处理时候在了解比较合适。对于元数据的支持,因为我还没有精力读到哪里。所以这个暂且搁下,有兴趣的读者可以自己查阅参考。
     
  • 相关阅读:
    前辈的经验
    ES6 的新特性
    jQuery 事件对象的属性和方法
    ECMAScript 6 入门
    浏览器的加载与页面性能优化
    ajax 基于 jquery 简单的 ajax 请求
    浏览器加载、解析、渲染的过程
    浏览器的加载和解析的过程,以及如何加快 HTML 的加载速度
    【经典问题】当你输入一个网址的时候,实际会发生什么?
    总结 IE 下的一些 BUG
  • 原文地址:https://www.cnblogs.com/sunwei2012/p/1642135.html
Copyright © 2011-2022 走看看