zoukankan      html  css  js  c++  java
  • Java 动态代理及AOP实现机制

    AOP实现机制
    http://www.iteye.com/topic/1116696

    AOP: (Aspect Oriented Programming) 面向切面编程
    AOP包括切面(aspect)、通知(advice)、连接点(joinpoint),实现方式就是通过对目标对象的代理在连接点前后加入通知,完成统一的切面操作

    利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。主要的功能是:日志记录,性能统计,安全控制,事务处理,异常处理等等。

    面向接口编程,(横向编程)


    AOP的底层实现-CGLIB动态代理和JDK动态代理

    http://blog.csdn.net/dreamrealised/article/details/12885739

    AOP是目前Spring框架中的核心之一,在应用中具有非常重要的作用,也是Spring其他组件的基础。它是一种面向切面编程的思想。关于AOP的基础知识,相信多数童鞋都已经了如指掌,我们就略过这部分,来讲解下AOP的核心功能的底层实现机制:如何用动态代理来实现切面拦截。
    AOP的拦截功能是由java中的动态代理来实现的。说白了,就是在目标类的基础上增加切面逻辑,生成增强的目标类(该切面逻辑或者在目标类函数执行之前,或者目标类函数执行之后,或者在目标类函数抛出异常时候执行。不同的切入时机对应不同的Interceptor的种类,如BeforeAdviseInterceptor,AfterAdviseInterceptor以及ThrowsAdviseInterceptor等)。
    那么动态代理是如何实现将切面逻辑(advise)织入到目标类方法中去的呢?下面我们就来详细介绍并实现AOP中用到的两种动态代理。
    AOP的源码中用到了两种动态代理来实现拦截切入功能:jdk动态代理和cglib动态代理。两种方法同时存在,各有优劣。

    jdk动态代理是由java内部的反射机制来实现的,

    cglib动态代理底层则是借助asm来实现的。

    总的来说,反射机制在生成类的过程中比较高效,而asm在生成类之后的相关执行过程中比较高效(可以通过将asm生成的类进行缓存,这样解决asm生成类过程低效问题)。还有一点必须注意:jdk动态代理的应用前提,必须是目标类基于统一的接口。如果没有上述前提,jdk动态代理不能应用。由此可以看出,jdk动态代理有一定的局限性,cglib这种第三方类库实现的动态代理应用更加广泛,且在效率上更有优势。
    一、jdk动态代理实现AOP拦截(代码中的关键地方都添加了注释)

    1、为目标类(target)定义统一的接口类Service,这个是jdk动态代理必须的前提。

    package jdkproxy; 
    
    /** 
    * 该类是所有被代理类的接口类,jdk实现的代理要求被代理类基于统一的接口 
    * 
    */ 
    public interface Service { 
        /** 
        * add方法 
        */ 
        public void add(); 
    
        /** 
        * update方法 
        */ 
        public void update(); 
    } 


    2、目标类AService,我们的实验目标就是在AService中add和update方法的前后实现拦截,加入自定义切面逻辑advise

    package jdkproxy; 
    
    /** 
    * 被代理类,即目标类target 
    * 
    * 
    */ 
    public class AService implements Service { 
        /* 
        * (non-Javadoc) 
        * 
        * @see jdkproxy.Service#add() 
        */ 
        public void add() { 
            System.out.println("AService add>>>>>>>>>>>>>>>>>>"); 
        } 
    
        /* 
        * (non-Javadoc) 
        * 
        * @see jdkproxy.Service#update() 
        */ 
        public void update() { 
            System.out.println("AService update>>>>>>>>>>>>>>>"); 
        } 
    } 


    3、实现动态代理类MyInvocationHandler,实现InvocationHandler接口,并且实现接口中的invoke方法。仔细看invoke方法,就是在该方法中加入切面逻辑的。目标类方法的执行是由mehod.invoke(target,args)这条语句完成。

    package jdkproxy; 
    
    import java.lang.reflect.InvocationHandler; 
    import java.lang.reflect.Method; 
    
    public class MyInvocationHandler implements InvocationHandler { 
        private Object target; 
    
        MyInvocationHandler() { 
            super(); 
        } 
    
        MyInvocationHandler(Object target) { 
            super(); 
            this.target = target; 
        } 
    
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 
            // 程序执行前加入逻辑,MethodBeforeAdviceInterceptor 
            System.out.println("before-----------------------------"); 
            // 程序执行 
            Object result = method.invoke(target, args); 
            // 程序执行后加入逻辑,MethodAfterAdviceInterceptor 
            System.out.println("after------------------------------"); 
            return result; 
        } 
    
    } 


    4、测试类,其中增强的目标对象是由Proxy.newProxyInstance(aService.getClass().getClassLoader(), aService.getClass().getInterfaces(), handler);来生成的。

    package jdkproxy; 
    
    import java.lang.reflect.Proxy; 
    
    public class Test { 
        public static void main(String[] args) { 
            Service aService = new AService(); 
            MyInvocationHandler handler = new MyInvocationHandler(aService); 
            // Proxy为InvocationHandler实现类动态创建一个符合某一接口的代理实例 
            Service aServiceProxy = (Service) Proxy.newProxyInstance(aService 
            .getClass().getClassLoader(), aService.getClass() 
            .getInterfaces(), handler); 
            // 由动态生成的代理对象来aServiceProxy 代理执行程序,其中aServiceProxy 符合Service接口 
            aServiceProxy.add(); 
            System.out.println(); 
            aServiceProxy.update(); 
            // 以下是对B的代理 
            // Service bService = new BService(); 
            // MyInvocationHandler handler = new MyInvocationHandler(bService); 
            // Service bServiceProxy = (Service) Proxy.newProxyInstance(bService 
            // .getClass().getClassLoader(), bService.getClass() 
            // .getInterfaces(), handler); 
            // bServiceProxy.add(); 
            // System.out.println(); 
            // bServiceProxy.update(); 
        } 
    } 


    自此,jdk动态代理来实现AOP拦截机制的代码已经实现,下面我们看一下拦截的结果,程序输出结果如下:
    before-----------------------------
    AService add>>>>>>>>>>>>>>>>>>
    after------------------------------
    before-----------------------------
    AService update>>>>>>>>>>>>>>>
    after------------------------------
    可以看到,在目标类AService的add和update方法前后已经加入了自定义的切面逻辑,AOP拦截机制生效了。

    二、cglib动态代理实现AOP拦截(代码中的关键地方都添加了注释)

    1、目标类,cglib不需要定义目标类的统一接口

    package cglibproxy; 
    
    /** 
    * 被代理类,即目标对象target 
    * 
    */ 
    public class Base { 
    /** 
    * 一个模拟的add方法 
    */ 
        public void add() { 
            System.out.println("add ------------"); 
        } 
    } 


    2、实现动态代理类CglibProxy,需要实现MethodInterceptor接口,实现intercept方法。

    该代理中在add方法前后加入了自定义的切面逻辑,目标类add方法执行语句为proxy.invokeSuper(object, args);

    package cglibproxy; 
    
    import java.lang.reflect.Method; 
    import net.sf.cglib.proxy.MethodInterceptor; 
    import net.sf.cglib.proxy.MethodProxy; 
    
    /** 
    * 此为代理类,用于在pointcut处添加advise 
    * 
    * 
    */ 
    public class CglibProxy implements MethodInterceptor { 
    
        public Object intercept(Object object, Method method, Object[] args, 
            MethodProxy proxy) throws Throwable { 
            // 添加切面逻辑(advise),此处是在目标类代码执行之前,即为MethodBeforeAdviceInterceptor。 
            System.out.println("before-------------"); 
            // 执行目标类add方法 
            proxy.invokeSuper(object, args); 
            // 添加切面逻辑(advise),此处是在目标类代码执行之后,即为MethodAfterAdviceInterceptor。 
            System.out.println("after--------------"); 
            return null; 
        } 
    
    } 

    3、获取增强的目标类的工厂Factory,其中增强的方法类对象是有Enhancer来实现的,代码如下所示:

    package cglibproxy; 
    
    import net.sf.cglib.proxy.Enhancer; 
    
    /** 
    * 工厂类,生成增强过的目标类(已加入切入逻辑) 
    * 
    */ 
    public class Factory { 
        /** 
        * 获得增强之后的目标类,即添加了切入逻辑advice之后的目标类 
        * 
        * @return 
        */ 
        public static Base getInstance(CglibProxy proxy) { 
            Enhancer enhancer = new Enhancer(); 
            enhancer.setSuperclass(Base.class); 
            //回调方法的参数为代理类对象CglibProxy,最后增强目标类调用的是代理类对象CglibProxy中的intercept方法 
            enhancer.setCallback(proxy); 
            // 此刻,base不是单纯的目标类,而是增强过的目标类 
            Base base = (Base) enhancer.create(); 
            return base; 
        } 
    } 


    4、测试类

    package cglibproxy; 
    
    public class Test { 
        public static void main(String[] args) { 
            CglibProxy proxy = new CglibProxy(); 
            // base为生成的增强过的目标类 
            Base base = Factory.getInstance(proxy); 
            base.add(); 
        } 
    } 

    自此,cglib动态代理实现的AOP拦截机制已经基本实现,下面我们来看一下拦截的效果如何,程序执行结果如下:
    before-------------
    add ------------
    after--------------
    可以看到,在目标类Base的add方法前后已经加入了自定义的切面逻辑,AOP拦截机制生效了。
    此外,需要说明一下的是,cglib动态代理用到了第三方类库,需要在项目中引入两个jar包:cglib.jar和asm.jar。稍后会在csdn资源中上传这两个jar包。免积分的啊。
    jar包和相关代码请见http://download.csdn.net/detail/dreamrealised/6427885

    总之,AOP的核心机制和基本功能已经能够通过动态代理来实现了,至于AOP中,如何从配置文档中得到目标类target、advisor的bean,如何判断拦截器类型等问题,就借助于Spring中另一个核心IOC来解决了,后续会有IOC的核心实现机制讲解。

  • 相关阅读:
    2021牛客暑期多校训练营5
    二分图知识点温习
    Codeforces Round #735 (Div. 2)
    牛客比赛订正(3,4)
    Harbour.Space Scholarship Contest 2021-2022 (Div. 1 + Div. 2) Editorial题解
    关于球的相关知识
    AtCoder Beginner Contest 210题解
    P7077 [CSP-S2020] 函数调用
    偏序问题学习笔记
    P1606 [USACO07FEB]Lilypad Pond G
  • 原文地址:https://www.cnblogs.com/liuxm2017/p/9803383.html
Copyright © 2011-2022 走看看