【Java】JDK动态代理实现原理

news2024/11/27 16:52:57

代理模式
代理模式一般包含三个角色:

  1. Subject:主题对象,一般是一个接口,定义一些业务相关的基本方法。
  2. RealSubject:具体的主题对象实现类,它会实现Subject接口中的方法。
  3. Proxy:代理对象,里面包含一个RealSubject的引用,外部会通过这个代理对象,来实现RealSubject中方法的调用。

JAVA中提供了动态代理的实现,需要依赖InvocationHandler

举个例子

Subject

首先创建一个主题对象,里面定义一个execute方法:

public interface Subject {
    void execute();
}

RealSubject

接着创建具体的主题对象实现类,它会实现Subject的方法

public class RealSubject implements Subject {
    @Override
    public void execute() {
        System.out.println("realsubject方法执行");
    }
}

创建InvocationHandler

JDK动态代理需要依赖InvocationHandler,所以这里创建一个ProxyInvocationHandler实现它的invoke方法,并提供了getProxy方法来获取创建的代理对象,ProxyInvocationHandler类中引用了需要代理的目标对象,也就是RealSubject,在invoke方法中通过反射执行了RealSubject中的方法:

public class ProxyInvocationHandler implements InvocationHandler {

    /**
     * 代理的目标对象,也就是RealSubject
     */
    private Object target;

   /**
     * 构造函数
     */
    public ProxyInvocationHandler(Object target){
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("开始执行方法:" + method.getName());
        // 通过反射执行RealSubject中的方法
        Object result = method.invoke(target, args);
        System.out.println("结束执行方法:" + method.getName());
        return null;
    }

    public Object getProxy() {
        // 创建代理对象,传入了类加载器、要代理对象的接口、InvocationHandler(this当前对象)
        return Proxy.newProxyInstance(Thread.currentThread()
                        .getContextClassLoader(), target.getClass().getInterfaces(), this);
    }
}

测试:

public class ProxyTest {
    public static void main(String[] args) {
        System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
        // 创建实际的对象
        Subject subject = new RealSubject();
        // 创建InvocationHandler,这里传入的真是对象
        ProxyInvocationHandler invocationHandler = new ProxyInvocationHandler(subject);
        // 获取代理对象
        Subject proxy = (Subject) invocationHandler.getProxy();
        // 执行方法
        proxy.execute();
    }
}

运行结果:

开始执行方法:execute
realsubject方法执行
结束执行方法:execute

根据输出结果,可以看出JDK动态代理,主要是通过InvocationHandler生成一个代理对象,通过这个代理对象可以执行目标方法,执行之时,首先会进入到InvocationHandler中的invoke方法,在创建InvocationHandler时
传入了实际的对象RealSubject,所以InvocationHandler中可以拿到真实对象,只需要在InvocationHandler中的invoke方法中通过反射执行RealSubject中对应的方法即可。

动态代理实现原理

在ProxyInvocationHandler中可以看到通过Proxy创建了一个代理对象,那么接下来就进入到Proxy中,看一下是如何创建代理对象的:

        // 创建代理对象,传入了类加载器、要代理对象的接口、InvocationHandler(this当前对象)
        return Proxy.newProxyInstance(Thread.currentThread()
                        .getContextClassLoader(), target.getClass().getInterfaces(), this);
Proxy

在Proxy中newProxyInstance方法创建代理对象的时候,传入了类加载器、需要代理的对象以及InvocationHandler:

  1. 根据类加载器和需要代理的对象接口信息生成代理对象的class;
  2. 根据生成的代理类的class信息,获取类的构造器;
  3. 通过构造器创建代理对象,并将InvocationHandler传入;
public class Proxy implements java.io.Serializable {
    // 创建代理对象
    @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);
        }
        // 1. 根据类加载器和需要代理的对象接口信息生成代理对象的class
        Class<?> cl = getProxyClass0(loader, intfs);
        try {
            if (sm != null) {
                checkNewProxyPermission(Reflection.getCallerClass(), cl);
            }
            // 2. 获取类构造器
            final Constructor<?> cons = cl.getConstructor(constructorParams);
            // InvocationHandler
            final InvocationHandler ih = h;
            if (!Modifier.isPublic(cl.getModifiers())) {
                AccessController.doPrivileged(new PrivilegedAction<Void>() {
                    public Void run() {
                        cons.setAccessible(true);
                        return null;
                    }
                });
            }
            // 3. 通过构造器创建代理对象,并将InvocationHandler传入
            return cons.newInstance(new Object[]{h});
        } catch (IllegalAccessException|InstantiationException e) {
           //...
        }
    }
}
生成代理类的class

