动态代理机制

一、概述

区别于静态代理,动态代理提供了一种手段,能够在运行时生成代理类,并针对代理对象统一处理。所有业务逻辑都收敛到了 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
相关推荐
JavaGuide20 分钟前
一个 SKILL.md 拿下 2.3 万 Star:让 Codex/Claude Code 少说废话、先给答案
前端·后端
晴天161 小时前
多个 VS Code 项目会导致 Chrome 和 Electron 应用一起卡住?-Day30
前端·chrome·electron
BigTopOne8 小时前
【WebRtc】DTLS-SRTP 加密完整流程详解
前端
陈随易8 小时前
pm2 替代品,nodejs&bun 线上部署必备工具
前端·后端·程序员
单线程_018 小时前
从案例分析 Vue3 Tokenizer 源码一
前端·javascript·vue.js
BigTopOne8 小时前
【WebRtc】-ICE Candidate 与 STUN/TURN 原理详解
前端
l1258658 小时前
# LangGraph Memory机制深度解析:短期记忆与长期记忆的工程实践
前端·人工智能·python·langchain·bootstrap
码视野9 小时前
基于 Spring Boot + Vue3 的【大学英语四六级 (CET-4/6) 作文智能评分与句式润色系统】设计与实现(含PRD/三端高保真源码/大屏)
java·前端·人工智能·spring boot·后端·vue3
大模型码小白9 小时前
AI 对话流性能调优:万级消息的虚拟滚动落地
java·大数据·前端·javascript·人工智能·算法·机器学习
xm_xm_xm_110 小时前
react17版本以前类组件常用操作
前端·javascript·react.js