栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

jdk动态代理原理源码深度分析

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

jdk动态代理原理源码深度分析

jdk动态代理原理源码深度分析

简单实例

接口:

package com.example.demo0423.proxy;

public interface ProxyTest {
    void send(String msg);
}

实现类:

package com.example.demo0423.proxy;

public class ProxyTestImpl implements ProxyTest{
    @Override
    public void send(String msg) {
        System.out.println(msg);
    }
}

InvocationHandler实现类:

package com.example.demo0423.proxy;

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

public class ProxyTestInvocationHandlerImpl implements InvocationHandler {
    private ProxyTest proxyTest;

    public void setProxyTest(ProxyTest proxyTest) {
        this.proxyTest = proxyTest;
    }

    @Override
    public Object invoke(Object obj, Method method, Object[] args) throws Throwable {
        System.out.println("前置会话");
        Object object = method.invoke(proxyTest,args);
        System.out.println("后置会话");
        return object;
    }
}

MainTest运行测试类,查看动态代理效果:

package com.example.demo0423.proxy;

import java.lang.reflect.Proxy;

public class MainTest {
    public static void main(String[] args) {
        System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");
        ProxyTestInvocationHandlerImpl invocationHandler=new ProxyTestInvocationHandlerImpl();
        ProxyTest proxyTestInstance=new ProxyTestImpl();
        invocationHandler.setProxyTest(proxyTestInstance);
        ProxyTest   proxyTest=  (ProxyTest) Proxy.newProxyInstance(proxyTestInstance.getClass().getClassLoader(),
                proxyTestInstance.getClass().getInterfaces(),invocationHandler);
        proxyTest.send("hello");
    }
}

运行结果:

