Java设计模式之代理模式(二)
一、CGLIB动态代理
JDK动态代理要求被代理的类必须实现接口,有很强的局限性,而CGLIB动态代理则不要求被代理类实现接口。简单的说,CGLIB会让生成的代理类继承被代理类,并在代理类中对代理方法进行强化处理(前置处理、后置处理等)。在CGLIB底层,其实是借助了ASM这个非常强大的Java字节码生成框架。Cglib无法代理被final修饰的方法。
cglib的原理是继承,cglib通过继承目标类,创建它的子类,在子类中重写父类中同名的方法,实现功能的修改。因为cglib是继承,重写方法,所以要求目标类不能是final的,方法也不能是final的。cglib的要求目标类比较宽松,只要能继承就可以了。cglib在很多的框架中使用,比如mybatis,spring框架中都有使用。
1、cglib动态代理的简单使用
使用CGLIB动态代理主要有以下几个步骤:
1.编写一个被代理类(也称委托类或目标类),无需实现任何接口;
2.自定义一个方法拦截器,实现MethodInterceptor接口,并重写intercept方法;
3.在intercept方法中调用MethodProxy类的invokeSuper方法(而不是调用invoke方法,因为invoke会引起死循环,导致堆栈内存溢出,具体原因在下面会详细分析。在invokeSuper方法中,底层实际上最终会调用被代理类中相应的被代理方法);
4.创建代理对象,并调用被代理类中的方法(实际上是调用代理对象中重写的代理方法)。
示例代码:
package com.tx.study.others.proxy.cglibProxy;
/**
* @Author: 倚天照海
* @Description: 被代理类
*/
public class InfoDemo {
//被代理方法(被final修饰的方法无法被代理)
public void welcome (String person){
System.out.println("welcome :" + person);
}
}
package com.tx.study.others.proxy.cglibProxy;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* @Author: 倚天照海
* @Description: 自定义方法拦截器,实现MethodInterceptor接口,并重写intercept方法
*/
public class MyMethodInterceptor implements MethodInterceptor {
/**
* 重写intercept方法
*
* @param o 代理对象
* @param method 被代理方法对应的Method对象
* @param objects 被代理方法的参数
* @param methodProxy 方法代理对象
* @return 被代理方法的返回值
* @throws Throwable 可能会抛出的异常
*/
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
//在真实的对象执行之前可以添加自己的操作,插入前置通知
System.out.println("before method!!!");
//在invokeSuper方法中最终会调用被代理类中对应的被代理方法
Object value = methodProxy.invokeSuper(o, objects);
//会引起死循环,导致内存溢出
//Object value = methodProxy.invoke(o, objects);
//在真实的对象执行之后可以添加自己的操作,插入后置通知
System.out.println("after method!!!");
return value;
}
}
package com.tx.study.others.proxy.cglibProxy;
import org.springframework.cglib.core.DebuggingClassWriter;
import org.springframework.cglib.proxy.Enhancer;
/**
* @Author: 倚天照海
* @Description: cglib动态代理测试类
*/
public class CglibProxyTest {
/**
* 该方法的作用就是封装获取代理对象的代码,即获取代理对象
* @return 代理对象
*/
public static Object getProxyInstance() {
//Enhancer是CGLIB的字节码增强器,可以很方便的对类进行拓展
Enhancer enhancer = new Enhancer();
//为代理类设置父类,即代理类继承被代理类
enhancer.setSuperclass(InfoDemo.class);
//为代理类设置回调对象,即自定义的方法拦截器
enhancer.setCallback(new MyMethodInterceptor());
//创建代理对象
return enhancer.create();
}
public static void main(String[] args) {
//将代理类class文件存入本地磁盘,方便反编译查看源码
String path = "D:\\ProgramFiles\\workspace\\zznode\\data-query\\tx-study\\src\\main\\java\\com\\tx\\study\\others\\proxy\\cglibProxy\\";
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, path);
//获取代理对象
InfoDemo instance = (InfoDemo) getProxyInstance();
//调用被代理方法(底层会调用自定义方法拦截器中重写的intercept方法)
instance.welcome("zhangsan");
}
}
运行结果:
before method!!!
welcome :zhangsan
after method!!!
2、cglib动态代理的原理
原理分析:
执行测试类,会生成三个class文件,如下所示,通过反编译可以看到这三个文件中的源码。
InfoDemo$$EnhancerByCGLIB$$8b8da05b.class (cglib生成的代理类,继承InfoDemo类)
InfoDemo$$FastClassByCGLIB$$f4c7f3ac.class (被代理类的FastClass,记为f1)
InfoDemo$$EnhancerByCGLIB$$8b8da05b$$FastClassByCGLIB$$9065a6c.class (代理类的FastClass,记为f2)(在下面会详细说明这三个类的部分源码)
在MethodProxy类中有一个静态内部类FastClassInfo,如下所示。FastClassInfo中有四个属性,FastClass f1表示被代理类的FastClass,FastClass f2表示代理类的FastClass,int i1表示被代理类的方法签名(实际上是方法对应的索引,根据该索引可以快速找到对应的方法,这就是cglib动态代理比JDK动态代理效率高的主要原因),int i2表示代理类的方法签名。通过FastClassInfo对象可以得到被代理类和代理类的FastClass,在下面会详细介绍。
private static class FastClassInfo {
FastClass f1;//被代理类的FastClass
FastClass f2;//代理类的FastClass
int i1; //被代理类的方法签名(index)
int i2;//代理类的方法签名
private FastClassInfo() {
}
}
先看一下本例中生成的代理类(InfoDemo$$EnhancerByCGLIB$$8b8da05b.class)的源码。
代理类InfoDemo$$EnhancerByCGLIB$$8b8da05b.class:
public class InfoDemo$$EnhancerByCGLIB$$8b8da05b extends InfoDemo implements Factory {
private MethodInterceptor CGLIB$CALLBACK_0; //拦截器
private static final Method CGLIB$welcome$0$Method; //被代理方法(是Method对象)
private static final MethodProxy CGLIB$welcome$0$Proxy; //方法代理(MethodProxy对象)
//..........................................省略
static void CGLIB$STATICHOOK1() {
CGLIB$THREAD_CALLBACKS = new ThreadLocal();
CGLIB$emptyArgs = new Object[0];
Class var0 = Class.forName("CglibTest.InfoDemo$$EnhancerByCGLIB$$8b8da05b");
Class var1;
//通过反射获取被代理方法
CGLIB$welcome$0$Method = ReflectUtils.findMethods(new String[]{"welcome", "(Ljava/lang/String;)V"}, (var1 = Class.forName("CglibTest.InfoDemo")).getDeclaredMethods())[0];
//生成与被代理方法对应的方法代理对象
CGLIB$welcome$0$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/String;)V", "welcome", "CGLIB$welcome$0");
//..........................................省略
}
//cglib会生成与被代理方法对应的代理方法CGLIB$welcome$0
final void CGLIB$welcome$0(String var1) {
//直接调用父类的被代理方法(实际上被代理方法就是在此处被调用的)
super.welcome(var1);
}
//由于代理类继承了被代理类,所以在代理类中生成了被代理方法(进行了重写)
public final void welcome(String var1) {
MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
if(this.CGLIB$CALLBACK_0 == null) {
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}
//判断代理类中是否设置了方法拦截,如果设置了就调用该拦截器的intercept方法
//在本例中设置了拦截器enhancer.setCallback(new MyMethodInterceptor());
if(var10000 != null) {
var10000.intercept(this, CGLIB$welcome$0$Method, new Object[]{var1}, CGLIB$welcome$0$Proxy);
} else {
//如果没有设置拦截器,就直接调用父类的被代理方法
super.welcome(var1);
}
}
//..........................................省略
}
在代理类中会生成与父类(即被代理类)中每一个方法相对应的两个方法,例如此例中父类的welcome方法,在代理类中生成了CGLIB$welcome$0代理方法和重写的welcome方法。另外,父类中的每一个方法都会在静态块中,通过MethodProxy.create生成对应的方法代理。
在本例测试类中,代理对象调用父类的被代理方法,即instance.welcome("zhangsan"),实际上调用的是代理类中被重写后的welcome方法,即public final void welcome(String var1)。在该方法中调用了拦截器MethodInterceptor的intercept方法。由于自定义的方法拦截器实现了MethodInterceptor接口,并对intercept方法进行了重写,所以,实际上调用的是重写后的intercept方法。在重写的intercept方法中调用了MethodProxy对象的invokeSuper(o, objects)方法,接下来重点看一下invokeSuper方法的执行过程,即分析MethodProxy的源码。
先看一下代理类中调用的intercept(this, CGLIB$welcome$0$Method, new Object[]{var1}, CGLIB$welcome$0$Proxy)方法,该方法中有四个参数,第一个参数是this,即调用代理类中重写后的welcome方法的对象,当然是代理对象了。第二个参数是被代理方法(实际上是Method对象),第三个参数是被代理方法的参数数组,第四个参数是方法代理对象(即MethodProxy对象)。这四个参数也就是自定义方法拦截器的intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy)方法中的四个参数。所以,methodProxy.invokeSuper(o, objects)方法中的methodProxy就是方法代理对象,o就是代理对象,objects就是被代理方法的参数。
MethodProxy 源码分析:
public class MethodProxy {
//下面的前三个变量在create方法中,都已经得到了初始值了。
private Signature sig1;
private Signature sig2;
private MethodProxy.CreateInfo createInfo;
//在调用invoke或者invokeSuper中,通过init()方法生成FastClassInfo
private volatile MethodProxy.FastClassInfo fastClassInfo;
public Object invokeSuper(Object obj, Object[] args) throws Throwable {
try {
this.init();
MethodProxy.FastClassInfo fci = this.fastClassInfo;
//调用代理类的FastClass的invoke方法
return fci.f2.invoke(fci.i2, obj, args);
} catch (InvocationTargetException var4) {
throw var4.getTargetException();
}
}
public Object invoke(Object obj, Object[] args) throws Throwable {
try {
this.init();
MethodProxy.FastClassInfo fci = this.fastClassInfo;
//调用被代理类的FastClass的invoke方法
return fci.f1.invoke(fci.i1, obj, args);
} catch (InvocationTargetException var4) {
throw var4.getTargetException();
} catch (IllegalArgumentException var5) {
if(this.fastClassInfo.i1 < 0) {
throw new IllegalArgumentException("Protected method: " + this.sig1);
} else {
throw var5;
}
}
}
//init方法就是为了生成FastClassInfo,FastClassInfo中存放着两个fastclass(f1、f2)和两个方法索引的值(i1、i2)。
private void init() {
if(this.fastClassInfo == null) {
Object var1 = this.initLock;
synchronized(this.initLock) {
if(this.fastClassInfo == null) {
MethodProxy.CreateInfo ci = this.createInfo;
MethodProxy.FastClassInfo fci = new MethodProxy.FastClassInfo();
//不是每一个方法都生成一个fastclass,每一个方法的fastclass都是一样的,
//只不过他们的i1,i2不一样。如果缓存中有就取出,没有就生成新的FastClass
fci.f1 = helper(ci, ci.c1);
fci.f2 = helper(ci, ci.c2);
fci.i1 = fci.f1.getIndex(this.sig1);
fci.i2 = fci.f2.getIndex(this.sig2);
this.fastClassInfo = fci;
this.createInfo = null;
}
}
}
}
//根据一个类的信息,返回该对象的一个Fastclass
private static FastClass helper(MethodProxy.CreateInfo ci, Class type) {
Generator g = new Generator();
g.setType(type);
g.setClassLoader(ci.c2.getClassLoader());
g.setNamingPolicy(ci.namingPolicy);
g.setStrategy(ci.strategy);
g.setAttemptLoad(ci.attemptLoad);
return g.create();
}
//FastClassInfo是MethodProxy的静态内部类
private static class FastClassInfo {
FastClass f1;//被代理类的FastClass
FastClass f2;//代理类的FastClass
int i1; //被代理类的方法签名(index)
int i2;//代理类的方法签名
private FastClassInfo() {
}
}
}
在MethodProxy类的invokeSuper方法中调用了代理类的FastClass的invoke方法,接下来看一下代理类的FastClass的源码。代理类的FastClass的字节码反编译后的源码如下所示。
代理类的FastClass:
代理类的FastClass(InfoDemo$$EnhancerByCGLIB$$8b8da05b$$FastClassByCGLIB$$9065a6c)的字节码反编译后的源码:
public class InfoDemo$$EnhancerByCGLIB$$8b8da05b$$FastClassByCGLIB$$9065a6c extends FastClass {
public InfoDemo$$EnhancerByCGLIB$$8b8da05b$$FastClassByCGLIB$$9065a6c(Class var1) {
super(var1);
}
public int getIndex(Signature var1) {
String var10000 = var1.toString();
switch(var10000.hashCode()) {
case -2055565910:
if(var10000.equals("CGLIB$SET_THREAD_CALLBACKS([Lnet/sf/cglib/proxy/Callback;)V")) {
return 12;
}
break;
case -1725733088:
if(var10000.equals("getClass()Ljava/lang/Class;")) {
return 24;
}
case 1013143764:
if(var10000.equals("CGLIB$welcome$0(Ljava/lang/String;)V")) {
return 17;
}
}
//----省略
}
//在MethodProxy类的invokeSuper方法中调用了此处的invoke方法
public Object invoke(int var1, Object var2, Object[] var3) throws InvocationTargetException {
8b8da05b var10000 = (8b8da05b)var2;
int var10001 = var1;
try {
switch(var10001) {
case 0:
return var10000.toString();
case 1:
return new Integer(var10000.hashCode());
case 17:
//调用代理对象的代理方法CGLIB$welcome$0
var10000.CGLIB$welcome$0((String)var3[0]);
}
//----省略
}
}
}
由上述代码可知,在代理类的FastClass的invoke方法中调用了代理对象的CGLIB$welcome$0方法,由上面分析的代理类的源码可知,在代理类的CGLIB$welcome$0方法中直接调用父类的被代理方法,即调用此例中InfoDemo类的welcome方法,执行被代理方法。至此,cglib动态代理的大致过程就分析完毕了。
如果在自定义方法拦截器中调用的不是methodProxy.invokeSuper(o, objects)方法,而是methodProxy.invoke(o, objects)方法,为什么会引起死循环,导致内存溢出呢?由MethodProxy的源码可知,在MethodProxy的invoke方法中调用了被代理类的FastClass的invoke方法,所以,接下来看一下被代理类的FastClass(InfoDemo$$FastClassByCGLIB$$f4c7f3ac.class)被反编译后的部分源码。
public class InfoDemo$$FastClassByCGLIB$$f4c7f3ac extends FastClass {
public Object invoke(Object obj, Object[] args) throws Throwable {
try {
switch(var10001) {
case 0:
//调用被代理对象的被代理方法welcome
var10000.welcome((String)var3[0]);
return null;
case 1:
}
}
}
}
在被代理类的FastClass的invoke方法中调用被代理对象的welcome方法,通过代理对象调用被代理方法,与在main函数中instance.welcome(“zhangsan”)是一样的步骤,即又从头开始循环调用,直到栈内存溢出。