`代理` 指的是代表授权方执行处理事务。在编程中,通常是通过一个代理对象代表目标对象去执行方法,是对调用目标的一个包装。这样来保证目标对象方法的安全性、或者增强目标对象的方法功能。
Java 有 3 种代理方式:
静态代理
通过手动创建代理对象,来实现对目标对象的代理。在这过程中,一般存在 3 种对象:客户端、目标对象、代理对象。
客户端通过代理对象去调用目标对象的真实方法。
对象的接口
public interface IService { void request();}
复制代码
真正的目标对象
public class RealService implements IService { @Override public void request() { System.out.println("真正处理请求内容"); }}
复制代码
代理类
public class ProxyService implements IService { private IService realService;
public ProxyService(IService service) { this.realService = service; }
@Override public void request() { // ...执行真实 request() 前其他逻辑 System.out.println("request() 方法前逻辑"); this.realService.request(); System.out.println("request() 方法后逻辑"); // ...执行真实 request() 后其他逻辑 }}
复制代码
客户端实现
public class StaticProxyClient {
public void callRequest() { // 目标对象 IService realService = new RealService(); // 代理对象 IService proxyService = new ProxyService(realService);
// 通过代理对象执行真正的逻辑,并且在代理对象中对请求进行增强 proxyService.request(); }}
复制代码
静态代理需要手动地为每个目标类创建代理类,而代理类中的逻辑都是类似的简单的,为了更简单地使用代理出现了动态代理,通过在运行期动态创建接口对象的代理,避免了手动去创建静态代理类。
动态代理是在运行时动态生成的,即编译完成后没有实际的 class 文件,而是在运行时动态生成类字节码,并加载到 JVM 中。
JDK 原生动态代理
JDK 提供了 Proxy.newProxyInstance() 来创建动态代理。实现步骤如下:
创建被代理的目标类及它的接口
创建接口 InvocationHandler 的实现类,它必须实现 invoke 方法
通过 Proxy 的静态方法 newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h) 创建一个代理对象
通过代理对象调用方法,代理的方法会被 InvocationHandler 的实现类接管
// 实现 InvocationHandler 类,它的 invoke() 方法会接管代理调用的方法public class MyInvocationHandler implements InvocationHandler { private Object target;
public MyInvocationHandler(Object target) { this.target = target; }
@Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("代理方法前逻辑"); Object result = method.invoke(this.target, args); System.out.println("代理方法后逻辑"); return result; }}
// JDK 动态代理测试public class JDKDynamicProxyClient { public static void main(String[] args) { RealService realService = new RealService(); InvocationHandler handler = new MyInvocationHandler(realService); IService service = (IService) Proxy.newProxyInstance( // 通常是接口类的 ClassLoader IService.class.getClassLoader(), // 要实现的接口数组 new Class[]{IService.class}, // 代理用来处理调用方法的 InvocationHandler handler ); // 通过代理类调用方法 service.request(); }}
复制代码
原理分析
主要是看 `Proxy.newProxyInstance()` 方法。
private static final WeakCache<ClassLoader, Class<?>[], Class<?>> proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
复制代码
Proxy.newProxyInstance() Proxy.getProxyClass0() ProxyClassFactory.get() #先从代理类缓存中查询,没有时从代理类工厂中创建 ProxyClassFactory.apply() #代理类工厂创建 ProxyGenerator.generateProxyClass() #代理类生成器,生成代理类代码 ProxyClassFactory.defineClass0() #加载生成的 class 文件到 jvm,java 接着就能调用了
复制代码
最终生成的代理类关键代码如下:
// 代理类继承 Proxy 类,并实现 IService 接口// 代理类名称固定位 "com.sun.proxy.$Proxy"+自增数字public final class com.sun.proxy.$Proxy0 extends Proxy implements IService { private static Method m1; private static Method m2; private static Method m3; private static Method m0;
// 构造函数中设置代理类的 InvocationHandler public com.sun.proxy.$Proxy0(InvocationHandler var1) throws { super(var1); } // ... public final void request() throws { try { // 实际是 handler 中调用目标类的方法 super.h.invoke(this, m3, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } }
static { try { m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object")); m2 = Class.forName("java.lang.Object").getMethod("toString"); // 代理的方法 m3 = Class.forName("proxy.IService").getMethod("request"); m0 = Class.forName("java.lang.Object").getMethod("hashCode"); } catch (NoSuchMethodException var2) { throw new NoSuchMethodError(var2.getMessage()); } catch (ClassNotFoundException var3) { throw new NoClassDefFoundError(var3.getMessage()); } }}
复制代码
当代理执行到 request() 方法时,实际执行的是 InvocationHandler 的 invoke() 方法,并在方法中通过反射调用目标类的真实方法。
JDK 动态代理要求被代理的对象必须有对应的一个或多个接口,不能给原类生成动态代理类。
CGLIB 动态代理
CGLIB 是一个强大的高性能代码生成包,可以在运行期转换字节码并生成目标类的子类。
CGLIB 创建代理类对象是通过 Enhancer 类实现的,创建代理的主要步骤:
// 被代理的类不需要实现接口,但是被代理的方法和类不能是 finalpublic class RequestService { public void request() { System.out.println("真正处理请求内容"); }}
// CGLIB 代理测试public class CGLIBProxyClient { public static void main(String[] args) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(RequestService.class); // 这里是一个匿名类,被代理方法的处理类,需要实现 MethodInterceptor 接口的 intercept 方法 enhancer.setCallback(new MethodInterceptor() { @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { System.out.println("真实方法前逻辑..."); methodProxy.invokeSuper(o, objects); System.out.println("真实方法后逻辑..."); return null; } }); RequestService requestService = (RequestService) enhancer.create(); requestService.request(); }}
复制代码
在项目中通过增加下面设置,可以将生成的动态类保存到代码的根目录
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "./");
复制代码
运行创建代理后,就可以在项目根目录中看到 enhancer.create 创建的动态类,它的关键代码如下:
// 继承目标类 RequestServicepublic class RequestService$$EnhancerByCGLIB$$6a2da525 extends RequestService implements Factory { // 重写了 request() 方法 public final void request() { MethodInterceptor var10000 = this.CGLIB$CALLBACK_0; if (this.CGLIB$CALLBACK_0 == null) { CGLIB$BIND_CALLBACKS(this); var10000 = this.CGLIB$CALLBACK_0; }
// enhancer.setCallback() 已经设置 callback,所以这里不为空 if (var10000 != null) { // 调用 callback 中实现的 intercept() 方法 var10000.intercept(this, CGLIB$request$0$Method, CGLIB$emptyArgs, CGLIB$request$0$Proxy); } else { super.request(); } }}
复制代码
在 Callback 中通过 `methodProxy.invokeSuper(o, objects);` 来调用目标类的方法
public class MethodProxy { public Object invokeSuper(Object obj, Object[] args) throws Throwable { try { // 初始化代理类、目标类信息 // fci.f2 为代理类; fci.i2 为代理类中的代理方法的方法下标 init(); FastClassInfo fci = fastClassInfo; // 代理类通过下标调用代理方法 return fci.f2.invoke(fci.i2, obj, args); } catch (InvocationTargetException e) { throw e.getTargetException(); } }}
复制代码
自动生成的 FastClass 类主要代码如下:
// 继承 FastClass 类public class RequestService$$FastClassByCGLIB$$86d56cc6 extends FastClass { // 根据方法签名的 hash 值映射方法的下标 public int getIndex(String var1, Class[] var2) { switch(var1.hashCode()) { // ... case 1095692943: if (var1.equals("request")) { switch(var2.length) { case 0: return 0; } } } return -1; } // 代理类实际调用的地方,通过代理方法的下标调用目标类方法(不是反射方式) public Object invoke(int var1, Object var2, Object[] var3) throws InvocationTargetException { RequestService var10000 = (RequestService)var2; int var10001 = var1; try { switch(var10001) { case 0: var10000.request(); return null; case 1: return new Boolean(var10000.equals(var3[0])); case 2: return var10000.toString(); case 3: return new Integer(var10000.hashCode()); } } catch (Throwable var4) { throw new InvocationTargetException(var4); } throw new IllegalArgumentException("Cannot find matching method/constructor"); }}
复制代码
最后
有问题,欢迎留言交流
参考内容
https://time.geekbang.org/column/article/7489
https://time.geekbang.org/column/article/10076
https://zhuanlan.zhihu.com/p/35144462
评论