AOP——JDK动态代理源码解析

本篇将从源码层面分析,JDK代理的具体实现方式。
摘录源码版本:JDK 1.8

概述

我们知道,在Spring AOP 中,创建代理有两种方式,jdk动态代理与cglib动态代理。本篇先讲一下JDK动态代理的低层原理。

JDK动态代理是使用了Java的反射机制,利用Proxy生成实现了目标接口的代理类。

大纲:

  • JDK 动态代理实现步骤
  • JDK 动态代理使用方法
  • InvocationHander
  • Proxy.newProxyInstance
  • Proxy 生成代理类的源码解析



JDK Proxy 实现原理

关键接口java.lang.reflect.InvocationHandler
关键类java.lang.reflect.Proxy

JDK动态代理实现步骤

JDK实现动态代理简单来说有五个步骤:
1,创建自定义InvocationHander,通过继承 InvocationHandler接口,来实现自定义的 invoke操作。(AOP逻辑就在 invoke() 方法中)
2,通过 Proxy.newProxyInstance 方法,生成并获取代理类的实例对象。
3,在 Proxy.newProxyInstance 方法中,通过反射,获取到代理类的构造方法,方法签名为 getConstructor(InvocationHandler.class)
4,通过构造函数生成Proxy类的对象,并且生成时,将自定义的InvocationHandler实例对象作为参数传入。
5,通过代理对象调用目标方法。


JDK动态代理的使用

通过以下代码,可以看到是如何利用 InvocationHanderProxy 生成代理对象并且使用代理的。

示例代码如下:

// 自定义 InvocationHandler  (也可以看做是代理)
public class JDKProxy implements InvocationHandler {
    // 要代理的目标实例
    private Object proxyObject;

    public JDKProxy(Object proxyObject) {
        this.proxyObject = proxyObject;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // do something
        Object ret = null;  //方法返回值
        System.out.println("JDK Proxy --- 调用前 --- 调用方法是:"+method.getName() + "---参数是:"+ GsonUtil.toJson(args));
        ret = method.invoke(proxyObject, args); //对目标实例调用invoke方法
        System.out.println("JDK Proxy --- 调用后");
        return ret;
    }
}

// 生成代理类的工厂方法
public static Object createJDKProxyInstance(Object proxyObject){
    JDKProxy jdkProxy = new JDKProxy(proxyObject);
    return Proxy.newProxyInstance(proxyObject.getClass().getClassLoader(), proxyObject.getClass().getInterfaces(), jdkProxy);
}

// 具体使用的代码
DemoManager demoProxy = (DemoManager) ProxyFactory.createJDKProxyInstance(new DemoManagerCustom());


InvocationHandler

先来看看 InvocationHandler 这个接口
注释是这么写的

InvocationHandler is the interface implemented by the invocation handler of a proxy instance.
Each proxy instance has an associated invocation handler. When a method is invoked on a proxy instance, the method invocation is encoded and dispatched to the invoke method of its invocation handler.

InvocationHandler 是一个接口,每一个需要 自定义 方法调用处理器的 代理实例都要实现这个接口。
每一个代理实例都关联着一个 invocation handler。当代理实例的方法被调用的时候,这个方法就会编码和转发到 invocation handler的 invoke 方法调用。

这个接口只有一个方法:invoke()

public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable;

这个方法接受三个参数:

  • proxy:指我们生成的代理对象实例
  • method:当前调用的method对象
  • args:调用method的方法参数列表。


Proxy

Proxy类,就是用来创建动态代理对象的类。类注释如下:

Proxy provides static methods for creating dynamic proxy classes and instances, and it is also the superclass of all dynamic proxy classes created by those methods

Proxy 提供了创建动态代理类和实例的静态方法,同时这些动态代理类的超类也会在这些方法里被创建。


Proxy类提供了许多方法,动态代理用的方法是 newProxyInstance,使用方式在示例代码中已经给出。

我们可以看看源码中方法的定义:

/**
  * Returns an instance of a proxy class for the specified interfaces
  * that dispatches method invocations to the specified invocation
  * handler.
  * @param  loader the class loader to define the proxy class
  * @param  interfaces the list of interfaces for the proxy class to implement
  * @param  h the invocation handler to dispatch method invocations to
**/
public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException

该方法接收三个参数

  • loader:一个ClassLoader对象,定义了由哪个ClassLoader来对生成的代理类进行加载。

  • interfaces:代理类的继承接口数组,表示的是我需要给代理的类提供的接口,如果我提供了接口给这个代理类,那么这个代理对象就声称实现了这些接口,那么我们就可以调用这些接口的方法。
    JDK动态代理只能对实现了接口的目标对象代理的根本原因

  • h:方法调用转发的目的 invocation hander 实例。表示这个代理对象会关联到哪个 invocation handler 上。


JDK动态代理原理

通过上面 InvocationHandler 和 Proxy 的介绍。那么动态代理的基本原理就可以捋清了:

