天天看点

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方法中加入我们的方法:       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方法中去:    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 入門      其他: 全文源码及word教程下载:点击下载  

继续阅读