「代理模式」是设计模式的一种,代理模式中有两个关键的成员:「代理类」(Proxy)和「被代理类」(RealSubject)
那Proxy有啥用呢,直接访问RealSubject不行嘛?
代理模式一般有两种实现方法:静态代理和动态代理。
静态代理就是上述UML图的实现方法,可以看到「代理类」Proxy内聚一个RealObject,实现共同的接口,可以很轻松地在这个方法上做加强。
接口:
csharp
复制代码
public interface Subject { void dosth(); }
被代理类:
java
复制代码
public class RealSubject implements Subject{ @Override public void dosth(){ System.out.println("dosth..."); } }
代理类:
java
复制代码
public class Proxy implements Subject{ private Subject subject; @Override public void dosth(){ System.out.println("before-------执行前增强逻辑"); subject.dosth(); System.out.println("after--------执行后增强逻辑"); } }
动态代理又有两种常见的实现:JDK动态代理和CGLIB动态代理。
JDK动态代理中:「代理类」Proxy不再实现Subject接口,而是implements InvocationHandler
,但仍聚合了被代理类(通过构造函数传入被代理类,Object类型)
代理类implements InvocationHandler
,重写invoke方法
java
复制代码
public class TestInterceptor implements InvocationHandler { private Object target;//目标对象的引用,这里设计成Object类型,更具通用性 public TestInterceptor(Object target){ this.target = target; } public Object invoke(Object proxy, Method method, Object[] arg) throws Throwable { System.out.println("before-------执行前逻辑"); Object result = method.invoke(target, arg);//调用目标对象的方法 System.out.println("Before return:"+result); return result; } }
利用Proxy类.newProxyInstance方法
java
复制代码
public class Main { public static void main(String[] args) { RealSubject target = new RealSubject();//生成目标对象 //接下来创建代理对象 Subject proxy = (Subject) Proxy.newProxyInstance( target.getClass().getClassLoader(), target.getClass().getInterfaces(), new TestInterceptor(target)); //调用代理类的方法,有切面逻辑 proxy.dosth(); } }
获取proxy的名称,发现是$Proxy0
通过前文的基本使用,我们了解到了两个关键点:InvocationHandler接口和Proxy代理类。
InvocationHandler只有一个invoke方法
typescript
复制代码
public interface InvocationHandler { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable; }
那么重点就是Proxy.newProxyInstance方法
java
复制代码
public static Object newProxyInstance(ClassLoader loader, Class>[] interfaces, InvocationHandler h) { // 1. 获取到代理类的class对象 Class> cl = getProxyClass0(loader, intfs); // 2. 拿到代理类的构造器,通过反射创建出一个代理类实例 final Constructor> cons = cl.getConstructor(constructorParams); final InvocationHandler ih = h; if (!Modifier.isPublic(cl.getModifiers())) { AccessController.doPrivileged(new PrivilegedAction
那getProxyClass0这个方法是如何拿到代理的class的呢
其实这个方法做了参数的校验,然后直接调用proxyClassCache.get方法
kotlin
复制代码
return proxyClassCache.get(loader, interfaces);
这个Cache也很好理解,我们两次获取代理对象,没必要生成两个class对象,因此要复用代理类的class,我们来关注cache未命中的情况,proxyClass是如何生成的。
这个proxyClassCache是Proxy内部的一个字段如下(注意两个入参):
swift
复制代码
private static final WeakCache
get方法最重要的部分如下:
java
复制代码
Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter)); // 最终这个class对象就是supplier.get方法返回的 // Supplier是一个lambda表达式 Supplier
那么核心就是Factory.get方法了:
ini
复制代码
value = Objects.requireNonNull(valueFactory.apply(key, parameter)); return value;
兜兜转转又回到了proxyClassCache的构造函数的第二个参数了,就是ProxyClassFactory
核心:ProxyClassFactory
代理类名的由来
java
复制代码
// 前缀 private static final String proxyClassNamePrefix = "$Proxy"; // 原子long private static final AtomicLong nextUniqueNumber = new AtomicLong(); // 代理类名 long num = nextUniqueNumber.getAndIncrement(); String proxyName = proxyPkg + proxyClassNamePrefix + num;
最后调用到:
java
复制代码
byte[] proxyClassFile = ProxyGenerator.generateProxyClass( proxyName, interfaces, accessFlags); return defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length);
发现generateProxyClass非常晦涩难懂,并且defineClass0是个native方法,总之是生成了字节码文件并加载了class对象,只能反编译查看代理类的实现。
发现代理类继承了Proxy,被代理的方法以Method的形式存储,最终通过反射的方式来调用。
cglib不再需要被代理类实现一个接口,这是和JDK动态代理与静态代理的不同点,这可以帮助我们远离必须实现接口的困扰。
被代理类:
java
复制代码
public class RealSubject{ public void dosth(){ System.out.println("dosth..."); } }
代理类, 实现 MethodInterceptor
接口
java
复制代码
public class CglibProxy implements MethodInterceptor { @Override public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { System.out.println("xxx"); methodProxy.invokeSuper(object, args); System.out.println("xxx"); return null; } }
利用Enhancer创建代理对象。
java
复制代码
public class CglibTest { public static void main(String[] args) { CglibProxy cglibproxy = new CglibProxy(); Enhancer enhancer = new Enhancer(); // 设置enhancer对象的父类(被代理类) enhancer.setSuperclass(RealSubject.class); // 设置enhancer的回调对象(代理类) enhancer.setCallback(cglibproxy); // 创建代理对象 RealSubject proxy = (RealSubject) enhancer.create(); proxy.dosth(); } }
如果要从ASM开始了解太费精力了,一种做法是直接看cglib生成的所有类反向推。
cglib主要通过ASM直接修改字节码文件,再通过类加载器加载生成一个class对象,就是我们的代理类Proxy,最后依然通过反射拿到Proxy的构造方法并创建一个实例对象并返回。
cglib的代理类Proxy实际上是继承被代理类RealSubject的,并且实现了Factory接口,因此cglib的局限性是:final类是无法被代理的。
cglib实际会生成五个字节码文件,比较重要的有三个,代理类,以及两个FastClass分别对应代理类和被代理类,所以在生成代理对象时会慢一些。cglib调用原始方法是通过FastClass的下标进行调用的。而JDK动态代理是通过反射进行调用的。
建议自己动手反编译代理类看一下,篇幅原因这里就不贴了,实在不想动手也可以参考:分析cglib动态代理的实现
静态代理,是一对一的关系。是确定了「被代理类」,专为此「被代理类」创建了一个代理类。
而动态代理,是多对多关系。解耦了「代理逻辑」与「被代理类」,彼此不相干。静态代理的主体是「被代理类」,而动态代理的主体既是「被代理类」,也是「代理逻辑」。只是我们在真正需要代理的时候,才把他们结合到一起。这里说「代理逻辑」,是因为真正的「代理类」是动态生成的,在此之前并不知道会用什么「代理逻辑」。而静态代理的「代理类」是静态的,代理逻辑是确定的。所以也可以说,静态代理我们是在编写「代理类」,而动态代理我们是在编写「代理逻辑」。
SpringBoot关于Spring AOP的配置类:AopAutoConfiguration,默认使用 Cglib 来实现AOP。
这样设置的原因是:避免在没有实现接口时报错(无法使用JDK动态代理)
可以通过在配置文件中输入如下命令关闭cglib
properties
复制代码
spring.aop.proxy-target-class=false