分享人: 元哥
1、代理模式
设计模式中的结构型设计模式
从字面意思理解,代理即A替B完成某件事。例如媒婆帮靓仔找一个广州的女朋友,那么媒婆就是代理方,代理的事情就是帮忙介绍男女朋友;靓仔就是委托方,委托的事情就是找一个广州的女朋友。
静态代理类图
Code实现
代理类和委托类实现了相同的接口,代理类和委托类实现了相同的方法。如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。
举个例子。上述的需求是靓仔要找个广州的女朋友,那现在假如靓仔还有个特殊的需求,要找个广州的男朋友,委托类Handsome和代理类Proxy都需要实现findBoyFriend()方法。
静态代理类只能为特定的接口服务。如想要为多个接口服务则需要建立很多个代理类。
如上的代码是只为 HandsomeMan 类的访问提供了代理,但是如果还要为其他类如 PrettyGril 类提供代理的话,就需要我们再次添加代理 PrettyGril 的代理类。
在举个开发中的例子:在调用具体实现类之前,需要打印日志等信息,这样我们只需要添加一个代理类,在代理类中添加打印日志的功能,然后调用实现类,这样就避免了修改具体实现类。满足我们所说的开闭原则。但是如果想让每个实现类都添加打印日志的功能的话,就需要添加多个代理类,以及代理类中各个方法都需要添加打印日志功能。
当有新使用动态代理
1)使用静态代理,当HandsomeMan类中有新的方法例如findBoyFriend()需要代理,相应findBoyFriend()方法需要在MiddleProxy类中进行实现;而使用动态代理,在MiddleProxy中不需要实现
2)

我们来对比静态代理和动态代理。在静态代理中target对象一个是MiddleProxy类的对象,而在动态代理中target对象是$Proxy0类的对象。
我们知道静态代理的MiddleProxy类是怎么做的,因为是我们声明的;但$Proxy0类原先是不存在,但是他是在调用newProxyInstance方法后生成,而方法中实现的是通过反射类Proxy.newProxyInstance。
public Object newProxyInstance(Object target) {
this.target = target;
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
复制代码 我们来通过特殊的方式,将$Proxy0类的字节码也就是class输出到文件,然后经过idea反编译得到以下
public final class $Proxy0 extends Proxy implements Target {
private static Method m1;
private static Method m2;
private static Method m3;
private static Method m0;
public $Proxy0(InvocationHandler var1) throws {...}
public final boolean equals(Object var1) throws {...}
public final String toString() throws {...}
public final void findGrilFriend() throws {
try {
super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final int hashCode() throws {...}
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.dynamic_proxy.Target").getMethod("findGrilFriend");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
复制代码
代码有点儿多我们不要慌。首先我们看到$Proxy0类继承Proxy,并实现Target接口,那么它肯定实现了findGrilFriend方法。
那么我们来大胆猜想,$Proxy0类是通过反射类Proxy.newProxyInstance()生成的,方法接收的参数就是Target接口对象,那么如果方法接收的参数是其他接口对象,例如BTarget接口对象,里面实现的方法findBoyFriend,那么$Proxy0类就会去实现BTarget接口的方法。因此可以认为,动态代理是静态代理的升级版,在程序运行过程中会动态根据传入的接口对象,动态生成指定接口对象的代理类$Proxy0的对象。
接下来,我们继续看findGrilFriend方法,调用的是 super.h.invoke(this, m3, (Object[])null); super.h 是 Proxy类的成员属性(InvocationHandler),在Proxy.newProxyInstance()生成$Proxy0会传this对象。
public $Proxy0(InvocationHandler var1) throws {
super(var1);
}
复制代码 public class MiddleProxy implements InvocationHandler {
public Object newProxyInstance(Object target) {
this.target = target;
return Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
this);
}
....
}
复制代码 m3是声明的findGrilFriend()方法
总结一下过程:
类图
代理类
动态生成具体的代理类code
public class HandsomeMan$$EnhancerByCGLIB$$596495c6 extends HandsomeMan implements Factory {
...省略很多代码
final void CGLIB$findGrilFriend$0() {
super.findGrilFriend();
}
public final void findGrilFriend() {
MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
if (var10000 == null) {
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}
if (var10000 != null) {
var10000.intercept(this, CGLIB$findGrilFriend$0$Method, CGLIB$emptyArgs, CGLIB$findGrilFriend$0$Proxy);
} else {
super.findGrilFriend();
}
}
}
复制代码
总结一下流程;
1)首先我们通过newProxyInstance动态生成代理类HandsomeMan$$EnhancerByCGLIB$$和对象
public class MiddleProxy implements MethodInterceptor {
public Object newProxyInstance(Object target){
//工具类
Enhancer enhancer=new Enhancer();
//设置被代理的对象,也可以理解为设置父类,因为动态代理类是继承了被动态代理类。
enhancer.setSuperclass(target.getClass());
//设置回调函数
enhancer.setCallback(this);
//创建子类的动态代理类对象
return enhancer.create();
}
....
}
复制代码 复制代码
2)在子类HandsomeMan$$EnhancerByCGLIB$$对象调用findGirlFriend方法时,会调用MiddleProxy类的intercept方法
3)在MiddleProxy类的intercept方法中,会在调用父类方法前后进行增强
public class MiddleProxy implements MethodInterceptor {
....
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("这里有很多单身的人.深圳、广州、东莞你找一个喜欢的。");
Object obj = methodProxy.invokeSuper(o, objects);
System.out.println("给你喜欢的这个人的联系方式。");
return obj;
}
}
复制代码 由于 JDK 动态代理限制了只能基于接口设计,而对于没有接口的情况,JDK 方式解决不了;
CGLib 采用了非常底层的字节码技术,其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑,来完成动态代理的实现。
但是 CGLib 在创建代理对象时所花费的时间却比 JDK 多得多,所以对于单例的对象,因为无需频繁创建对象,用 CGLib 合适,反之,使用 JDK 方式要更为合适一些。 同时,由于 CGLib 由于是采用动态创建子类的方法,对于 final 方法,无法进行代理。
优点:没有接口也能实现动态代理,而且采用字节码增强技术,性能也不错。 缺点:技术实现相对难理解些。
注意:对于从Object中继承的方法,CGLIB代理也会进行代理,如 hashCode() 、 equals() 、 toString() 等,但是 getClass() 、 wait() 等方法不会,因为它是final方法,CGLIB无法代理。