1)通过继承 InvocationHander,来自定义一个 invocation hander。这个hander 保存了一个代理目标对象的引用。

2)在实现 invoke() 方法中,我们把接收到的方法调用请求,用proxyObject来执行。即 method.invoke(proxyObject, args),得到代理对象的方法调用结果并返回。

3)在 invoke 方法中嵌入 AOP 相关的处理逻辑。这样就实现了目标方法调用时,横切逻辑的执行。

4)代理对象的生成,使用 Proxy.newProxyInstance() 方法生成,传入的三个参数依次为,目标对象的类加载器目标对象实现的接口列表invocation handler 实例

5)这样生成的代理对象,就是继承了目标对象接口列表的代理类的实例对象。
使用向上转型的方式,把对象声明为接口的实例。并且和目标对象有一样的方法。

6)利用多态的机制,对接口对象的方法调用,会动态链接到代理对象的方法调用,然后又会转发到我们自定义 invocation hander 中,对 invoke 函数的调用,实现横切逻辑的调用。



源码解析

通过Debug模式查看代理对像的类型可以发现
Proxy.newProxyInstance() 生成的代理类名称为 com.sun.proxy.$Proxy0。这个类是JVM在运行时动态生成的,以$开头,Proxy为中间,最后的序号表示类对象的序号。
(生成代理类的逻辑可以参考 Proxy.ProxyClassFactory,下面放出了源码)

其中有一个公共的构造器:
public com.sun.proxy.$Proxy0(java.lang.reflect.InvocationHandler)
那么可以知道,我们的代理实例对象,就是用这个构造器生成的。
如图:

代理类对象



Proxy.newProxyInstance


    /** parameter types of a proxy class constructor */
    private static final Class<?>[] constructorParams =
        { InvocationHandler.class };

    @CallerSensitive
    public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {
        Objects.requireNonNull(h);  //h 不能为空,所以 InvocationHandler 是必须的

        final Class<?>[] intfs = interfaces.clone();  //对象拷贝
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {  //校验是否有权限创建一个代理类
            checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
        }

        /*
         * Look up or generate the designated proxy class.
         * (寻找 或者 生成 指定的代理类)
         */
        Class<?> cl = getProxyClass0(loader, intfs);

        /*
         * Invoke its constructor with the designated invocation handler.
         * 调用这个代理类的构造器,传入指定的 invocation handler 对象
         */
        try {
            if (sm != null) {  //权限校验,不细讲
                checkNewProxyPermission(Reflection.getCallerClass(), cl);
            }

            // 获取参数是 InvocationHander.class 类型的构造器
            final Constructor<?> cons = cl.getConstructor(constructorParams);
            final InvocationHandler ih = h;
            // 如果这个构造器不是公有的,则把修饰符改为公有。
            if (!Modifier.isPublic(cl.getModifiers())) {  
                AccessController.doPrivileged(new PrivilegedAction<Void>() {
                    public Void run() {
                        cons.setAccessible(true);
                        return null;
                    }
                });
            }
            // 调用构造器,生成代理类的对象并且返回
            return cons.newInstance(new Object[]{h});
        } catch (IllegalAccessException|InstantiationException e) {
            throw new InternalError(e.toString(), e);
        } catch (InvocationTargetException e) {
            Throwable t = e.getCause();
            if (t instanceof RuntimeException) {
                throw (RuntimeException) t;
            } else {
                throw new InternalError(t.toString(), t);
            }
        } catch (NoSuchMethodException e) {
            throw new InternalError(e.toString(), e);
        }
    }



getProxyClass0
在上面方法中可以看到,生成动态代理类的方法,是调用了
getProxyClass0 方法。

而其中关键在于 proxyClassCache.get(loader, interfaces);
根据该方法的注释可以明白,这里有一个代理类的缓存,如果这个代理类已经 有指定类加载器并且继承了这些接口,那么将直接返回生成的代理类,否则将由ProxyClassFactory生成代理类。

那么看看这个方法的源码:

    /**
     * a cache of proxy classes(一个已经生成了代理类的缓存)
     */
    private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
        proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());

    private static Class<?> getProxyClass0(ClassLoader loader,
                                           Class<?>... interfaces) {
        if (interfaces.length > 65535) {
            throw new IllegalArgumentException("interface limit exceeded");
        }

        // If the proxy class defined by the given loader implementing
        // the given interfaces exists, this will simply return the cached copy;
        // otherwise, it will create the proxy class via the ProxyClassFactory
        return proxyClassCache.get(loader, interfaces);
    }



