AOP实现原理(一)JDK动态代理

背景

在前几篇文章中,我们介绍了AOP的使用方法,辣么AOP为何会如此神奇呢?通过查阅资料,我们了解到AOP的实现是通过代理去实现。
在分析问题之前我们应该有如下几点疑问:
1. 什么是代理。
2. AOP实现代理方式有几种,这几种方式各有什么优点。
3. 如何去实现AOP的代理方式。

介绍

  1. 动态代理 : 在程序运行期间由Java反射等机制动态生成,也就是在将class加载到jvm时期完成的工作。这种方式是不会修改字节码。
  2. 静态代理: 在程序运行前已经存在代理类的字节码的文件,也就是在编译时期已经完成代理工作。

Spring实现AOP的方式是通过动态代理。也就是在目标类的基础上增加切面逻辑,生成目标类的增强的代理类。
Spring AOP的动态代理方式有两种方式:
1. 基于JDK实现的JDK动态代理。
2. 基于ASM实现的CGLIB动态代理。

JDK动态代理

JDK动态代理通过反射来处理被代理的类。JDK动态代理的核心是InvocationHandler接口和Proxy类。
1. InvocationHandler 中仅一个方法public Object invoke(Object proxy, Method method, Object[] args)
这三个参数 分别是:代理实例、调用的方法、方法的参数列表。
2. Proxy类是真正创建代理实例的类,其中主要是使用 Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)来创建。这三个参数分别是:目标的ClassLoader对象、提供给目标类的接口对象、InvocationHandler 对象。

下面 我们举个例子来说明。:
接口:

package com.perf.Test.aop.jdk;

/** * @author cj34920 * Date: 2018/04/21. */
public interface IDayWork {
   void  breakfast();
   void  lunch();
   void  dinner();
}

实现类

package com.perf.Test.aop.jdk;

/** * @author cj34920 * Date: 2018/04/21 */
public class DayWorkImpl implements IDayWork {
    @Override
    public void breakfast() {
        System.out.println("吃早饭");
    }

    @Override
    public void lunch() {
        System.out.println("吃午饭");
    }

    @Override
    public void dinner() {
        System.out.println("吃晚饭");
    }
}

TimeHandler类

package com.perf.Test.aop.jdk;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

/** * @author cj34920 * Date: 2018/04/21 */
public class TimeHandler implements InvocationHandler{
    private Object target;
    public TimeHandler(Object target) {
        this.target = target;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("吃饭之前要洗手");
        //调用真正的方法
        Object retVal = method.invoke(target, args);
        System.out.println("吃饭之后要洗碗");
        return retVal;
    }
}

主函数:

 public static void main(String[] args) {
     DayWorkImpl dayWork = new DayWorkImpl();
     TimeHandler timeHandler = new TimeHandler(dayWork);
     IDayWork work = (IDayWork) Proxy.newProxyInstance(dayWork.getClass().getClassLoader(), dayWork.getClass().getInterfaces(), timeHandler);
     work.breakfast();
     work.lunch();
     work.dinner();
     System.out.println(work.getClass());
 }

运行结果:

吃饭之前要洗手
吃早饭
吃饭之后要洗碗
吃饭之前要洗手
吃午饭
吃饭之后要洗碗
吃饭之前要洗手
吃晚饭
吃饭之后要洗碗
class com.sun.proxy.$Proxy0
  1. 这边我们需要注意的是 Proxy.newProxyInstance返回的对象是代理类,但是这个代理类不是我们这边DayWorkImpl这个类,所以这边需要注意不要写成 DayWorkImpl。
  2. 那么 为什么我们能够将这个代理类转化成 IDayWork ?其原因就在 newProxyInstance的第二个参数上面,我们回想一下第二个参数是什么?是接口,是我们提供给代理对象的接口,那么我们这个代理对象就会去实现这个接口,这种情况下,我们当然可以将这个代理对象强制转化成提供的接口类型。
  3. Proxy.newProxyInstance创建的代理对象是在JVM运行时动态生成的一个对象,这个对象不是我们知道的任何一个对象,而是运行时动态生成的,并且命名方式都是以$Proxy这种类型的。看运行结果也看出来 $Proxy0就是实际代理类。

总结

JDK动态代理的特点:JDK动态代理的方法都必须有接口的实现(newProxyInstance第二个参数看出来)。

下一篇文章给大家介绍AOP动态代理的另外一种方式CGLIB动态代理。

https://blog.csdn.net/bicheng4769/article/details/80030266

    原文作者:AOP
    原文地址: https://blog.csdn.net/bicheng4769/article/details/80028158
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