Android动态代理源码分析

news2024/12/23 9:17:11

前言

前面我们简单介绍了代理模式的使用Android设计模式详解之代理模式,我们也明白了动态代理模式的重要性,那动态代理究竟是如何实现将方法交给InvocationHandler.invoke执行的呢?本文就从源码的角度一步步去探究这个问题;

源码分析

从前面一篇文章我们知道动态代理是通过调用 Proxy.newProxyInstance()方法来实现的,我们就从这个方法入手
Proxy.newProxyInstance()方法:

	//constructorParams成员变量
    private static final Class<?>[] constructorParams =
        { InvocationHandler.class };

 public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {	
    	//先检查传入的InvocationHandler是否为空,为空则直接抛空指针
        Objects.requireNonNull(h);
		//克隆一份代理接口,并通过传入的classLoader去获取一个代理class
        final Class<?>[] intfs = interfaces.clone();
        Class<?> cl = getProxyClass0(loader, intfs);

        try {
   			//获取代理class的带有InvocationHandler参数的构造函数
            final Constructor<?> cons = cl.getConstructor(constructorParams);
            final InvocationHandler ih = h;
            //如果构造函数是私有的,反射设置可访问
            if (!Modifier.isPublic(cl.getModifiers())) {
                cons.setAccessible(true);  
            }
            //通过反射生成代理对象
            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调用反射去创建一个代理类的对象实例;

这里我们再重点分析下代理类的创建流程, Class<?> cl = getProxyClass0(loader, intfs);这段代码

	//proxyClassCache缓存
    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");
        }
      	//很明显这里做了缓存处理
        return proxyClassCache.get(loader, interfaces);
    }

可以看到代理类最终是交给ProxyClassFactory工厂类创建的,我们就重点看下ProxyClassFactory

    /**
     * 一个工厂函数,在给定ClassLoader和接口数组的情况下生成、定义和返回代理类。
     */
    private static final class ProxyClassFactory
        implements BiFunction<ClassLoader, Class<?>[], Class<?>>
    {
        // 代理类的后缀
        private static final String proxyClassNamePrefix = "$Proxy";

        // 用于生成唯一代理类名的下一个数字
        private static final AtomicLong nextUniqueNumber = new AtomicLong();

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

            Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
            //遍历代理接口
            for (Class<?> intf : interfaces) {
                Class<?> interfaceClass = null;
                try {
                	//反射获取代理接口对应的class
                    interfaceClass = Class.forName(intf.getName(), false, loader);
                } catch (ClassNotFoundException e) {
                }
                if (interfaceClass != intf) {
                	//通过classLoader加载的class与原代理接口不一致则抛异常
                    throw new IllegalArgumentException(
                        intf + " is not visible from class loader");
                }
             	//反射获取代理接口对应的class不是一个接口则抛异常
                if (!interfaceClass.isInterface()) {
                    throw new IllegalArgumentException(
                        interfaceClass.getName() + " is not an interface");
                }
	              //验证代理接口是否重复
                if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
                    throw new IllegalArgumentException(
                        "repeated interface: " + interfaceClass.getName());
                }
            }

            String proxyPkg = null;     //代理类包名
            int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
			//遍历代理接口,获取包名信息
            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) {
                proxyPkg = "";
            }

            {
               //获取接口中的所有方法
                List<Method> methods = getMethods(interfaces);
                //根据方法签名等信息进行排序
                Collections.sort(methods, ORDER_BY_SIGNATURE_AND_SUBTYPE);
                //如果方法中的任何两个方法具有相同的名称和参数,但返回类型不一致,则抛异常
                validateReturnTypes(methods);
                //获取所有的异常集合、方法集合
                List<Class<?>[]> exceptions = deduplicateAndGetExceptions(methods);
                Method[] methodsArray = methods.toArray(new Method[methods.size()]);
                Class<?>[][] exceptionsArray = exceptions.toArray(new Class<?>[exceptions.size()][]);
				//代理类名依次递增
                long num = nextUniqueNumber.getAndIncrement();
                String proxyName = proxyPkg + proxyClassNamePrefix + num;
                //生成代码类
                return generateProxy(proxyName, interfaces, loader, methodsArray,
                                     exceptionsArray);
            }
        }
    }

代理类的创建工作交给ProxyClassFactory工厂来完成,ProxyClassFactory.apply方法会遍历所有的代理接口,并做校验、方法排序等工作,最终会调用generateProxy方法来生成代理类,而generateProxy方法是一个native方法!!!

    private static native Class<?> generateProxy(String name, Class<?>[] interfaces,
                                                 ClassLoader loader, Method[] methods,
                                                 Class<?>[][] exceptions);

经过上面的步骤,就通过调用 Proxy.newProxyInstance()方法生成了代理类,并创建了代理类的实例对象返回;

那这个创建的代理类长什么样呢?为什么通过代理类的对象调用代理接口方法就可以执行到InvocationHandler.invoke方法中呢?

