• Spring AOP 创建增强类


        AOP联盟为增强定义了org.aopalliance.aop.Advice接口,Spring支持5种类型的增强:

        1)前置增强:org.springframework.aop.BeforeAdvice 代表前置增强,因为Spring 只支持方法级的增强,所有MethodBeforeAdvice是目前可用的前置增强,表示在目标方法执行前实施增强,而BeforeAdvice是为了将来版本扩展需要而定义的;
        2)后置增强:org.springframework.aop.AfterReturningAdvice 代表后增强,表示在目标方法执行后实施增强;
        3)环绕增强:org.aopalliance.intercept.MethodInterceptor 代表环绕增强,表示在目标方法执行前后实施增强;
        4)异常抛出增强:org.springframework.aop.ThrowsAdvice 代表抛出异常增强,表示在目标方法抛出异常后实施增强;
        5)引介增强:org.springframework.aop.IntroductionInterceptor 代表引介增强,表示在目标类中添加一些新的方法和属性。
     
    1、前置增强
        模拟服务员向顾客表示欢迎和对顾客提供服务。
    Waiter接口:
    package com.yyq.advice;
    public interface Waiter {
        void greetTo(String name);
        void serveTo(String name);
    }

    NaiveWaiter服务类:

    package com.yyq.advice;
    public class NaiveWaiter implements Waiter {
        @Override
        public void greetTo(String name) {
            System.out.println("greet to " + name + "...");
        }
        @Override
        public void serveTo(String name) {
            System.out.println("serving to " + name + "...");
        }
    }

    前置增强实现类:

    package com.yyq.advice;
    import org.springframework.aop.MethodBeforeAdvice;
    import java.lang.reflect.Method;
    public class GreetingBeforeAdvice implements MethodBeforeAdvice {
        @Override
        public void before(Method method, Object[] objects, Object o) throws Throwable {
            String clientName = (String) objects[0];
            System.out.println("How are you ! Mr." + clientName + ".");
        }
    }

    测试方法:

    @Test
        public void testBeforeAdvice(){
            Waiter target = new NaiveWaiter();
            BeforeAdvice advice = new GreetingBeforeAdvice();
            ProxyFactory pf = new ProxyFactory(); //Spring提供的代理工厂
            pf.setTarget(target);    //设置代理目标
            pf.addAdvice(advice);
            Waiter proxy = (Waiter)pf.getProxy();    //生成代理实例
            proxy.greetTo("John");
            proxy.serveTo("Tom");
        }
    输出结果:
    How are you ! Mr.John.
    greet to John...
    How are you ! Mr.Tom.
    serving to Tom...
     
    在Spring中配置:beans.xml
    <bean id="greetingAdvice" class="com.yyq.advice.GreetingBeforeAdvice"/>
    <bean id="target" class="com.yyq.advice.NaiveWaiter"/>
    
    <bean id="waiter1" class="org.springframework.aop.framework.ProxyFactoryBean"
         p:proxyInterfaces="com.yyq.advice.Waiter"
         p:interceptorNames="greetingAdvice"
         p:target-ref="target"/>

      ProxyFactoryBean 是FactoryBean接口的实现类。

        · target:代理的目标对象;
        · proxyInterfaces:代理所实现的接口,可以是多个接口。该属性还有一个别名属性interfaces;
        · interceptorNames:需要织入目标对象的Bean列表,采用Bean的名称指定。这些Bean必须是实现了org.aopalliance.intercept.Method 或 org.springframework.aop.Advisor的Bean,配置中的顺序对应调用的顺序;
        · singleton:返回的代理是否是单实例,默认为单实例;
        · optimize:当设置为true时,强制使用CGLib代理。对于singleton的代理,我们推荐使用CGLib,对于其他作用域类型的代理,最好使用JDK代理。原因是CGLib创建代理时速度慢,而创建出的代理对象运行效率较高,而使用JDK代理的表现正好相反;
        · proxyTargetClass:是否对类进行代理(而不是对接口进行代理),设置为true时,使用CGLib代理。
    测试方法:
    @Test
        public void testBeforeAdvice2(){
            String configPath = "com\yyq\advice\beans.xml";
            ApplicationContext ctx = new ClassPathXmlApplicationContext(configPath);
            Waiter waiter = (Waiter)ctx.getBean("waiter1");
            waiter.greetTo("Joe");
        }
    输出结果:
    How are you ! Mr.Joe.
    greet to Joe...
     
    2、后置增强
        后置增强在目标类方法调用后执行。模拟服务员在每次服务后使用礼貌用语。
    GreetingAfterAdvice后置增强实现类:
    package com.yyq.advice;
    import org.springframework.aop.AfterReturningAdvice;
    import java.lang.reflect.Method;
    public class GreetingAfterAdvice implements AfterReturningAdvice {
        @Override
        public void afterReturning(Object o, Method method, Object[] objects, Object o2) throws Throwable {
            System.out.println("Please enjoy yourself.");
        }
    }

     在beans.xml文件添加后置增强:

    <bean id="greetingBefore" class="com.yyq.advice.GreetingBeforeAdvice"/>
    <bean id="greetingAfter" class="com.yyq.advice.GreetingAfterAdvice"/>
    <bean id="waiter2" class="org.springframework.aop.framework.ProxyFactoryBean"
         p:proxyInterfaces="com.yyq.advice.Waiter"
         p:interceptorNames="greetingBefore,greetingAfter"
         p:target-ref="target"/>

    测试方法:

     @Test
        public void testBeforeAndAfterAdvice(){
            String configPath = "com\yyq\advice\beans.xml";
            ApplicationContext ctx = new ClassPathXmlApplicationContext(configPath);
            Waiter waiter = (Waiter)ctx.getBean("waiter2");
            waiter.greetTo("Joe");
        }
    结果输出:
    How are you ! Mr.Joe.
    greet to Joe...
    Please enjoy yourself.
     
    3、环绕增强
        环绕增强允许在目标类方法调用前后织入横切逻辑,它综合实现了前置、后置增强两者的功能。
    GreetingInterceptor环绕增强实现类:
    package com.yyq.advice;
    import org.aopalliance.intercept.MethodInterceptor;
    import org.aopalliance.intercept.MethodInvocation;
    public class GreetingInterceptor implements MethodInterceptor {
        @Override
        public Object invoke(MethodInvocation methodInvocation) throws Throwable {
            Object[] args = methodInvocation.getArguments();
            String clientName = (String) args[0];
            System.out.println("Hi,Mr " + clientName + ".");
            Object obj = methodInvocation.proceed();
            System.out.println("Please enjoy yourself~");
            return obj;
        }
    }

       Spring 直接使用AOP联盟所定义的MethodInterceptor作为环绕增强的接口。该接口拥有唯一的接口方法 Object invoke(MethodInvocation invocation), MethodInvocation不但封装目标方法及其入参数组,还封装了目标方法所在的实例对象,通过MethodInvocation的getArguments()可以获取目标方法的入参数组,通过proceed()反射调用目标实例相应的方法。

    在beans.xml文件添加环绕增强:
     <bean id="greetingAround" class="com.yyq.advice.GreetingInterceptor"/>
        <bean id="waiter3" class="org.springframework.aop.framework.ProxyFactoryBean"
              p:proxyInterfaces="com.yyq.advice.Waiter"
              p:interceptorNames="greetingAround"
              p:target-ref="target"/>

    测试方法:

      @Test
        public void testAroundAdvice(){
            String configPath = "com\yyq\advice\beans.xml";
            ApplicationContext ctx = new ClassPathXmlApplicationContext(configPath);
            Waiter waiter = (Waiter)ctx.getBean("waiter3");
            waiter.greetTo("Joe");
        }
    结果输出:
    Hi,Mr Joe.
    greet to Joe...
    Please enjoy yourself~
     
    4、异常抛出增强
        异常抛出增强最适合的应用场景是事务管理,当参与事务的某个Dao发送异常时,事务管理器就必须回滚事务。
    Forum业务类:
    package com.yyq.advice;
    public class Forum {
        private int forumId;
        public int getForumId() {
            return forumId;
        }
        public void setForumId(int forumId) {
            this.forumId = forumId;
        }
    }

     ForumService业务类:

    package com.yyq.advice;
    import java.sql.SQLException;
    public class ForumService {
        public void removeForum(int forumId){
            System.out.println("removeForum....");
            throw new RuntimeException("运行异常");
        }
        public void updateForum(Forum forum)throws Exception{
            System.out.println("updateForum");
            throw new SQLException("数据更新操作异常。");
        }
    }

    TransactionManager异常抛出增强实现类:

    package com.yyq.advice;
    import org.springframework.aop.ThrowsAdvice;
    import java.lang.reflect.Method;
    public class TransactionManager implements ThrowsAdvice {
        public void afterThrowing(Method method, Object[] args, Object target, Exception ex) throws Throwable {
            System.out.println("----------------");
            System.out.println("method:" + method.getName());
            System.out.println("抛出异常:" + ex.getMessage());
            System.out.println("成功回滚事务。");
        }
    }
        ThrowAdvice异常抛出增强接口没有定义任何方法,它是一个标识接口,在运行期Spring使用反射的机制自行判断,我们采用以下签名形式定义异常抛出的增强方法:void afterThrowing(Mehod method, Object[] args, Object target, Throwable);方法名必须为afterThrowing,方法入参规定,前三个参数是可选的,要么三个入参提供,要么不提供,最后一个入参是Throwable或者子类。
    在beans.xml文件添加异常抛出增强:
    <bean id="transactionManager" class="com.yyq.advice.TransactionManager"/>
        <bean id="forumServiceTarget" class="com.yyq.advice.ForumService"/>
        <bean id="forumService" class="org.springframework.aop.framework.ProxyFactoryBean"
              p:interceptorNames="transactionManager"
              p:target-ref="forumServiceTarget"
              p:proxyTargetClass="true"/>

     测试方法:

    @Test
        public void testThrowsAdvice(){
            String configPath = "com\yyq\advice\beans.xml";
            ApplicationContext ctx = new ClassPathXmlApplicationContext(configPath);
            ForumService fs = (ForumService)ctx.getBean("forumService");
            try{
                fs.removeForum(10);
            } catch (Exception e) {}
            try{
                fs.updateForum(new Forum());
            } catch (Exception e) {}
        }
    结果输出:
    ----------------
    method:removeForum
    抛出异常:运行异常
    成功回滚事务。
    updateForum
    ----------------
    method:updateForum
    抛出异常:数据更新操作异常。
    成功回滚事务。
     
    5、引介增强
            引介增强为目标类创建新的方法和属性,所以引介增强的连接点是类级别的,而非方法级别的。通过引介增强,我们可以为目标类添加一个接口的实现,即原来目标类未实现某个接口,通过引介增强可以为目标类创建实现某个接口的代理。Spring定义了引介增强接口IntroductionInterceptor,该接口没有定义任何的方法,Spring为该接口提供了DelegatingIntroductionInterceptor实现类。
    Monitorable:用于标识目标类是否支持性能监视的接口
    package com.yyq.advice;
    public interface Monitorable {
        void setMonitorActive(boolean active);
    }

     ControllablePerformanceMonitor 为引介增强实现类:

    package com.yyq.advice;
    import org.aopalliance.intercept.MethodInvocation;
    import org.springframework.aop.support.DelegatingIntroductionInterceptor;
    public class ControllablePerformanceMonitor extends DelegatingIntroductionInterceptor implements Monitorable {
        private ThreadLocal<Boolean> MonitorStatusMap = new ThreadLocal<Boolean>();
        @Override
        public void setMonitorActive(boolean active) {
            MonitorStatusMap.set(active);
        }
        public Object invoke(MethodInvocation mi) throws Throwable {
            Object obj = null;
            if (MonitorStatusMap.get() != null && MonitorStatusMap.get()) {
                PerformanceMonitor.begin(mi.getClass().getName() + "." + mi.getMethod().getName());
                obj = super.invoke(mi);
                PerformanceMonitor.end();
            } else {
                obj = super.invoke(mi);
            }
            return obj;
        }
    }

    PerformanceMonitor监视类:

    package com.yyq.advice;
    
    public class PerformanceMonitor {
        private static ThreadLocal<MethodPerformance> performanceRecord = new ThreadLocal<MethodPerformance>();
        public static void begin(String method) {
            System.out.println("begin monitor...");
            MethodPerformance mp = new MethodPerformance(method);
            performanceRecord.set(mp);
        }
        public static void end(){
            System.out.println("end monitor...");
            MethodPerformance mp = performanceRecord.get();
            mp.printPerformance();
        }
    }

    MethodPerformance记录性能信息:

    public class MethodPerformance {
        private long begin;
        private long end;
        private String serviceMethod;
        public MethodPerformance(String serviceMethod){
            this.serviceMethod = serviceMethod;
            this.begin = System.currentTimeMillis();
        }
        public void printPerformance(){
            end = System.currentTimeMillis();
            long elapse = end - begin;
            System.out.println(serviceMethod + "花费" + elapse + "毫秒。");
        }
    }

    在beans.xml文件添加引介增强:

    <bean id="pmonitor" class="com.yyq.advice.ControllablePerformanceMonitor"/>
    <bean id="forumServiceImplTarget" class="com.yyq.advice.ForumServiceImpl"/>
    <bean id="forumService2" class="org.springframework.aop.framework.ProxyFactoryBean"
              p:interfaces="com.yyq.advice.Monitorable"
              p:interceptorNames="pmonitor"
              p:target-ref="forumServiceImplTarget"
              p:proxyTargetClass="true"/>
    测试方法:
    @Test
        public void testIntroduce(){
            String configPath = "com\yyq\advice\beans.xml";
            ApplicationContext ctx = new ClassPathXmlApplicationContext(configPath);
            ForumServiceImpl forumServiceImpl = (ForumServiceImpl)ctx.getBean("forumService2");
            forumServiceImpl.removeForum(23);
            forumServiceImpl.removeTopic(1023);
            Monitorable monitorable = (Monitorable)forumServiceImpl;
            monitorable.setMonitorActive(true);
            forumServiceImpl.removeForum(22);
            forumServiceImpl.removeTopic(1023);
        }
    结果输出:
    模拟删除Forum记录:23
    模拟删除Topic记录:1023
    begin monitor...
    模拟删除Forum记录:22
    end monitor...
    org.springframework.aop.framework.Cglib2AopProxy$CglibMethodInvocation.removeForum花费40毫秒。
    begin monitor...
    模拟删除Topic记录:1023
    end monitor...
    org.springframework.aop.framework.Cglib2AopProxy$CglibMethodInvocation.removeTopic花费21毫秒。
     
     
  • 相关阅读:
    dsp与sem的互补以及技术实现
    SmartAssembly 6 + ClickOnce 混淆发布,妈妈再也不用担心程序裸奔了
    IL学习,使用Emit动态创建一个委托
    DataReader扩展方法
    已安装 SQL Server 2005 Express 工具。若要继续,请删除 SQL Server 2005 Express 工具
    仿163网盘无刷新多文件上传系统
    asdf
    闽江学院2015-2016学年下学期《软件测试》课程-第一次作业(个人作业)
    闽江学院2015-2016学年下学期《软件测试》课程-第三次博客作业
    闽江学院2015-2016学年下学期《软件测试》课程-第二次作业(个人作业)
  • 原文地址:https://www.cnblogs.com/yangyquin/p/5463697.html
Copyright © 2020-2023  润新知