动态代理机制

一、概述

区别于静态代理,动态代理提供了一种手段,能够在运行时生成代理类,并针对代理对象统一处理。所有业务逻辑都收敛到了 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
相关推荐
文心快码BaiduComate8 小时前
CCF程序员大会码力全开:AI加速营决赛入围名单揭晓,12月6日大理见!
前端·百度·程序员
vivo互联网技术8 小时前
从不足到精进:H5即开并行加载方案的演进之路
前端·h5·webview·客户端·大前端
我命由我123458 小时前
微信小程序 - 内容弹出框实现(Vant Weapp 实现、原生实现)
开发语言·前端·javascript·微信小程序·小程序·前端框架·js
裴嘉靖8 小时前
uniapp做的APP和安卓苹果做的什么区别
前端
申阳8 小时前
Day 20:开源个人项目时的一些注意事项
前端·后端·程序员
天蓝色的鱼鱼8 小时前
大文件上传实战:基于Express、分片、Web Worker与压缩的完整方案
前端·node.js
500佰8 小时前
解读NotebookLM基于AI的PTT生成 程序化处理方法
前端·google·程序员
前端老宋Running8 小时前
别再给组件“打洞”了:这才是 React 组件复用的正确打开方式
前端·javascript·前端框架
u***28478 小时前
Spring Boot项目接收前端参数的11种方式
前端·spring boot·后端
pcm1235678 小时前
java中用哈希表写题碰到的误区
java·前端·散列表