动态代理机制

一、概述

区别于静态代理,动态代理提供了一种手段,能够在运行时生成代理类,并针对代理对象统一处理。所有业务逻辑都收敛到了 InvocationHandler.invoke 中,避免了业务逻辑过于冗余。

二、代理类

java 复制代码
public static Object newProxyInstance(ClassLoader loader,
                                      Class<?>[] interfaces,
                                      InvocationHandler h)
        throws IllegalArgumentException {
    Objects.requireNonNull(h);
    
    final Class<?>[] intfs = interfaces.clone();
    // 1. 生成带代理类
    Class<?> cl = getProxyClass0(loader, intfs);
    try {
        final Constructor<?> cons = cl.getConstructor(constructorParams);
        final InvocationHandler ih = h;
        // 2. 修改访问标记符
        if (!Modifier.isPublic(cl.getModifiers())) cons.setAccessible(true);
        // 3. 反射创建对象
        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 在首次加载的时候,也是通过 ProxyClassFactory 来完成的。

java 复制代码
private static Class<?> getProxyClass0(ClassLoader loader,
                                       Class<?>... interfaces) {
    if (interfaces.length > 65535) {
        throw new IllegalArgumentException("interface limit exceeded");
    }
    // 1. 首次这里取到的肯定是 null 对象,然后交给了 ProxyClassFactory 处理
    return proxyClassCache.get(loader, interfaces);
}

ProxyClassFactory 的生成代理类源码如下 :

java 复制代码
public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {


    Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
    
    // 1、代理对象接口进行校验
    for (Class<?> intf : interfaces) {
        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");
        }
        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;     // package to define proxy class in
    int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
    // 2. 代理对象包名进行校验
    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");
            }
        }
    }
    {
        // 3. 生成代理对象
        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);
    }
}

生成代理主要经历了如下几个步骤:

  • 代理对象校验
    • 当前的类加载器能够加载
    • 代理对象必须是接口
    • 同类型接口不能被多次代理
  • 代理对象路径校验,必须是相同的包名
  • 生成代理对象
    • 递归获取代理对象的所有方法,根据接口签名和子类型进行排序
    • 获取代理方法的所有的异常
    • 调用 Native 方法生成代理对象:包名 + $Proxy + number
相关推荐
weedsfly8 分钟前
从类继承到自定义 Hook:模板方法模式的前端演进
前端·javascript·面试
Quz9 分钟前
QML 线性布局:Row/RowLayout 与 Column/ColumnLayout
前端·qt
你为她披上外套时我正站在窗外13 分钟前
用 Rust + WebAssembly 写了个字幕工具,文件全程不离开浏览器
前端
帅帅哥的兜兜13 分钟前
AI模型初了解
前端
bonechips14 分钟前
React + WebGPU(二):合成事件与进度条组件封装
前端·react.js
我是大卫17 分钟前
【图】React源码解析-从底层数据结构、渲染调度到闭包原理深挖useRef的核心机制
前端·react.js·源码
光影少年23 分钟前
RN导航与路由
前端·react native·react.js
smallcelebration34 分钟前
139 @vueuse/core依赖介绍使用
前端·javascript·vue.js
蒜苔肉丝1 小时前
Vue3 动态表单:模块复用 + 动态增删行 + 数据回显
前端·javascript·vue.js
Cxiaomu1 小时前
从 TRTC Demo 到独立音视频服务:React + TRTC Web SDK 服务化实践
前端·react.js·音视频