大家都知道,动态代理常见的两种实现方式,jdk的动态代理和cglib动态代理,而jdk动态代理必须基于接口,cglib没有这样的要求。之前我们讨论过jdk动态代理为什么必须基于接口,传送门:
那么cglib动态代理真的没有任何限制吗?
首先,先来用cglib实现一个动态代理
被代理对象
public class Student {
public void say(){
System.out.println("i'm student");
}
}
代理类
public class MyMethodInterceptor implements MethodInterceptor{
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("before dosomething...");
//注意这里的方法调用,不是用反射哦!!!
Object object = proxy.invokeSuper(obj, args);
System.out.println("after dosomething...");
return object;
}
}
测试类
public static void main(String[] args) {
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "");
Enhancer enhancer = new Enhancer();
//设置目标类的字节码文件
enhancer.setSuperclass(Student.class);
//设置回调函数
enhancer.setCallback(new MyMethodInterceptor());
//这里的create方法就是正式创建代理类
Student proxy = (Student)enhancer.create();
proxy.say();
}
运行结果
before dosomething...
i'm student
after dosomething...
发现动态代理生效了。
动态代理获取原类(被代理类),通常需要通过继承和接口实现来进行获取,如下
Student proxy = (Student)enhancer.create();
因此我们猜测,cglib代理应该是通过继承来实现代理的。
先做个实验,由于java当中有fianl关键字,可以修饰类,表示该类不可被继承,所以将被代理类修改如下:
public final class Student {
public void say(){
System.out.println("i'm student");
}
}
再执行之前的测试类,执行结果如下,可以看出确实出错了,并且提示不能继承fianl类,说明我们分析正确。
Exception in thread "main" java.lang.IllegalArgumentException: Cannot subclass final class com.example.demo.cglib.Student
at net.sf.cglib.proxy.Enhancer.generateClass(Enhancer.java:565)
at net.sf.cglib.core.DefaultGeneratorStrategy.generate(DefaultGeneratorStrategy.java:25)
at net.sf.cglib.core.AbstractClassGenerator.generate(AbstractClassGenerator.java:332)
at net.sf.cglib.proxy.Enhancer.generate(Enhancer.java:492)
at net.sf.cglib.core.AbstractClassGenerator$ClassLoaderData$3.apply(AbstractClassGenerator.java:96)
at net.sf.cglib.core.AbstractClassGenerator$ClassLoaderData$3.apply(AbstractClassGenerator.java:94)
at net.sf.cglib.core.internal.LoadingCache$2.call(LoadingCache.java:54)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at net.sf.cglib.core.internal.LoadingCache.createEntry(LoadingCache.java:61)
at net.sf.cglib.core.internal.LoadingCache.get(LoadingCache.java:34)
at net.sf.cglib.core.AbstractClassGenerator$ClassLoaderData.get(AbstractClassGenerator.java:119)
at net.sf.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:294)
at net.sf.cglib.proxy.Enhancer.createHelper(Enhancer.java:480)
at net.sf.cglib.proxy.Enhancer.create(Enhancer.java:305)
at com.example.demo.cglib.Test.main(Test.java:16)
下面我们再分析下cglib的源码,证明上面的过程。
首先从enhancer.create()
方法开始,只保留关键方法
public class Enhancer extends AbstractClassGenerator {
……
public Object create() {
……
return this.createHelper();
}
private Object createHelper() {
this.preValidate();
……
Object result = super.create(key);
return result;
}
}
接着看super.create(key)
方法
public abstract class AbstractClassGenerator<T> implements ClassGenerator {
protected Object create(Object key) {
……
// 获取代理对象
Object obj = data.get(this, this.getUseCache());
return obj instanceof Class ? this.firstInstance((Class)obj) : this.nextInstance(obj);
}
}
public Object get(AbstractClassGenerator gen, boolean useCache) {
if (!useCache) {
return gen.generate(this);
} else {
Object cachedValue = this.generatedClasses.get(gen);
return gen.unwrapCachedValue(cachedValue);
}
}
protected Class generate(AbstractClassGenerator.ClassLoaderData data) {
……
// 生成代理类的字节
byte[] b = this.strategy.generate(this);
……
}
最终调用到下面方法
public byte[] generate(ClassGenerator cg) throws Exception {
DebuggingClassWriter cw = this.getClassVisitor();
this.transform(cg).generateClass(cw);
// 生成字节
return this.transform(cw.toByteArray());
}
此时再看cw.toByteArray()
方法,可以看到DebuggingClassWriter.debugLocation可以配置代理类生成到指定路径。
public byte[] toByteArray() {
return (byte[])((byte[])AccessController.doPrivileged(new PrivilegedAction() {
public Object run() {
byte[] b = ((ClassWriter)DebuggingClassWriter.access$001(DebuggingClassWriter.this)).toByteArray();
// 如果为true,则可以输出代理类到 DebuggingClassWriter.debugLocation 位置
if (DebuggingClassWriter.debugLocation != null) {
String dirs = DebuggingClassWriter.this.className.replace('.', File.separatorChar);
try {
(new File(DebuggingClassWriter.debugLocation + File.separatorChar + dirs)).getParentFile().mkdirs();
File file = new File(new File(DebuggingClassWriter.debugLocation), dirs + ".class");
BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(file));
try {
out.write(b);
} finally {
out.close();
}
if (DebuggingClassWriter.traceCtor != null) {
file = new File(new File(DebuggingClassWriter.debugLocation), dirs + ".asm");
out = new BufferedOutputStream(new FileOutputStream(file));
try {
ClassReader cr = new ClassReader(b);
PrintWriter pw = new PrintWriter(new OutputStreamWriter(out));
ClassVisitor tcv = (ClassVisitor)DebuggingClassWriter.traceCtor.newInstance(null, pw);
cr.accept(tcv, 0);
pw.flush();
} finally {
out.close();
}
}
} catch (Exception var17) {
throw new CodeGenerationException(var17);
}
}
return b;
}
}));
}
那么,我们将原来的测试类,修改如下:
public static void main(String[] args) {
//在指定目录下生成动态代理类,我们可以反编译看一下里面到底是一些什么东西
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "/Users/ganhuojun/code/my_demo/target");
Enhancer enhancer = new Enhancer();
//设置目标类的字节码文件
enhancer.setSuperclass(Student.class);
//设置回调函数
enhancer.setCallback(new MyMethodInterceptor());
//这里的create方法就是正式创建代理类
Student proxy = (Student)enhancer.create();
proxy.say();
}
执行结果如下,并查看相应目录
CGLIB debugging enabled, writing to '/Users/ganhuojun/code/my_demo/target'
before dosomething...
i'm student
after dosomething...
打开代理类Student$$EnhancerByCGLIB$$8b03b040.class
,可以看到确实继承了原类,因此cglib实现动态代理,要求类必须不能被fianl修饰
public class Student$$EnhancerByCGLIB$$8b03b040 extends Student implements Factory {
//内容省略
……
}
结论:
cglib实现动态代理,要求类必须不能被fianl修饰。扩展一下,如果方法被final修饰,能否被代理执行呢?