"C:Program FilesJavajdk1.8.0_271binjava.exe" -agentlib:jdwp=transport=dt_socket,address=127.0.0.1:52267,suspend=y,server=n -javaagent:D:idea2019ideaIU-2019.2.4.winpluginsjavalibrtdebugger-agent.jar -Dfile.encoding=UTF-8 -classpath "C:Program FilesJavajdk1.8.0_271jrelibcharsets.jar;C:Program FilesJavajdk1.8.0_271jrelibdeploy.jar;C:Program FilesJavajdk1.8.0_271jrelibextaccess-bridge-64.jar;C:Program FilesJavajdk1.8.0_271jrelibextcldrdata.jar;C:Program FilesJavajdk1.8.0_271jrelibextdnsns.jar;C:Program FilesJavajdk1.8.0_271jrelibextjaccess.jar;C:Program FilesJavajdk1.8.0_271jrelibextjfxrt.jar;C:Program FilesJavajdk1.8.0_271jrelibextlocaledata.jar;C:Program FilesJavajdk1.8.0_271jrelibextnashorn.jar;C:Program FilesJavajdk1.8.0_271jrelibextsunec.jar;C:Program FilesJavajdk1.8.0_271jrelibextsunjce_provider.jar;C:Program FilesJavajdk1.8.0_271jrelibextsunmscapi.jar;C:Program FilesJavajdk1.8.0_271jrelibextsunpkcs11.jar;C:Program FilesJavajdk1.8.0_271jrelibextzipfs.jar;C:Program FilesJavajdk1.8.0_271jrelibjavaws.jar;C:Program FilesJavajdk1.8.0_271jrelibjce.jar;C:Program FilesJavajdk1.8.0_271jrelibjfr.jar;C:Program FilesJavajdk1.8.0_271jrelibjfxswt.jar;C:Program FilesJavajdk1.8.0_271jrelibjsse.jar;C:Program FilesJavajdk1.8.0_271jrelibmanagement-agent.jar;C:Program FilesJavajdk1.8.0_271jrelibplugin.jar;C:Program FilesJavajdk1.8.0_271jrelibresources.jar;C:Program FilesJavajdk1.8.0_271jrelibrt.jar;D:workspace-ideal-20220423demo01targetclasses;D:maven-Repositoryorgspringframeworkbootspring-boot-starter2.6.7spring-boot-starter-2.6.7.jar;D:maven-Repositoryorgspringframeworkbootspring-boot2.6.7spring-boot-2.6.7.jar;D:maven-Repositoryorgspringframeworkspring-context5.3.19spring-context-5.3.19.jar;D:maven-Repositoryorgspringframeworkbootspring-boot-autoconfigure2.6.7spring-boot-autoconfigure-2.6.7.jar;D:maven-Repositoryorgspringframeworkbootspring-boot-starter-logging2.6.7spring-boot-starter-logging-2.6.7.jar;D:maven-Repositorychqoslogbacklogback-classic1.2.11logback-classic-1.2.11.jar;D:maven-Repositorychqoslogbacklogback-core1.2.11logback-core-1.2.11.jar;D:maven-Repositoryorgapachelogginglog4jlog4j-to-slf4j2.17.2log4j-to-slf4j-2.17.2.jar;D:maven-Repositoryorgapachelogginglog4jlog4j-api2.17.2log4j-api-2.17.2.jar;D:maven-Repositoryorgslf4jjul-to-slf4j1.7.36jul-to-slf4j-1.7.36.jar;D:maven-Repositoryjakartaannotationjakarta.annotation-api1.3.5jakarta.annotation-api-1.3.5.jar;D:maven-Repositoryorgspringframeworkspring-core5.3.19spring-core-5.3.19.jar;D:maven-Repositoryorgspringframeworkspring-jcl5.3.19spring-jcl-5.3.19.jar;D:maven-Repositoryorgyamlsnakeyaml1.29snakeyaml-1.29.jar;D:maven-Repositoryorgspringframeworkbootspring-boot-starter-web2.6.7spring-boot-starter-web-2.6.7.jar;D:maven-Repositoryorgspringframeworkbootspring-boot-starter-json2.6.7spring-boot-starter-json-2.6.7.jar;D:maven-Repositorycomfasterxmljacksoncorejackson-databind2.13.2.1jackson-databind-2.13.2.1.jar;D:maven-Repositorycomfasterxmljacksoncorejackson-annotations2.13.2jackson-annotations-2.13.2.jar;D:maven-Repositorycomfasterxmljacksoncorejackson-core2.13.2jackson-core-2.13.2.jar;D:maven-Repositorycomfasterxmljacksondatatypejackson-datatype-jdk82.13.2jackson-datatype-jdk8-2.13.2.jar;D:maven-Repositorycomfasterxmljacksondatatypejackson-datatype-jsr3102.13.2jackson-datatype-jsr310-2.13.2.jar;D:maven-Repositorycomfasterxmljacksonmodulejackson-module-parameter-names2.13.2jackson-module-parameter-names-2.13.2.jar;D:maven-Repositoryorgspringframeworkbootspring-boot-starter-tomcat2.6.7spring-boot-starter-tomcat-2.6.7.jar;D:maven-Repositoryorgapachetomcatembedtomcat-embed-core9.0.62tomcat-embed-core-9.0.62.jar;D:maven-Repositoryorgapachetomcatembedtomcat-embed-el9.0.62tomcat-embed-el-9.0.62.jar;D:maven-Repositoryorgapachetomcatembedtomcat-embed-websocket9.0.62tomcat-embed-websocket-9.0.62.jar;D:maven-Repositoryorgspringframeworkspring-web5.3.19spring-web-5.3.19.jar;D:maven-Repositoryorgspringframeworkspring-beans5.3.19spring-beans-5.3.19.jar;D:maven-Repositoryorgspringframeworkspring-webmvc5.3.19spring-webmvc-5.3.19.jar;D:maven-Repositoryorgspringframeworkspring-aop5.3.19spring-aop-5.3.19.jar;D:maven-Repositoryorgspringframeworkspring-expression5.3.19spring-expression-5.3.19.jar;D:maven-Repositoryorgspringframeworkbootspring-boot-devtools2.6.7spring-boot-devtools-2.6.7.jar;D:maven-Repositoryorgprojectlomboklombok1.18.24lombok-1.18.24.jar;D:maven-Repositoryorgslf4jslf4j-api1.7.36slf4j-api-1.7.36.jar;D:idea2019ideaIU-2019.2.4.winlibidea_rt.jar" com.example.demo0423.proxy.MainTest
Connected to the target VM, address: '127.0.0.1:52267', transport: 'socket'
前置会话
hello
后置会话
Disconnected from the target VM, address: '127.0.0.1:52267', transport: 'socket'

