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中,提到了事务代理,我想那个相对于持久化处理时候在了解比较合适。对于元数据的支持,因为我还没有精力读到哪里。所以这个暂且搁下,有兴趣的读者可以自己查阅参考。
     
  • 相关阅读:
    14.18 InnoDB Backup and Recovery 备份和恢复:
    14.18 InnoDB Backup and Recovery 备份和恢复:
    php使用 _before_index() 来实现访问页面前,判断登录
    php使用 _before_index() 来实现访问页面前,判断登录
    查询方式实例演示
    查询方式实例演示
    haproxy timeout server 46000 后台超时时间
    haproxy timeout server 46000 后台超时时间
    14.10.5 Reclaiming Disk Space with TRUNCATE TABLE 回收空间使用TRUNCATE TABLE
    14.10.5 Reclaiming Disk Space with TRUNCATE TABLE 回收空间使用TRUNCATE TABLE
  • 原文地址:https://www.cnblogs.com/sunwei2012/p/1642135.html
Copyright © 2011-2022 走看看