getProxyClass0中首先会进行边界检查,然后根据类加载器和需要代理的对象接口信息从缓存中获取生成的代理类的calss,具体的实现在WeakCache的get方法中:

   /**
     * 代理类的缓存
     */
    private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
        proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());

    /**
     * 生成代理类的class
     */
    private static Class<?> getProxyClass0(ClassLoader loader,
                                           Class<?>... interfaces) {
        // 边界检查
        if (interfaces.length > 65535) {
            throw new IllegalArgumentException("interface limit exceeded");
        }
        // 从缓存proxyClassCache中获取class
        return proxyClassCache.get(loader, interfaces);
    }

   
WeakCache

WeakCache的get方法中如果根据缓存key获取对象为空,会创建一个Factory对象赋值给Supplier,Factory是WeakCache的一个内部类,它实现了Supplier接口,然后调用Supplier的get方法来生成代理类的class,接下来进入到Factory的get方法中:

final class WeakCache<K, P, V> {
    // 获取class
    public V get(K key, P parameter) {
        Objects.requireNonNull(parameter);
        expungeStaleEntries();
        // 获取缓存key
        Object cacheKey = CacheKey.valueOf(key, refQueue);
        // 根据key获取对象
        ConcurrentMap<Object, Supplier<V>> valuesMap = map.get(cacheKey);
        // 如果为空
        if (valuesMap == null) {
            // 创建一个ConcurrentMap
            ConcurrentMap<Object, Supplier<V>> oldValuesMap
                = map.putIfAbsent(cacheKey,
                                  valuesMap = new ConcurrentHashMap<>());
            if (oldValuesMap != null) {
                valuesMap = oldValuesMap;
            }
        }
        // 创建subKey
        Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
        Supplier<V> supplier = valuesMap.get(subKey);
        Factory factory = null;
        while (true) {
            if (supplier != null) {
                // 调用get方法获取class
                V value = supplier.get();
                if (value != null) {
                    return value;
                }
            }
            // 如果为空,创建Factory
            if (factory == null) {
                factory = new Factory(key, parameter, subKey, valuesMap);
            }
            // 如果supplier为null
            if (supplier == null) {
                supplier = valuesMap.putIfAbsent(subKey, factory);
                if (supplier == null) {
                    // 将factory赋值给supplier
                    supplier = factory;
                }
            } else {
                if (valuesMap.replace(subKey, supplier, factory)) {
                    supplier = factory;
                } else {
                    // retry with current supplier
                    supplier = valuesMap.get(subKey);
                }
            }
        }
    }
}
Factory

Factory是WeakCache的一个内部类,它实现了Supplier接口,在get方法中,又调用了valueFactory的apply方法创建class,valueFactory是WeakCache的一个成员变量,在WeakCache的构造函数中可以看到传入了valueFactory对象进行初始化,那么接下来就需要回到Proxy类中,看一下如何实例化WeakCache的:

final class WeakCache<K, P, V> {
    
    private final BiFunction<K, P, V> valueFactory;
    
    public WeakCache(BiFunction<K, P, ?> subKeyFactory,
                     BiFunction<K, P, V> valueFactory) {
        this.subKeyFactory = Objects.requireNonNull(subKeyFactory);
        // 初始化
        this.valueFactory = Objects.requireNonNull(valueFactory);
    }

    // Factory
    private final class Factory implements Supplier<V> {

        private final K key;
        private final P parameter;
        private final Object subKey;
        private final ConcurrentMap<Object, Supplier<V>> valuesMap;

        Factory(K key, P parameter, Object subKey,
                ConcurrentMap<Object, Supplier<V>> valuesMap) {
            this.key = key;
            this.parameter = parameter;
            this.subKey = subKey;
            this.valuesMap = valuesMap;
        }