Process finished with exit code 0
运行原理分析

如果看客阁下对上述demo例子还觉得运行结果有点懵逼的话,或者觉得这个demo神奇的话,那就跟当年鄙人初学动态代理时应该是一个水准的,那这篇文章下面的这段就挺适合阁下静下心来看看

动态代理精华分析

从上述demo中,可以看到,demo用到了接口ProxyTest,接口实现ProxyTestImpl,还有个InvocationHandler实现类ProxyTestInvocationHandlerImpl。然后调用Proxy的newProxyInstance方法,获取到了一个代理对象,调用这个代理对象的send方法,然后看运行效果是即调用了invocationHanderImpl的invoke方法,也调用了ProxyTestImpl的send方法。如果初学动态代理,或者初学java者,在这个地方难免会懵,不急,我们循序向下观看分析。

类Proxy-创建动态代理对象

在测试类MainTest,调用Proxy类的接口生成动态代理类:

Proxy.newProxyInstance(proxyTestInstance.getClass().getClassLoader(),
                proxyTestInstance.getClass().getInterfaces(),invocationHandler);

那么,我们不妨进入这个newProxyInstance方法去看看它到底做了什么,Proxy类是jdk封装的类,在此我只把这个方法黏贴进来,因为这个类太长了:

    @CallerSensitive
    public static Object newProxyInstance(ClassLoader loader,
                                          Class[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {
        Objects.requireNonNull(h);

        final Class[] intfs = interfaces.clone();
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
        }

        
        Class cl = getProxyClass0(loader, intfs);

        
        try {
            if (sm != null) {
                checkNewProxyPermission(Reflection.getCallerClass(), cl);
            }

            final Constructor cons = cl.getConstructor(constructorParams);
            final InvocationHandler ih = h;
            if (!Modifier.isPublic(cl.getModifiers())) {
                AccessController.doPrivileged(new PrivilegedAction() {
                    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);
        }
    }

从代码中我们可以看到第三个参数InvocationHandler h,最终通过下面一行代码传了出去:

cons.newInstance(new Object[]{h});

那cons这个变量是什么?看newProxyInstance方法中其中有几行:

        Class cl = getProxyClass0(loader, intfs);
final Constructor cons = cl.getConstructor(constructorParams);
cons.newInstance(new Object[]{h});

通过上面几行代码能看出来,getProxyClass0是获取代理类的class对象的,这获取代理类class对象过程中如果没有字节码文件就生成代理类$Proxy0.class字节码文件了(getProxyClass0这个方法如何生成字节码文件,这个小伙伴们可以自行进入这个方法阅读里面的代码,太长了,不想贴出来,大家仔细看应该都能看懂这里面的东西,提示一下,最终生成字节码文件调用的还是proxy的内部类ProxyClassFactory的apply方法生成的),然后调用代理类的getConstructor方法获取构造器类的class对象,最后用构造器的newInstance方法反射创建一个代理对象。

查看动态代理类源代码

关于如何查看动态代理类,请仔细看MainTest类里main方法第一行,有一行代码:

System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");

代码运行的时候加入这行,就会在项目的根目录下生成一个包comsunproxy,动态代理类就会生成在这个目录下(注意这个目录和java的classpath不是一回事,这个是在项目的根目录下,和pom.xml和src目录平级的)。

动态代理类反编译后源代码

至此,有点思路了,Proxy代理类的作用是生成代理类,并把invocationhandler实例传给代理类,那么代理类长什么样?下面我把我这个demo生成的动态代理类反编译出来的源代码贴出来:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package com.sun.proxy;

import com.example.demo0423.proxy.ProxyTest;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

public final class $Proxy0 extends Proxy implements ProxyTest {
    private static Method m1;
    private static Method m2;
    private static Method m3;
    private static Method m0;

    public $Proxy0(InvocationHandler var1) throws  {
        super(var1);
    }

    public final boolean equals(Object var1) throws  {
        try {
            return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final String toString() throws  {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final void send(String var1) throws  {
        try {
            super.h.invoke(this, m3, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final int hashCode() throws  {
        try {
            return (Integer)super.h.invoke(this, m0, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m3 = Class.forName("com.example.demo0423.proxy.ProxyTest").getMethod("send", Class.forName("java.lang.String"));
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

通过代码可以看到,这个动态代理类, extends Proxy implements ProxyTest ,所以mainTest里拿到动态代理类后,调用send方法,其实是调用的是动态代理类的send方法:

 ProxyTestInvocationHandlerImpl invocationHandler=new ProxyTestInvocationHandlerImpl();
        ProxyTest proxyTestInstance=new ProxyTestImpl();
        invocationHandler.setProxyTest(proxyTestInstance);
        ProxyTest   proxyTest=  (ProxyTest) Proxy.newProxyInstance(proxyTestInstance.getClass().getClassLoader(),
                proxyTestInstance.getClass().getInterfaces(),invocationHandler);

        proxyTest.send("hello");

而动态代理类的send方法我们看:

    public final void send(String var1) throws  {
        try {
            super.h.invoke(this, m3, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

就一行,super.h.invoke(this, m3, new Object[]{var1}),这个地方这个super.h
是什么呐?
点进去看,super.h是父类中的:

    protected InvocationHandler h;

这个怎么赋上值的呐?

动态代理类对象运行时调用invoke方法源码分析

刚才我们看了,在动态代理类对象生成的时候调用的是cons.newInstance(new Object[]{h}),没看明白的话回过去再看看,
而生成动态代理对象的是时候的这个h是Proxy.newProxyInstance调用时候传过来的,也就是说这个h就是main方法里new的那个invocationHandler,如下所示的

ProxyTestInvocationHandlerImpl invocationHandler=new ProxyTestInvocationHandlerImpl();

这个handler最终被传到动态代理类的构造器中反射生成动态代理对象,然后我们看动态代理对象的构造函数:

    public $Proxy0(InvocationHandler var1) throws  {
        super(var1);
    }

这个构造器又调用了父类的构造函数,它的父类是谁呐?Proxy,所以我们接着看Proxy的构造器:

    protected Proxy(InvocationHandler h) {
        Objects.requireNonNull(h);
        this.h = h;
    }

看明白了吧,最终invocationHandler被传到Proxy的h上了。
那么当调用动态代理类的send方法时,super.h其实就是下面这个对象:

ProxyTestInvocationHandlerImpl invocationHandler=new ProxyTestInvocationHandlerImpl();

所以super.h.invoke就是调用的ProxyTestInvocationHandlerImpl 里的invoke方法:

    @Override
    public Object invoke(Object obj, Method method, Object[] args) throws Throwable {
        System.out.println("前置会话");
        Object object = method.invoke(proxyTest,args);
        System.out.println("后置会话");
        return object;
    }

而这个方法里的参数obj是动态代理对象,Method是要调用的send方法对象,args是调用时候传输的参数对象数组,这些参数都是从动态代理类里获取到传输给invocationHandler的。
平时我们设计动态代理都会把要代理的类作为类属性传输进来让invoker方法去反射调用,就像本例中把ProxyTestImpl的实例传进来用于调用被代理方法,但是如果我们有被代理方法调用需求的话,其实可以不设计接口的实现类,直接让动态代理去生成实现类,然后根据接口名字去做一些操作,像mybatis底层框架就是这么做的。
jdk的动态代理还是设计的很精妙的,值得一探究竟。欢迎评论,不喜勿喷。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/851089.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号