前言
面试中经常被问到动态代理相关内容,每次都答得不够完美,又因近期在尝试手写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方法
接着看proxyClassCacheprivate 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); }
核心是ProxyClassFactroy,让我们关注它// 使用WeakCache降低出现内存泄漏的概率 private static final WeakCache<ClassLoader, Class<?>[], Class<?>> proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
BiFunction 是 Java 8 引入的一个函数式接口,位于 java.util.function 包中。它代表一个接受两个输入参数并返回一个结果的函数。BiFunction 接口定义如下:// 核心是实现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()); } } }
然后我们顺着往下找,通过generateProxyClass ->generateClassFile ->generateMethod@FunctionalInterface public interface BiFunction<T, U, R> { R apply(T t, U u); // 其他默认方法 }
代码过长这里只给出比较重要的部分
到这里我们才终于发现JDK动态代理的原理:{ // 获取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; }
原来就是在代理方法中通过Proxy引用了自定义的InvocationHandler!!
通过Proxy.newProxyInstance()方法将invocationHandler传入,然后生成代理类来继承Proxy类,从而拿到InvocationHandler,最后在代理类中调用invoke()方法