        @Override
        public synchronized V get() { 
            // 
            Supplier<V> supplier = valuesMap.get(subKey);
            if (supplier != this) {
                return null;
            }
            
            V value = null;
            try {
                // 调用valueFactory的apply方法创建class
                value = Objects.requireNonNull(valueFactory.apply(key, parameter));
            } finally {
                if (value == null) { // remove us on failure
                    valuesMap.remove(subKey, this);
                }
            }
            
            ......
              
            return value;
        }
    }
}
ProxyClassFactory

Proxy中WeakCache初始化的时候使用的是ProxyClassFactory类型的factory:

public class Proxy implements java.io.Serializable {  
     /**
     * WeakCache初始化
     */
    private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
        proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
}

所以调用valueFactory的apply方法的时候会进入到ProxyClassFactory的apply方法,在apply方法中会通过ProxyGenerator动态生成代理类并加载类,然后将实例化的代理类返回:

 private static final class ProxyClassFactory
        implements BiFunction<ClassLoader, Class<?>[], Class<?>>
    {
        // 前缀
        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);
            ......

            long num = nextUniqueNumber.getAndIncrement();
            String proxyName = proxyPkg + proxyClassNamePrefix + num;

            /*
             * 生成代理类
             */
            byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                proxyName, interfaces, accessFlags);
            try {
                // 加载代理,并返回对象
                return defineClass0(loader, proxyName,
                                    proxyClassFile, 0, proxyClassFile.length);
            } catch (ClassFormatError e) {
                
                throw new IllegalArgumentException(e.toString());
            }
        }
    }
ProxyGenerator

ProxyGenerator是Proxy的一个内部类,用于动态生成class:

 private static final class ProxyClassFactory implements BiFunction<ClassLoader, Class<?>[], Class<?>> {  
      public static byte[] generateProxyClass(final String var0, Class<?>[] var1, int var2) {
        ProxyGenerator var3 = new ProxyGenerator(var0, var1, var2);
        // 生成class
        final byte[] var4 = var3.generateClassFile();
        // 是否保存到文件,如果开启了之后,运行程序之后会在包下面生成class文件
        if(saveGeneratedFiles) {
            AccessController.doPrivileged(new PrivilegedAction<Void>() {
                public Void run() {
                    try {
                        int var1 = var0.lastIndexOf(46);
                        Path var2;
                        if(var1 > 0) {
                            Path var3 = Paths.get(var0.substring(0, var1).replace('.', File.separatorChar), new String[0]);
                            Files.createDirectories(var3, new FileAttribute[0]);
                            var2 = var3.resolve(var0.substring(var1 + 1, var0.length()) + ".class");
                        } else {
                            var2 = Paths.get(var0 + ".class", new String[0]);
                        }

                        Files.write(var2, var4, new OpenOption[0]);
                        return null;
                    } catch (IOException var4x) {
                        throw new InternalError("I/O exception saving generated file: " + var4x);
                    }
                }
            });
        }
        return var4;
    }
}
代理类的生成

由于设置了sun.misc.ProxyGenerator.saveGeneratedFiles为true,所以可以在包下面看到生成的代理类$Proxy0:

  1. 它继承了Proxy并实现了Subject,并且在构造函数中需要传入InvocationHandler对象;
  2. 当执行$Proxy0中的execute方法时,实际上调用的是InvocationHandler的invoke方法;

package com.sun.proxy;

import com.example.demo.bean.Subject;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

