设计模式 - 代理模式
代理模式:为其他对象提供一种代理以控制对这个对象的访问 - 访问者通过代理对象间接访问被代理对象,使代理对象对被代理对象实现控制
实现方式:(组合)控制类(Proxy) + 执行逻辑接口(ISubject),由proxy封装了对subject的访问,在访问subject方法前后可以织入其他逻辑(功能附着)
核心:功能附着,Subject对象复杂本职任务,额外的逻辑交由代理对象,使subject职责相关的逻辑和其他逻辑解耦 - 本质就是:拦截
静态代理
// 代理模式 - (控制类 + 执行逻辑接口的组合)
// 在subject方法的前后织入额外的与subject职责无关的逻辑,使subject的业务逻辑和其他逻辑分离。
public class Proxy implements ISubject{
// 静态代理:硬编码,手动注入,面向单一接口
// 动态代理:更强的扩展性,自动生成新的代理类,和被代理类属于用一个继承体系。
private ISubject subject = null;
public Proxy(ISubject subject){
this.subject = subject;
}
@Override
public void work() {
long startTime = System.currentTimeMillis();
this.subject.work();
System.out.println("接口耗时:" + (System.currentTimeMillis() - startTime));
}
}
动态代理
JDK Proxy
- Subject,作为被代理类,负责主要的业务逻辑
- InvocationHandler,作为主要的控制类,负责织入额外的逻辑以及Subject对象的控制
- Proxy类,在程序运行过程中,动态创建代理对象
- ISubject接口的子类:动态生成的代理对象,其方法职责是:当被调用时,获取该方法对应的method对象和传入参数params,转交给InvocationHandler对象,由InvocationHandler通过反射调用真实的subject中的方法
JDK Proxy底层的实现原理:程序运行过程中,手写class字节码文件,并由类加载器进行加载和初始化。
// JDK Proxy,代理对象和被代理对象需实现同一个接口,通过接口的方法列表生成Method对象实现代理类到主题类方法之间的关联。
// JDK Proxy,采用反射实现subject方法的调用
public class Test {
public static void main(String[] args) {
// 主题类
ISubject subject = new Subject();
// 附加逻辑类(用于添加附加逻辑相关的代码)
InvocationHandler handler = new MyInvocationHandler(subject);
// 代理类(实现了主题类的接口,接口中方法的实现:获取此方法相关的信息和参数,传递给handler.invoke()方法
ISubject proxy = (ISubject)Proxy.newProxyInstance(clazz.getClassLoader(),clazz.getInterfaces(),handler);
// 1. 在work方法中获取到被调用方法的信息(Method对象和params),传递给hanlder
// 2. handler 使用反射 Method.invoke(target,params),实现方法调用
proxy.work();
}
}
// InvocationHandler - 提供一个invoke方法,在invoke方法中完成额外逻辑的织入
public class MyInvocationHandler implements InvocationHandler {
private Object subject = null;
// 方案1 - 单一职责(由高层模块完成代理对象的创建 - Proxy.newProxyInstance())
public MyInvocationHandler (Object subject){
this.subject = subject;
}
// 方案2 - 将创建代理对象的职责与invoke的职责合并,高层只关心获取到的proxy对象。
public MyInvocationHandler (){};
public Object getProxy(Object subject){
this.subject = subject;
Class<?> clazz = subject.getClass();
return Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(),this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("before!"); // 此为织入前置逻辑
Object result = method.invoke(this.subject,args); // 反射
System.out.println("after!"); // 此处织入后置逻辑
return result;
}
}
// $Proxy JDK实际在内存中生成的$Proxy0对象字节码反编译。
// 代理对象作用:收集调用对象和传入参数,并传递给invocationHandler对象。
public final class $Proxy0 extends Proxy implements ISubject {
private static Method m1;
private static Method m3;
private static Method m2;
private static Method m0;
public $Proxy0(InvocationHandler var1) throws {
super(var1);
}
public final boolean equals(Object var1) throws {
try {
// 参数2:该方法的method对象 参数3:该方法的调用参数 --- 只需method、被调用方法的实例对象和参数,即可调用实例的方法
return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final void work() throws {
try {
super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final String toString() throws {
try {
return (String)super.h.invoke(this, m2, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final int hashCode() throws {
try {
return (Integer)super.h.invoke(this, m0, (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"));
m3 = Class.forName("top.kiqi.design.pattern.proxy.dynamic_proxy.jdk_proxy.ISubject").getMethod("work");
m2 = Class.forName("java.lang.Object").getMethod("toString");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
CGLib proxy
- Subject,作为被代理类,负责主要的业务逻辑
- MethodInterceptor,拦截器,负责织入额外的逻辑,然后通过回调MethodProxy.invokeSuper()完成被代理类的控制
- Enhancer:在程序运行过程中,动态创建代理对象(ASM字节码框架)
- Subject的实现子类:完成子类方法信息的采集,并调用MethodInterceptor.intercep()方法(参数:Method,args,MethodProxy)
- MethodProxy:提供给MethodInterceptor的回调对象,会根据方法的信息查找并调用subject方法(非反射,是根据方法信息,通过if else执行的判断) --- 与jdk proxy的主要区别
CGLib Proxy底层的实现原理:程序运行过程中,通过ASM框架生成class字节码文件,并由类加载器进行加载和初始化。
// cglib 通过继承被代理类的方式实现代理。
public class Test {
public static void main(String[] args) {
Subject subject = (Subject) new CGlibInterceptor().getProxy(Subject.class);
subject.work();
}
}
public class CGlibInterceptor implements MethodInterceptor {
// cglib创建逻辑,通过生成传入clazz的子类的字节码,实现proxy
public Object getProxy(Class<?> clazz){
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(clazz);
enhancer.setCallback(this);
return enhancer.create();
}
// intercept方法,加入前后的织入逻辑,然后回调methodProxy.invokeSuper()方法,invokeSupper()方法中会根据方法信息通过if else判断找到并执行subject对应的方法。
// MethodProxy methodProxy: CGLIB$work$0$Proxy = MethodProxy.create(var1, var0, "()V", "work", "CGLIB$work$0");
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("before!");
Object result = methodProxy.invokeSuper(o, objects);
System.out.println("after!");
return result;
}
}
对比 - JDK proxy 和 CGLib proxy
- JDK proxy通过实现同一个接口完成代理,而CGLib proxy通过继承被代理类完成代理
- JDK proxy对subject的调用是通过反射实现的,而CGLib proxy中根据方法信息通过if else匹配完成subject对应方法的查找和调用
- JDK proxy: 代理类结构简单,创建效率更高
- CGLib proxy: 代理类复杂,执行效率更高
- JDK proxy是硬手写字节码实现的,CGLib proxy借用ASM框架完成代理类字节码的生成
PS:CGLib proxy通过继承父类实现,因此无法代理到final方法。
欢迎疑问、期待评论、感谢指点 -- kiqi,愿同您为友
-- 星河有灿灿,愿与之辉