动态代理机制

一、概述

区别于静态代理,动态代理提供了一种手段,能够在运行时生成代理类,并针对代理对象统一处理。所有业务逻辑都收敛到了 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
相关推荐
码码哈哈0.012 分钟前
macos26 Liquid class 示例代码
前端
hhemin26 分钟前
web前端给项目加入skills目录,Ai自动查找技能(后端也能参考)
前端
代码煮茶31 分钟前
Vue3 组件库二次封装实战 | 基于 Element Plus 封装企业级 UI 组件库
前端·javascript·vue.js
KaMeidebaby32 分钟前
卡梅德生物技术快报|单克隆抗体人源化 PEG 修饰质控方法体系构建与验证
服务器·前端·数据库·人工智能·算法·百度·新浪微博
元宵大师38 分钟前
[升级V2.1.5]回测模块重构:参数确认+异步进度+日志持久化!本地Web版多因子轮动系统
前端·重构
咋吃都不胖lyh1 小时前
限流重试、指数退避、随机抖动
前端
之歆1 小时前
DAY_11JavaScript BOM与DOM深度解析:底层原理与工程实践(上)
开发语言·前端·javascript·ecmascript
冴羽yayujs1 小时前
GitHub 前端热榜项目 - 日榜(2026-05-17)
前端·github
老马95271 小时前
opencode8-桌面应用实战 3
前端·人工智能·后端