// 动态生成了一个$Proxy0类,它继承了Proxy并实现了Subject
public final class $Proxy0 extends Proxy implements Subject {
    private static Method m1;
    private static Method m2;
    private static Method m3;
    private static Method m0;
    // 传入InvocationHandler对象
    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})).booleanValue();
        } 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);
        }
    }
    
    // Subject的execute方法
    public final void execute() throws  {
        try {
            // 调用了InvocationHandler的invoke方法
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

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

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[]{Class.forName("java.lang.Object")});
            m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
            m3 = Class.forName("com.example.demo.bean.Subject").getMethod("execute", new Class[0]);
            m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

总结

JDK的动态代理实现原理是在运行中动态生成代理类,这个代理类实现了Subject接口,在对代理类进行实例化的时候,需要传入InvocationHandler,当调用代理类的方法时,会执行InvocationHandler的invoke方法,在invoke方法中再执行真正的目标方法,从而完成代理功能。

参考

【拉勾教育】Dubbo源码解读与实战-代理模式与常见实现

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1096816.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

使用CSS圆锥渐变创建背景图案

本文翻译自 How to create background pattern using CSS & conic-gradient&#xff0c;作者&#xff1a; Temani Afif&#xff0c; 略有删改。 拥有一个好的背景图案可以使你的网站设计与众不同。多亏了CSS渐变&#xff0c;我们可以使用几行代码创建花哨炫酷的图案。在这篇…

品牌被侵权也可以免费维权了

构成品牌侵权的理由一般有&#xff0c;商标侵权、专利侵权、著作权侵权等&#xff0c;当这些侵权链接持续存在&#xff0c;必然会影响品牌声誉和利益&#xff0c;同时不利于品牌和谐健康的渠道环境构建&#xff0c;低价、假货、窜货也可算作侵权&#xff0c;那何种侵权可以免费…

抖音热搜榜上榜策略

热点事件是抖音热搜榜的常客&#xff0c;要想登上热搜榜&#xff0c;首先要把握住热点&#xff0c;抢占先机。关注时事新闻、娱乐圈动态、社会热点等&#xff0c;结合自己的短视频内容&#xff0c;迅速制作出相关的短视频&#xff0c;以便在话题讨论初期就吸引用户的关注。同时…

获取时间Calendar类(LocalDateTime、LocalDate、LocalTime)

1.Calendar 是一个抽象类&#xff0c;并且构造器是private 2.我们只能通过getInstance()来获取实例 3.里面包含了大量的字段和方法提供给程序员 4. Calendar 没有提供对应的格式化的类&#xff0c;因此需要程序员自己组合来输出(灵活) 5.如果我们想要使用24小时制 Calendar.Hou…

二十五、【色调调整基础】

文章目录 1、亮度/对比度a、亮度b、对比度 2、曝光度3、阈值4、色阶5、反相6、黑白7、渐变映射 1、亮度/对比度 a、亮度 亮度是指画面的明亮程度 b、对比度 对比度指的是一幅图像中&#xff0c;明暗区域最亮和最暗之间不同亮度层级的测量&#xff0c;如下图所示&#xff0…

递归最小二乘法RLS

参考&#xff1a;RLS递归最小二乘法(Recursive Least Squares)_hymwgk的博客-CSDN博客

Springboot3 + knife4j(springdoc) 框架整合

前言 kinfe4j文档地址&#xff1a;https://doc.xiaominfo.com/docs/quick-start springdoc文档地址&#xff1a;https://springdoc.org/#demos 原本使用的是springfox&#xff0c;自Springboot3开始&#xff0c;knife4j引入了springdoc。 Maven引入 <dependency><…

无线设备的天线怎么安装最好?

天线作为无线传输过程中一个必不可少的配件&#xff0c;因此天线的安装和选型对于无线传输的稳定性发挥着至关重要的作用。本文将介绍影响天线安装对于无线通信效果的影响。 一、天线的工作原理 天线是一种能量变换器&#xff0c;它把传输线上传播的导行波&#xff0c;变换成在…

有哪些免费的数据恢复软件?EasyRecovery免费版下载

EasyRecovery作为一款专业的数据电脑恢复软件&#xff0c;它能够轻松搞定数据恢复&#xff0c;恢复丢失和删除的文件&#xff0c;支持电脑、相机、移动硬盘、U盘、SD卡、内存卡、光盘、本地电子邮件和 RAID 磁盘阵列等各类存储设备的数据恢复。这个新版本功能更加强大。目前的最…

54 完全平方数

完全平方数 理解&#xff1a;dp[i] 1 min ⁡ j [ 1 , i ] d p [ i − j 2 ] 1 {\min}_{j[1, \sqrt{i}]}dp[i-j^2] 1minj[1,i ​]​dp[i−j2] (不是贪心)题解1 DP 给你一个整数 n &#xff0c;返回 和为 n 的完全平方数的最少数量 。 完全平方数是一个整数&#xff0c;其值…

信号频谱分析与功率谱密度

当我们涉及无线通信、信号处理和电子设备时&#xff0c;信号频谱分析与功率谱密度是两个至关重要的概念。它们帮助我们理解信号的特性、噪声和频率分布&#xff0c;从而优化通信系统、设计滤波器以及进行故障诊断。本文将初步探讨信号频谱分析与功率谱密度&#xff0c;一起理解…

StarRC:版本陈旧引发的多corner提取RC失败

我正在「拾陆楼」和朋友们讨论有趣的话题&#xff0c;你⼀起来吧&#xff1f; 拾陆楼知识星球入口 运行StarRC提取多corner RC时会遇到提取失败的情况&#xff0c;当分次提取各个corner RC时却成功了&#xff0c;这种时候优先考虑nxtgrd版本陈旧的情况。 debug方法也简单&…

理解LoadRunner,基于此工具进行后端性能测试的详细过程(下)

5、录制并增强虚拟用户脚本 从整体角度看&#xff0c;用LoadRunner 开发虚拟用户脚本主要包括下面四步骤&#xff1a; 识别测试应用使用的协议 录制脚本 完善录制得到的脚本 验证脚本的正确性 识别被测应用使用的协议 如果明确知道了被测系统所采用的协议&#xff0c;可…

java小区物业水电费管理系统springboot+vue

智能小区管理系统被人们投放于现在的生活中进行使用&#xff0c;该款管理类软件就可以让管理人员处理信息的时间介于十几秒之间。在这十几秒内就能完成信息的编辑等操作。有了这样的管理软件&#xff0c;智能小区管理信息的管理就离无纸化办公的目标更贴近了。 这次开发的智能小…

RFID智能制造应用:助力企业提升制造效率!

随着企业间竞争加剧&#xff0c;如何提升企业生产效率&#xff0c;降低成本成为不少制造企业持续追求的目标。利用智能制造中的RFID设备&#xff0c;可以对企业入库、盘点、生产、出库等流程进行监控&#xff0c;本文将探讨智能制造中的RFID设备如何帮助企业提升制造效率&#…

Maven打包知识点

一.前言 今天在工作中需要给测试同学提供一个jar包&#xff0c;里面包含了pom里面得外部依赖&#xff0c;我依旧按照往常得方式进行打包 二.外部依赖变成jar包里面的class 发现打的jar包里面并没有外部依赖。 直接上代码 <plugins><plugin><artifactId>ma…

如何实现前端数据持久化(LocalStorage、IndexedDB等)?

聚沙成塔每天进步一点点 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 欢迎来到前端入门之旅&#xff01;感兴趣的可以订阅本专栏哦&#xff01;这个专栏是为那些对Web开发感兴趣、刚刚踏入前端领域的朋友们量身打造的。无论你是完全的新手还是有一些基础的开发…

TIA博途软件中如何设置在程序中自动显示变量的注释信息?

TIA博途软件中如何设置在程序中自动显示变量的注释信息? 本例以TIA博途V15为例进行举例说明 如下图所示,新建一个项目后,打开PLC变量表,这里我选择几个变量进行举例说明,给这几个变量添加注释信息, 打开OB1,编写一句简单的程序,如下图所示,可以看到此时变量只显示名称…

第三章 运算符与表达式Pro

三、运算符与表达式 C语言一共有34个运算符&#xff0c;记这些运算符时从以下几个方面记&#xff1a; 运算符的书写形式&#xff1b; 运算符的运算规则;(两个整数相除,结果为整数) 所能进行的操作数&#xff1b;&#xff08;取余运算符%&#xff0c;操作数只能是整数&#xff0…

面试题:Java反射和new效率对比,差距有多大?

文章目录 一、基础概念二、new 对象和反射创建对象的效率对比所以下面我们来探讨一下为什么这么大差别?总结起来有下面几个原因 三、反射和new 的使用场景反射的部分使用场景new对象和反射的区别 一、基础概念 Java中&#xff0c;一般我们创建一个对象可能会选择new一下个实例…