我们在调用 Proxy.newProxyInstance()方法前,修改下系统属性如下:

 //修改属性设置保存生成的proxy文件
        System.getProperties().setProperty("jdk.proxy.ProxyGenerator.saveGeneratedFiles", "true")

这样我们就可以看到生成的代理对象
生成的代理对象

我这里还是前面的租客租房例子,生成的代理对象如下:

package com.sun.proxy;

import com.crystal.essayjoker.pattern.proxy.dynamic.IRental;
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 IRental {
    private static Method m1;
    private static Method m3;
    private static Method m4;
    private static Method m2;
    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 void renting() throws  {
        try {
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

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

    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 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"));
            m3 = Class.forName("com.crystal.essayjoker.pattern.proxy.dynamic.IRental").getMethod("renting");
            m4 = Class.forName("com.crystal.essayjoker.pattern.proxy.dynamic.IRental").getMethod("rentingOut");
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

可以看到调用proxy的代理方法最终还是通过反射调用传入的InvocationHandler的invoke方法,至此谜题解开;

总结

动态代理的实现原理:
调用 Proxy.newProxyInstance()方法会通过反射带有InvocationHandler参数的构造函数生成代理类,并返回代理类的实例,当调用代理类的方法时,又会通过反射执行InvocationHandler对应的invoke方法,从而实现动态代理这套机制;

结语

如果以上文章对您有一点点帮助,希望您不要吝啬的点个赞加个关注,您每一次小小的举动都是我坚持写作的不懈动力!ღ( ´・ᴗ・` )

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

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

相关文章

开发工具与低代码开发平台丨上海道宁联合Grapecity为您提供各类软件开发工具和服务

Grapecity 提供软件开发技术和低代码平台 通过各类软件开发工具和服务 提升开发效率 推动软件产业发展 葡萄城研发各类开发控件和 商业智能报表工具 提供控件技术和数据分析工具 基于专业控件的技术积累 发布低代码开发平台 开发商介绍 葡萄城成立于1980年&#xff0c…

C# XML基本操作

一 XML的内容 二 XML的处理方式 1 DOM 文档对象模型&#xff08;Document Object Model&#xff09; 2 SAX XML解析的简单API&#xff08;Simple API for XML&#xff09; 3 .NET提供了XML支持&#xff1a;System.XML名称空间 三 常用的XML类 1 XmlDocument .LoadXml()…

linux安装mongodb的详细教程

目录 一、下载MongoDB Community Server(社区版) 三、启动mongodb 四、利用配置文件启动mongodb服务 一、下载MongoDB Community Server(社区版) 1.1、下载地址 Download MongoDB Community Server | MongoDB 1.2、选择安装版本 二、上传下载后的mongodb安装包&#xff0c;如…

vue路由写法小结

咱们先说路由的匹配语法&#xff0c;动态路由在项目中经常用到&#xff0c;在某些情况下我们要对路径参数进行正则表达式校验。 基础用法&#xff1a; 使用正则表达式后&#xff08;仅匹配数字&#xff09;&#xff1a; 加入*传入多个params参数&#xff0c;此时参数可以传多个…

使用Nordic的nrf52832进行主从机连接抓包分析

主机使用例程&#xff1a;nRF5_SDK_17.1.0_ddde560\examples\ble_central\ble_app_blinky_c\pca10040\s132\arm5_no_packs 从机使用例程&#xff1a;nRF5_SDK_17.1.0_ddde560\examples\ble_peripheral\ble_app_blinky\pca10040\s132\arm5_no_packs nrf528321. 空中数据包格式1.…

基于Amlogic 安卓9.0, 驱动简说(二):字符设备驱动,自动创建设备

文章目录一、前言二、系列文章三、替换部分3.1 自动分配设备号3.2 自动创建设备四、解析&#xff1a;完整源码4.1 helloworld_amlogic_char_driver_auto_mknode.c4.2 Makefile五、编译执行&#xff08;1&#xff09;编译及部署&#xff08;2&#xff09;加载ko文件&#xff08;…

HCIP作业三

文章目录一&#xff0c;建立拓扑图1&#xff0c;目的&#xff1a;实现全网可达二&#xff0c;在OSPF1区域1&#xff0c;所有路由器配置IP地址&#xff08;包含OSPF100区域&#xff09;2&#xff0c;进行O1的宣告&#xff0c;不能宣告ISP网段3&#xff0c;在O1区域的R3写缺省去I…

MIT6.830-2022-lab3实验思路详细讲解

文章目录一、实验概览二、实验过程成本估算&#xff08;Cost Estimation&#xff09;&#xff1a;基数和选择率Exercise 1: IntHistogramExercise 2: TableStatsExercise 3: Join Cost EstimationExercise 4: Join Cost EstimationExtra Credit总结一、实验概览 对于这次lab&a…

尚医通-数据字典接口-前端列表(十三)

目录&#xff1a; &#xff08;1&#xff09;数据字典接口-需求和准备 &#xff08;2&#xff09;数据字典接口-列表 &#xff08;3&#xff09; 数据字典前端-列表 &#xff08;1&#xff09;数据字典接口-需求和准备 前面我们完成了医院设置模块的开发&#xff0c;现在来…

js数组篇

数组定义 var arr1new Array(33,44,55,66); var arr2[A,B,C,D]; var arr3new Array(4); 数组常用方法 splice()方法用于替换数组中的指定项 splice(3,2,X,Y,Z)&#xff1a;从下标为3的项开始&#xff0c;连续替换2项 &#xff0c;插入XYZ var arr[A,B,C,D,E,F,G]; arr.spl…

Java线程间通信机制 (等待唤醒机制)

1.1 线程间通信 概念&#xff1a;多个线程在处理同一个资源&#xff0c;但是处理的动作&#xff08;线程的任务&#xff09;却不相同。 比如&#xff1a;线程A用来生成包子的&#xff0c;线程B用来吃包子的&#xff0c;包子可以理解为同一资源&#xff0c;线程A与线程B处理的动…

[JavaEE] Thread类及其常见方法

专栏简介: JavaEE从入门到进阶 题目来源: leetcode,牛客,剑指offer. 创作目标: 记录学习JavaEE学习历程 希望在提升自己的同时,帮助他人,,与大家一起共同进步,互相成长. 学历代表过去,能力代表现在,学习能力代表未来! 目录 1.Thread 的常见构造方法 2.Thread 的几个常见属性…

机器学习基石2(noise和error)

文章目录一、什么是noise&#xff1f;二、什么是error&#xff1f;三、常用error一、什么是noise&#xff1f; 我们之前的讨论都是一种理想化的说明&#xff0c;比如数据来源于目标函数fff&#xff0c;似乎我们手里拿到的数据是这样得来的&#xff0c;随机取一个输入&#xff0…

DOM算法系列009-判断给定节点是否为块级元素

UID: 20221227215215 aliases: tags: source: cssclass: created: 2022-12-27 块级元素 给定一个节点&#xff0c;如何判定它是否为块级元素呢&#xff1f; 首先&#xff0c;块级元素一定是元素节点&#xff0c;所以它的nodeType 1其次&#xff0c;我们思考&#xff0c;有什…

Redis分布式缓存、秒杀

目录一、单点Redis的问题二、RDB三、AOF四、Redis优化秒杀流程1、秒杀步骤&#xff1a;2、Redis优化秒杀步骤&#xff1a;3、秒杀的lua脚本4、调用秒杀的lua脚本5、通过线程池&#xff0c;操作阻塞队列五、基于Redis实现共享session登录NoSQL数据库进阶实战哪吒精品系列文章一、…

linux常用命令(二)-文件操作

文件创建 - touch 一般使用touch 来创建某个新增的文件 语法 touch [-acfm][文件名]a&#xff1a;改变档案的读取时间记录c&#xff1a;假如目的档案不存在&#xff0c;不会建立新的档案。与 --no-create 的效果一样。f&#xff1a;不使用&#xff0c;是为了与其他 unix 系统…

python的tkinter(图形用户界面)

目录标题什么是图形用户界面&#xff08;GUI&#xff09;Tinter函数和参数说明&#xff08;常用&#xff09;Lable(标签)&#xff1a;效果Button(按钮)效果Entry(文本框)效果Text &#xff08;多行文本框&#xff09;Canvas(画布)效果Message(消息弹窗)效果什么是图形用户界面&…

kaggle学习笔记-otto-baseline4-本地CV的构建

总览 步骤 1 - 生成候选 对于每个测试用户&#xff0c;我们生成可能的选择&#xff0c;即候选人。、我们从 5 个来源生成候选人&#xff1a; 点击、购物车、订单的用户历史记录测试周期间最受欢迎的 20 次点击、购物车、订单具有类型权重的点击/购物车/订单到购物车/订单的共…

中国芯片奇才,仅用三年打破欧美垄断,创造奇迹

有这么一位中国人&#xff0c;打破了欧美长达10年的芯片技术垄断。这位最该追的星&#xff0c;她是谁&#xff1f;又是如何让欧美芯片领域闻风丧胆。早在2017年&#xff0c;芯片国产化已接近50%&#xff0c;然而25g以上芯片却仅有3%&#xff0c;该技术一直掌握在欧美等发达国家…

融云任杰:激活组织生命力 让听见炮火的人做决策 | TGO专访

任杰&#xff0c;融云联合创始人兼首席科学家&#xff0c;TGO 鲲鹏会&#xff08;北京&#xff09;学员&#xff1b;曾就职于微软和神州泰岳等公司&#xff0c;在微软两次获得全球杰出员工奖&#xff0c;曾负责中国联通搭建 WAP 网关、增值业务管理平台&#xff1b;在神州泰岳期…