动态代理机制

一、概述

区别于静态代理,动态代理提供了一种手段,能够在运行时生成代理类,并针对代理对象统一处理。所有业务逻辑都收敛到了 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
相关推荐
橙序员小站2 小时前
Agent Skill 是什么?一文讲透 Agent Skill 的设计与实现
前端·后端
炫饭第一名4 小时前
速通Canvas指北🦮——基础入门篇
前端·javascript·程序员
王晓枫4 小时前
flutter接入三方库运行报错:Error running pod install
前端·flutter
符方昊4 小时前
React 19 对比 React 16 新特性解析
前端·react.js
ssshooter4 小时前
又被 Safari 差异坑了:textContent 拿到的值居然没换行?
前端
曲折5 小时前
Cesium-气象要素PNG色斑图叠加
前端·cesium
Forever7_5 小时前
Electron 淘汰!新的桌面端框架 更强大、更轻量化
前端·vue.js
Angelial5 小时前
Vue3 嵌套路由 KeepAlive:动态缓存与反向配置方案
前端·vue.js
jiayu5 小时前
Angular学习笔记24:Angular 响应式表单 FormArray 与 FormGroup 相互嵌套
前端
jiayu5 小时前
Angular6学习笔记13:HTTP(3)
前端