深入浅出动态代理

82 阅读5分钟

前言

面试中经常被问到动态代理相关内容,每次都答得不够完美,又因近期在尝试手写RPC框架,了解到不少的动态代理相关的内容,顺道总结一下,于是就有了这篇文章

静态代理? 动态代理!

区别

静态代理是在编译期间就生成了实际的字节码和对应的class文件,而动态代理可以在运行中动态生成代理类。

  • 静态代理示例
public interface Service {
    void perform();
}
// 实际类
public class RealService implements Service {
    @Override
    public void perform() {
        System.out.println("Performing service...");
    }
}
// 代理类也要实现和被代理的类实现的同一个接口才能进行代理
public class StaticProxy implements Service {
	// 将需要代理的类封装到自己的内部
    private final Service realService;
	
    public StaticProxy(Service realService) {
        this.realService = realService;
    }
	// 实际就是在实现的方法中进行代理操作,然后执行被代理的类的方法
    @Override
    public void perform() {
        System.out.println("Static Proxy: Before performing service");
        realService.perform();
        System.out.println("Static Proxy: After performing service");
    }
}

实际上就是通过将被代理类封装到内部,然后调用。缺点很明显:没新增一个代理都需要重新新建一个类然后重新写一个包装。

  • 动态代理实例(JDK代理)
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public interface Service {
    void perform();
}

public class RealService implements Service {
    @Override
    public void perform() {
        System.out.println("Performing service...");
    }
}

public class DynamicProxyHandler implements InvocationHandler {
    private final Object target;

    public DynamicProxyHandler(Object target) {
        this.target = target;
    }
	// 
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("Dynamic Proxy: Before performing service");
        Object result = method.invoke(target, args);
        System.out.println("Dynamic Proxy: After performing service");
        return result;
    }
}

public class DynamicProxyDemo {
    public static void main(String[] args) {
        Service realService = new RealService();
        Service proxyInstance = (Service) Proxy.newProxyInstance(
                realService.getClass().getClassLoader(),
                realService.getClass().getInterfaces(),
                new DynamicProxyHandler(realService)
        );

        proxyInstance.perform();
    }
}

源码解读

参考

解密Proxy

(以下是基于Java8的源码) 我们在使用JDK动态代理的时候使用的最多的就是Proxy.newProxyInstance()方法,接下来我们就来解密这个方法是如何实现我们的动态代理。

 @CallerSensitive
    public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {
	    // 检查空指针异常
        Objects.requireNonNull(h);

        final Class<?>[] intfs = interfaces.clone();
        // 安全检查
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
        }

        // 生成代理类
        Class<?> cl = getProxyClass0(loader, intfs);

        /*
         * Invoke its constructor with the designated invocation handler.
         */
        try {
            if (sm != null) {
                checkNewProxyPermission(Reflection.getCallerClass(), cl);
            }

            final Constructor<?> cons = cl.getConstructor(constructorParams);
            final InvocationHandler ih = h;
            if (!Modifier.isPublic(cl.getModifiers())) {
                AccessController.doPrivileged(new PrivilegedAction<Void>() {
                    public Void run() {
                        cons.setAccessible(true);
                        return null;
                    }
                });
            }
            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);
        }
    }

参数解析:

  • loader:类加载器,用于自定义加载类
  • interface 接口数组,代理类将实现这些接口
  • h 调用处理器,处理代理实例上的方法调用 这段代码的主要意思是获取调用者类和代理类构造函数。 接着关注getProxyClass0方法
 private static Class<?> getProxyClass0(ClassLoader loader,
                                           Class<?>... interfaces) {
        // 规定对实现的接口数量不得超过这个数量
        if (interfaces.length > 65535) {
            throw new IllegalArgumentException("interface limit exceeded");
        }

        // If the proxy class defined by the given loader implementing
        // the given interfaces exists, this will simply return the cached copy;
        // otherwise, it will create the proxy class via the ProxyClassFactory
        return proxyClassCache.get(loader, interfaces);
    }

接着看proxyClassCache

// 使用WeakCache降低出现内存泄漏的概率
private static final WeakCache<ClassLoader, Class<?>[], Class<?>>  
    proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());

核心是ProxyClassFactroy,让我们关注它

