上一篇我们对Cglib动态代理进行了代码测试,这一篇文章我们看下Cglib实现动态代理的原理,其实分析动态代理弄懂两件事就行:代理类怎么生成的、代理类方法如何调用的被代理类方法。
1 代理类的生成
首先代理类对象是调用enhancer.create()
生成的,其中会调用super.create()
。
public class Enhancer extends AbstractClassGenerator {
public Object create(Class[] argumentTypes, Object[] arguments) {
this.classOnly = false;
if (argumentTypes != null && arguments != null && argumentTypes.length == arguments.length) {
this.argumentTypes = argumentTypes;
this.arguments = arguments;
return this.createHelper();
} else {
throw new IllegalArgumentException("Arguments must be non-null and of equal length");
}
}
private Object createHelper() {
this.preValidate();
Object key = KEY_FACTORY.newInstance(this.superclass != null ? this.superclass.getName() : null, ReflectUtils.getNames(this.interfaces), this.filter == ALL_ZERO ? null : new WeakCacheKey(this.filter), this.callbackTypes, this.useFactory, this.interceptDuringConstruction, this.serialVersionUID);
this.currentKey = key;
Object result = super.create(key);
return result;
}
}
进入到父类AbstractClassGenerator
的create()
方法,其中做的事情为:通过cglib的生成策略生成代理类的字节码文件,然后加载到jvm,再通过反射创建代理类对象。
public abstract class AbstractClassGenerator<T> implements ClassGenerator {
protected Object create(Object key) {
try {
ClassLoader loader = this.getClassLoader();
Map<ClassLoader, AbstractClassGenerator.ClassLoaderData> cache = CACHE;
AbstractClassGenerator.ClassLoaderData data = (AbstractClassGenerator.ClassLoaderData)cache.get(loader);
if (data == null) {
Class var5 = AbstractClassGenerator.class;
synchronized(AbstractClassGenerator.class) {
cache = CACHE;
data = (AbstractClassGenerator.ClassLoaderData)cache.get(loader);
if (data == null) {
Map<ClassLoader, AbstractClassGenerator.ClassLoaderData> newCache = new WeakHashMap(cache);
data = new AbstractClassGenerator.ClassLoaderData(loader);
newCache.put(loader, data);
CACHE = newCache;
}
}
}
this.key = key;
//1.获取代理类对象
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) {
//2.生成代理类对象
return gen.generate(this);
} else {
Object cachedValue = this.generatedClasses.get(gen);
return gen.unwrapCachedValue(cachedValue);
}
}
protected Class generate(AbstractClassGenerator.ClassLoaderData data) {
Object save = CURRENT.get();
CURRENT.set(this);
try {
ClassLoader classLoader = data.getClassLoader();
if (classLoader == null) {
throw new IllegalStateException("ClassLoader is null while trying to define class " + this.getClassName() + ". It seems that the loader has been expired from a weak reference somehow. Please file an issue at cglib's issue tracker.");
} else {
String className;
synchronized(classLoader) {
className = this.generateClassName(data.getUniqueNamePredicate());
data.reserveName(className);
this.setClassName(className);
}
Class gen;
if (this.attemptLoad) {
try {
gen = classLoader.loadClass(this.getClassName());
Class var25 = gen;
return var25;
} catch (ClassNotFoundException var20) {
}
}
//3.生成代理类字节码
byte[] b = this.strategy.generate(this);
className = ClassNameReader.getClassName(new ClassReader(b));
ProtectionDomain protectionDomain = this.getProtectionDomain();
synchronized(classLoader) {
if (protectionDomain == null) {
//4.根据字节码生成代理类对象
gen = ReflectUtils.defineClass(className, b, classLoader);
} else {
gen = ReflectUtils.defineClass(className, b, classLoader, protectionDomain);
}
}
Class var8 = gen;
return var8;
}
}...
}
}
上一篇做代码测试时我们保存了代理类相关的字节码文件,会看到如下3个class文件,其中第2个是代理类class,其余两个是代理类的fastclass和被代理类fastclass,这两个稍后再说。
我们先粗略看一下代理类class中的内容,代理类run()
方法中,会调用方法拦截器的intercept()
方法,方法拦截器会通过某些手段找到代理类的CGLIB$run$0()
方法,最后实现调用父类即被代理类Person
的run()
方法,这个手段在Jdk中是反射,而在Cglib中是一种新的机制-FastClass机制。
public class Person$$EnhancerByCGLIB$$c7ae5d4e extends Person implements Factory {
private static final Method CGLIB$run$0$Method;
private static final MethodProxy CGLIB$run$0$Proxy;
static void CGLIB$STATICHOOK1() {
Class var0 = Class.forName("com.codezhao.designpattern.proxypattern.cglibproxy.Person$$EnhancerByCGLIB$$c7ae5d4e");
Class var1;
CGLIB$run$0$Method = ReflectUtils.findMethods(new String[]{"run", "()V"}, (var1 = Class.forName("com.codezhao.designpattern.proxypattern.cglibproxy.Person")).getDeclaredMethods())[0];
CGLIB$run$0$Proxy = MethodProxy.create(var1, var0, "()V", "run", "CGLIB$run$0");
}
final void CGLIB$run$0() {
super.run();
}
public final void run() {
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$run$0$Method, CGLIB$emptyArgs, CGLIB$run$0$Proxy);
} else {
super.run();
}
}
}
2 FastClass机制
由于反射的效率较低,Cglib采用FastClass机制实现被代理类方法的调用,FastClass机制就是给一个类的方法建立索引,通过索引直接调用方法。看下面的例子理解一下:
/**
* @author codeZhao
* @date 2021/2/20 19:46
* @Description
*/
public class FastClassTest {
public static void main(String[] args) {
Test test = new Test();
Test$FastClass test$FastClass = new Test$FastClass();
int index = test$FastClass.getIndex("g()v");
test$FastClass.invoke(index, test);
}
}
class Test {
public void f() {
System.out.println("f()");
}
public void g() {
System.out.println("g()");
}
}
class Test$FastClass {
public Object invoke(int index, Object o) {
Test t = (Test) o;
switch (index) {
case 1:
t.f();
return null;
case 2:
t.g();
return null;
}
return null;
}
public int getIndex(String signature) {
switch (signature.hashCode()) {
case 3078511://"f()v的hashCode"
return 1;
case 3108302://"g()v的hashCode"
return 2;
}
return -1;
}
}
动态调用时,假设要调用某Test对象的g()
方法,FastClass机制实现方式是首先通过getIndex()
传入方法签名信息,获取方法的索引,然后通过invoke()
传入方法索引和类对象,执行索引对应方法。如果是反射来实现的话,大概是下面这样:
Test test = new Test();
Method method = Class.forName("Test").getMethod("g");
method.invoke(test);
FastClass机制和反射相比,构建时比较复杂,但运行效率高于反射。
对比上面的小例子,我们从代理类class文件开始,看下FastClass是如何创建的以及代理类如何调用的被代理类方法run()
。
1.首先,在代理类的静态代码块中,会通过MethodProxy.create(var1, var0, "()V", "run", "CGLIB$run$0")
创建针对run()
方法的MethodProxy对象CGLIB$run$0$Proxy
,该对象保存有生成FastClass所需的方法签名信息、代理/被代理类的信息。
public class Person$$EnhancerByCGLIB$$c7ae5d4e extends Person implements Factory {
static void CGLIB$STATICHOOK1() {
//代理类Class对象
Class var0 = Class.forName("com.codezhao.designpattern.proxypattern.cglibproxy.Person$$EnhancerByCGLIB$$c7ae5d4e");
//被代理类Class对象
Class var1 = Class.forName("com.codezhao.designpattern.proxypattern.cglibproxy.Person");
//创建针对某个方法的MethodProxy对象,该对象保存有代理/被代理方法的签名信息和代理/被代理类的信息
CGLIB$run$0$Proxy = MethodProxy.create(var1, var0, "()V", "run", "CGLIB$run$0");
}
}
//MethodProxy.create(var1, var0, "()V", "run", "CGLIB$run$0")对应的内容
public class MethodProxy {
public static MethodProxy create(Class c1, Class c2, String desc, String name1, String name2) {
MethodProxy proxy = new MethodProxy();
proxy.sig1 = new Signature(name1, desc);
proxy.sig2 = new Signature(name2, desc);
proxy.createInfo = new MethodProxy.CreateInfo(c1, c2);
return proxy;
}
}
2.然后在执行代理类的run()
方法时,会进入方法拦截器MethodInterceptor
的intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy)
方法,其中o传入代理类Person$$EnhancerByCGLIB$$c7ae5d4e
对象,methodProxy传入run()
方法的MethodProxy对象CGLIB$run$0$Proxy
,intercept()
中会执行methodProxy.invokeSuper()
。
methodProxy.invokeSuper()
这个方法比较关键,涉及FastClass生成、被代理类方法调用。
其中又涉及一个关键的内部类FastClassInfo
:
private static class FastClassInfo {
FastClass f1; //被代理类Person的FastClass
FastClass f2; //代理类Person$$EnhancerByCGLIB$$c7ae5d4e的FastClass
int i1; //方法run()在f1中的索引
int i2; //方法CGLIB$run$0()在f2中的索引
}
3.invokeSuper()
方法中首先进入init()
检查FastClassInfo
是否存在,没有的话就先生成。fci.f2.invoke(fci.i2, obj, args)
的作用就是调用代理类FastClass对象f1的invoke()
方法,根据传入的索引fci.i2
执行代理类对象对应的方法CGLIB$run$0()
,CGLIB$run$0()
会直接调用被代理类的run()
方法。
public class MethodProxy {
public Object invokeSuper(Object obj, Object[] args) throws Throwable {
try {
this.init();
MethodProxy.FastClassInfo fci = this.fastClassInfo;
return fci.f2.invoke(fci.i2, obj, args);
} catch (InvocationTargetException var4) {
throw var4.getTargetException();
}
}
}
//fci.f2.invoke(fci.i2, obj, args)对应的内容
public class Person$$EnhancerByCGLIB$$c7ae5d4e$$FastClassByCGLIB$$6be6b960 extends FastClass {
public Object invoke(int var1, Object var2, Object[] var3) {
c7ae5d4e var10000 = (c7ae5d4e)var2;
int var10001 = var1;
switch(var10001) {
...
case 20:
var10000.CGLIB$run$0();
return null;
...
}
}
//CGLIB$run$0()对应内容
public class Person$$EnhancerByCGLIB$$c7ae5d4e extends Person implements Factory {
final void CGLIB$run$0() {
super.run();
}
}
通过2、3步就知道了代理类如何调用的被代理类方法。
4.回过头看一下init()
中FastClassInfo
的生成,其中涉及代理/被代理类的FastClass类、代理/被代理方法的索引的生成。具体流程看下面的注释,还是比较清晰的。
public class MethodProxy {
private void init() {
if (this.fastClassInfo == null) {
synchronized(this.initLock) {
if (this.fastClassInfo == null) {
//createInfo由之前所讲的代理类中的MethodProxy.create(var1, var0, "()V", "run", //"CGLIB$run$0")生成,包含了代理/被代理方法的名字信息和代理/被代理类的信息
MethodProxy.CreateInfo ci = this.createInfo;
//新建一个FastClassInfo对象
MethodProxy.FastClassInfo fci = new MethodProxy.FastClassInfo();
//生成被代理类Person的FastClass,f1
fci.f1 = helper(ci, ci.c1);
//生成代理类Person$$EnhancerByCGLIB$$c7ae5d4e的FastClass,f2
fci.f2 = helper(ci, ci.c2);
//根据方法run()的签名信息获取其在f1中的索引
fci.i1 = fci.f1.getIndex(this.sig1);
//根据方法CGLIB$run$0()的签名信息获取其在f2中的索引
fci.i2 = fci.f2.getIndex(this.sig2);
this.fastClassInfo = fci;
this.createInfo = null;
}
}
}
}
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();
}
}
//代理类FastClass的getIndex(),就是根据方法签名信息的hashCode返回索引
//至于方法CGLIB$run$0()的签名信息为什么叫""CGLIB$run$0()V"",想一下之前的MethodProxy.create(var1, var0, "()V", "run", //"CGLIB$run$0")就知道了
public class Person$$EnhancerByCGLIB$$c7ae5d4e$$FastClassByCGLIB$$6be6b960 extends FastClass {
public Person$$EnhancerByCGLIB$$c7ae5d4e$$FastClassByCGLIB$$6be6b960(Class var1) {
super(var1);
}
public int getIndex(Signature var1) {
String var10000 = var1.toString();
switch(var10000.hashCode()) {
...
case 53168513:
if (var10000.equals("CGLIB$run$0()V")) {
return 20;
}
break;
...
return -1;
}
}
3 总结
1.Cglib动态代理中的代理类没有默认继承的父类,所以Cglib对接口和类都可以实现代理。
2.Jdk和Cglib中代理类对象调用被代理类方法的流程都是:通过代理类调用方法拦截器(Cglib中是MethodInterceptor,Proxy中是InvocationHandler),方法拦截器再调用被代理类方法。不同的是:Jdk中调用被代理类方法是通过反射,而Cglib中调用被代理类方法是通过事先建立好的索引,索引的建立和获取在FastClass中。
3.因为反射会降低性能,所以Cglib动态代理运行效率优于Jdk动态代理。
欢迎关注我的公众号【codeZhao】,获取更多技术干货和职业思考。