ProxyClassFactory
创建类的核心还是在 ProxyClassFactory 类中。
可以看到 ProxyClassFactory 实现了函数式接口 BiFunction。(JDK8)
入参是 类加载器(ClassLoader) 和 接口数组(Class<?>[])

    /**
     * A factory function that generates, defines and returns the proxy class given
     * the ClassLoader and array of interfaces.
     */
    private static final class ProxyClassFactory
        implements BiFunction<ClassLoader, Class<?>[], Class<?>>
    {
        // prefix for all proxy class names 
        //(所有生成的代理类前缀都是 $Proxy)
        private static final String proxyClassNamePrefix = "$Proxy";

        // next number to use for generation of unique proxy class names
        // (代理类的生成序号)
        private static final AtomicLong nextUniqueNumber = new AtomicLong();

        @Override
        public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {

            Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);

            // 遍历接口数组,校验:
            // 1)接口是否能找到,加载到的实例是否和传入的接口实例一样
            // 2)校验加载的实例是否是一个接口
            // 3)接口去重
            for (Class<?> intf : interfaces) {
                /*
                 * Verify that the class loader resolves the name of this
                 * interface to the same Class object.
                 */
                Class<?> interfaceClass = null;
                try {
                    interfaceClass = Class.forName(intf.getName(), false, loader);
                } catch (ClassNotFoundException e) {
                }
                if (interfaceClass != intf) {
                    throw new IllegalArgumentException(
                        intf + " is not visible from class loader");
                }
                /*
                 * Verify that the Class object actually represents an
                 * interface.
                 */
                if (!interfaceClass.isInterface()) {
                    throw new IllegalArgumentException(
                        interfaceClass.getName() + " is not an interface");
                }
                /*
                 * Verify that this interface is not a duplicate.
                 */
                if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
                    throw new IllegalArgumentException(
                        "repeated interface: " + interfaceClass.getName());
                }
            }

            // 声明代理类所在的包路径
            String proxyPkg = null;     // package to define proxy class in
            // 代理类的访问修饰符 
            int accessFlags = Modifier.PUBLIC | Modifier.FINAL;

            /*
             * 记录非公有的接口包路径,使得生成的代理类和这个接口包路径相同
             * Record the package of a non-public proxy interface so that the
             * proxy class will be defined in the same package.  Verify that
             * all non-public proxy interfaces are in the same package.
             */
            for (Class<?> intf : interfaces) {
                int flags = intf.getModifiers();
                if (!Modifier.isPublic(flags)) {
                    accessFlags = Modifier.FINAL;
                    String name = intf.getName();
                    int n = name.lastIndexOf('.');
                    String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
                    if (proxyPkg == null) {
                        proxyPkg = pkg;
                    } else if (!pkg.equals(proxyPkg)) {
                        throw new IllegalArgumentException(
                            "non-public interfaces from different packages");
                    }
                }
            }

            if (proxyPkg == null) {
                // if no non-public proxy interfaces, use com.sun.proxy package
                proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
            }

            /*
             * Choose a name for the proxy class to generate.
             * 生成代理类的名字
             */
            long num = nextUniqueNumber.getAndIncrement();
            String proxyName = proxyPkg + proxyClassNamePrefix + num;

            /*
             * Generate the specified proxy class.
             * 生成类的字节码,然后通过本地方法 defineClass0 生成代理类
             */
            byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                proxyName, interfaces, accessFlags);
            try {
                return defineClass0(loader, proxyName,
                                    proxyClassFile, 0, proxyClassFile.length);
            } catch (ClassFormatError e) {
                /*
                 * A ClassFormatError here means that (barring bugs in the
                 * proxy class generation code) there was some other
                 * invalid aspect of the arguments supplied to the proxy
                 * class creation (such as virtual machine limitations
                 * exceeded).
                 */
                throw new IllegalArgumentException(e.toString());
            }
        }
    }



(如果有什么错误或者建议,欢迎留言指出)
(本文内容是对各个知识点的转载整理,用于个人技术沉淀,以及大家学习交流用)


参考资料:

深度剖析JDK动态代理机制(JDK Proxy源码分析)
java动态代理机制详解

静态代理和动态代理的理解
Spring源码剖析——AOP实现原理 (Spring 使用JDK Proxy )
SpringAOP两种方式——JDKDynamicAopProxy和cglib2AopProxy源码解析 (Spring 使用JDK Proxy )

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,968评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,601评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,220评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,416评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,425评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,144评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,432评论 3 401
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,088评论 0 261
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,586评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,028评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,137评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,783评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,343评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,333评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,559评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,595评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,901评论 2 345

推荐阅读更多精彩内容

  • /Library/Java/JavaVirtualMachines/jdk-9.jdk/Contents/Home...
    光剑书架上的书阅读 3,856评论 2 8
  • 本文主要讲实现AOP的 代理模式原理,以及静态代理,动态代理的区别和具体实现。 对SpringAOP的概念和使用,...
    _Zy阅读 746评论 0 1
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,601评论 18 139
  • 很难说改变是件好事还是坏事,但是自从每天早上从祷告变成新闻之后,这个世界就焦虑了不少,因为让每个人都处于惴惴不安的...
    WonderLAB阅读 1,151评论 0 10
  • 荒野行动中玩家处处面临各种抉择,究竟是在城区降落还是郊外?究竟是隔岸观火还是冲锋陷阵?可能在其他游戏中玩家会因为“...
    陈小晨儿阅读 420评论 0 0