zoukankan      html  css  js  c++  java
  • AOP的底层实现-CGLIB动态代理和JDK动态代理

          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实现的代理要求被代理类基于统一的接口
    *
    * @author typ
    *
    */
    public interface Service {
    /**
    * add方法
    */
    public void add();

    /**
    * update方法
    */
    public void update();
    }
    2、目标类AService,我们的实验目标就是在AService中add和update方法的前后实现拦截,加入自定义切面逻辑advise
    package jdkproxy;

    /**
    * 被代理类,即目标类target
    *
    * @author typ
    *
    */
    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;

    /**
    * @author typ
    *
    */
    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;

    /**
    * @author typ
    *
    */
    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
    *
    * @author typ
    *
    */
    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
    *
    * @author typ
    *
    */
    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;

    /**
    * 工厂类,生成增强过的目标类(已加入切入逻辑)
    *
    * @author typ
    *
    */
    public class Factory {
    /**
    * 获得增强之后的目标类,即添加了切入逻辑advice之后的目标类
    *
    * @param proxy
    * @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;

    /**
    * @author typ
    *
    */
    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的核心实现机制讲解。

    原文链接:https://blog.csdn.net/dreamrealised/java/article/details/12885739

  • 相关阅读:
    安装lnmp 时如何修改数据库数据存储地址及默认访问地址
    ubuntu 设置root用户密码并实现root用户登录
    解决ubuntu 远程连接问题
    linux 搭建FTP服务器
    PHP 根据ip获取对应的实际地址
    如何发布自己的composer包
    使用composer安装composer包报Your requirements could not be resolved to an installable set of packages
    laravel 框架配置404等异常页面
    使用Xshell登录linux服务器报WARNING! The remote SSH server rejected X11 forwarding request
    IoTSharp 已支持国产松果时序数据库PinusDB
  • 原文地址:https://www.cnblogs.com/daxiong225/p/12581221.html
Copyright © 2011-2022 走看看