// 核心是实现BiFunction
/**
 * ProxyClassFactory 是一个工厂类,用于生成和定义代理类。
 * 它实现了 BiFunction 接口,接受 ClassLoader 和接口数组作为输入,
 * 返回生成的代理类。
 */
private static final class ProxyClassFactory
    implements BiFunction<ClassLoader, Class<?>[], Class<?>> {

    // 所有代理类名称的前缀
    private static final String proxyClassNamePrefix = "$Proxy";

    // 用于生成唯一代理类名称的下一个数字
    private static final AtomicLong nextUniqueNumber = new AtomicLong();

    /**
     * 生成并定义代理类。
     *
     * @param loader 用于定义代理类的��加载器
     * @param interfaces 代理类要实现的接口数组
     * @return 生成的代理类
     * @throws IllegalArgumentException 如果接口数组中的任何接口不可见或不是接口,或接口重复
     */
    @Override
    public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {

        // 使用 IdentityHashMap 检查接口是否重复
        Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
        for (Class<?> intf : interfaces) {
            /*
             * 验证类加载器是否将此接口的名称解析为相同的 Class 对象。
             */
            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: " + loader);
            }
            /*
             * 验证 Class 对象是否实际代表一个接口。
             */
            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;     // 定义代理类的包
        int accessFlags = Modifier.PUBLIC | Modifier.FINAL;

        /*
         * 记录非公共代理接口的包,以便代理类将在同一包中定义。
         * 验证所有非公共代理接口是否在同一包中。
         */
        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");
                }
            }
        }

        if (proxyPkg == null) {
            // 如果没有非公共代理接口,使用 com.sun.proxy 包
            proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
        }

        /*
         * 选择要生成的代理类的名称。
         */
        long num = nextUniqueNumber.getAndIncrement();
        String proxyName = proxyPkg + proxyClassNamePrefix + num;

        /*
         * 生成指定的代理类。
         */
        byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
            proxyName, interfaces, accessFlags);
        try {
            return defineClass0(loader, proxyName,
                                proxyClassFile, 0, proxyClassFile.length);
        } catch (ClassFormatError e) {
            /*
             * 这里的 ClassFormatError 意味着(除非代理类生成代码中有错误),
             * 否则是代理类创建时提供的参数存在其他无效方面(例如虚拟机限制超出)。
             */
            throw new IllegalArgumentException(e.toString());
        }
    }
}

BiFunction 是 Java 8 引入的一个函数式接口,位于 java.util.function 包中。它代表一个接受两个输入参数并返回一个结果的函数。BiFunction 接口定义如下:

@FunctionalInterface
public interface BiFunction<T, U, R> {
    R apply(T t, U u);

    // 其他默认方法
}

然后我们顺着往下找,通过generateProxyClass ->generateClassFile ->generateMethod 代码过长这里只给出比较重要的部分

{
	// 获取InvocationHandler字段
	 out.writeByte(opc_getfield);
            out.writeShort(cp.getFieldRef(
                superclassName,
                handlerFieldName, "Ljava/lang/reflect/InvocationHandler;"));
	// 加载Method对象
            code_aload(0, out);

            out.writeByte(opc_getstatic);
            out.writeShort(cp.getFieldRef(
                dotToSlash(className),
                methodFieldName, "Ljava/lang/reflect/Method;"));
	// 后面紧跟处理方法参数的部分,这部分省略

	// 重点!
	// 调用InvocationHandler.invoke方法
	 out.writeByte(opc_invokeinterface);
            out.writeShort(cp.getInterfaceMethodRef(
                "java/lang/reflect/InvocationHandler",
                "invoke",
                "(Ljava/lang/Object;Ljava/lang/reflect/Method;" +
                    "[Ljava/lang/Object;)Ljava/lang/Object;"));
            out.writeByte(4);
            out.writeByte(0);
    // 后面设置返回值和异常处理等信息,最后返回方法的信息
	return minfo;
}

到这里我们才终于发现JDK动态代理的原理: 原来就是在代理方法中通过Proxy引用了自定义的InvocationHandler!! 通过Proxy.newProxyInstance()方法将invocationHandler传入,然后生成代理类来继承Proxy类,从而拿到InvocationHandler,最后在代理类中调用invoke()方法

SpringAOP !(未完待续)