系列文章:
cglib的思路和jdk的差不多,大家首先一定要看过上,再来看下,下面我们通过一张图给大家回顾一下两者的相似之处和区别:
JDK的动态代理使用InvocationHandler作为拦截器,拦截所有代理方法的执行,从而可以通过InvocationHandler的invoke方法完成逻辑织入功能
本节的标题是模拟cglib动态代理,我们这里模拟的是cglib生成的$Proxy0代理类的样子
public class Target {
public void save() {
System.out.println("save()");
}
public void save(int i) {
System.out.println("save(int)");
}
public void save(long j) {
System.out.println("save(long)");
}
}
public class Proxy extends Target {
private MethodInterceptor methodInterceptor;
public void setMethodInterceptor(MethodInterceptor methodInterceptor) {
this.methodInterceptor = methodInterceptor;
}
static Method save0;
static Method save1;
static Method save2;
static MethodProxy save0Proxy;
static MethodProxy save1Proxy;
static MethodProxy save2Proxy;
static {
try {
save0 = Target.class.getMethod("save");
save1 = Target.class.getMethod("save", int.class);
save2 = Target.class.getMethod("save", long.class);
save0Proxy = MethodProxy.create(Target.class, Proxy.class, "()V", "save", "saveSuper");
save1Proxy = MethodProxy.create(Target.class, Proxy.class, "(I)V", "save", "saveSuper");
save2Proxy = MethodProxy.create(Target.class, Proxy.class, "(J)V", "save", "saveSuper");
} catch (NoSuchMethodException e) {
throw new NoSuchMethodError(e.getMessage());
}
}
// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 带原始功能的方法
public void saveSuper() {
super.save();
}
public void saveSuper(int i) {
super.save(i);
}
public void saveSuper(long j) {
super.save(j);
}
// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 带增强功能的方法
@Override
public void save() {
try {
methodInterceptor.intercept(this, save0, new Object[0], save0Proxy);
} catch (Throwable e) {
throw new UndeclaredThrowableException(e);
}
}
@Override
public void save(int i) {
try {
methodInterceptor.intercept(this, save1, new Object[]{i}, save1Proxy);
} catch (Throwable e) {
throw new UndeclaredThrowableException(e);
}
}
@Override
public void save(long j) {
try {
methodInterceptor.intercept(this, save2, new Object[]{j}, save2Proxy);
} catch (Throwable e) {
throw new UndeclaredThrowableException(e);
}
}
}
MethodProxy这里的功能主要是避免方法的反射调用,这里大家先知道即可,下面会详细解释cglib是如何实现的
public class A13 {
public static void main(String[] args) {
Proxy proxy = new Proxy();
Target target = new Target();
proxy.setMethodInterceptor(new MethodInterceptor() {
@Override
public Object intercept(Object p, Method method, Object[] args,
MethodProxy methodProxy) throws Throwable {
System.out.println("before...");
// return method.invoke(target, args); // 反射调用
// FastClass
// return methodProxy.invoke(target, args); // 内部无反射, 结合目标用
return methodProxy.invokeSuper(p, args); // 内部无反射, 结合代理用
}
});
proxy.save();
proxy.save(1);
proxy.save(2L);
}
}
和 jdk 动态代理原理查不多
public class A14Application {
public static void main(String[] args) throws InvocationTargetException {
Target target = new Target();
Proxy proxy = new Proxy();
proxy.setCallbacks(new Callback[]{(MethodInterceptor) (p, m, a, mp) -> {
System.out.println("proxy before..." + mp.getSignature());
// ⬇️调用目标方法(三种)
// Object result = m.invoke(target, a); // ⬅️反射调用
// Object result = mp.invoke(target, a); // ⬅️非反射调用, 结合目标用
Object result = mp.invokeSuper(p, a); // ⬅️非反射调用, 结合代理用
System.out.println("proxy after..." + mp.getSignature());
return result;
}});
// ⬇️调用代理方法
proxy.save();
}
}
注意
上面讲到了,通过MethodProxy,cglib可以避免通过反射调用目标对象里面的方法,那么具体是如何做到的呢?
注意看这一行代码:
//这里()V是方法描述符号,使用的是底层jvm表示方法
save0Proxy = MethodProxy.create(Target.class, Proxy.class, "()V", "save", "saveSuper");
看一下create方法的具体实现;
public static MethodProxy create(Class c1, Class c2, String desc, String name1, String name2) {
MethodProxy proxy = new MethodProxy();
//创建了两个方法签名---这里方法签名包裹了两个信息,为被代理对象原始方法名--save和方法描述符号---》()V
//name1是为了方便通过目标对象直接调用,避免反射调用
proxy.sig1 = new Signature(name1, desc);
//此方法前面的name2指的是---saveSuper---主要是为了代理对象调用该方法,从而避免反射---该方法代码回顾一下:
proxy.sig2 = new Signature(name2, desc);
proxy.createInfo = new MethodProxy.CreateInfo(c1, c2);
return proxy;
}
回顾一下saveSuper方法:
public void saveSuper() {
//父类不就是调用目标对象的方法吗,这样就不需要反射调用了
super.save();
}
createInfo方法干了啥子?
//c1是目标对象class,c2是代理对象class
proxy.createInfo = new MethodProxy.CreateInfo(c1, c2);
主要关注c1和c2就可以了: 分别封装了目标对象和代理对象的class类型
private static class CreateInfo {
Class c1;
Class c2;
NamingPolicy namingPolicy;
GeneratorStrategy strategy;
boolean attemptLoad;
public CreateInfo(Class c1, Class c2) {
this.c1 = c1;
this.c2 = c2;
AbstractClassGenerator fromEnhancer = AbstractClassGenerator.getCurrent();
if (fromEnhancer != null) {
this.namingPolicy = fromEnhancer.getNamingPolicy();
this.strategy = fromEnhancer.getStrategy();
this.attemptLoad = fromEnhancer.getAttemptLoad();
}
}
}
回顾上面cglib的测试代码:
MethodInterceptor:
public interface MethodInterceptor extends Callback {
Object intercept(Object var1, Method var2, Object[] var3, MethodProxy var4) throws Throwable;
}
//上面说过,通过MethodInterceptor 的invoke和invokesuper方法调用可以避免对方法的反射调用,那么如何实现的呢?
//传入的是目标对象和方法参数---猜测和上面的-----》proxy.sig1 = new Signature(save, desc);有关
Object result = mp.invoke(target, a); // ⬅️非反射调用, 结合目标用
//传入的是代理对象和方法参数----猜测和上面的----》proxy.sig1 = new Signature(saveSuper, desc);有关
Object result = mp.invokeSuper(p, a); // ⬅️非反射调用, 结合代理用
来看看MethodProxy的invoke方法和invokeSuper方法源码:
public Object invoke(Object obj, Object[] args) throws Throwable {
try {
//这里的init方法很重要
this.init();
//FastClassInfo封装的是啥?
MethodProxy.FastClassInfo fci = this.fastClassInfo;
//f1.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;
}
}
}
public Object invokeSuper(Object obj, Object[] args) throws Throwable {
try {
//init方法干了(O_o)??
this.init();
//同样的疑问?
MethodProxy.FastClassInfo fci = this.fastClassInfo;
return fci.f2.invoke(fci.i2, obj, args);
} catch (InvocationTargetException var4) {
throw var4.getTargetException();
}
}
问题很多,我们一个个解决,先来看一下FastClassInfo是什么:
private static class FastClassInfo {
//这里解释一下: FastClass是cglib为目标对象调用反射避免反射创建和代理对象调用父类方法避免反射创建而规定的两个代理类
FastClass f1;
FastClass f2;
int i1;
int i2;
private FastClassInfo() {
}
}
第一个疑惑揭开,下面看一下init方法的实现:
private void init() {
//如果两个代理类已经生成好了,那么就直接返回
//这里使用了单例模式的双重锁检测机制
if (this.fastClassInfo == null) {
synchronized(this.initLock) {
if (this.fastClassInfo == null) {
//CreateInfo包裹的是目标对象和代理对象的class类型
MethodProxy.CreateInfo ci = this.createInfo;
//包裹的是为目标对象和代理对象又分别生成的代理对象
MethodProxy.FastClassInfo fci = new MethodProxy.FastClassInfo();
//根据目标对象的class类型,生成其的代理对象(不要搞混,这个其实不算正统的代理对象,主要是为了避免反射调用方法,下面会讲具体实现)
fci.f1 = helper(ci, ci.c1);
//根据代理对象class类型,生成其代理对象
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;
}
}
}
}
helper方法源码:主要就是生成FastClass代理类
private static FastClass helper(MethodProxy.CreateInfo ci, Class type) {
Generator g = new Generator();
g.setType(type);
g.setContextClass(type);
g.setClassLoader(ci.c2.getClassLoader());
g.setNamingPolicy(ci.namingPolicy);
g.setStrategy(ci.strategy);
g.setAttemptLoad(ci.attemptLoad);
return g.create();
}
init方法就是为了生成FastClass代理类
回到invoke方法:
//生成了FastClass代理类
this.init();
//拿到了当前方法对象的两个代理类
MethodProxy.FastClassInfo fci = this.fastClassInfo;
//执行对目标对象再进行包装的代理类的invoke方法--也就是说该方法是直接调用的方法,而不是反射调用的方法
//注意这里第一个参数传入的是当前被调用方法的索引值,第二个参数是目标对象,第三个参数是参数值
return fci.f1.invoke(fci.i1, obj, args);
回到invokeSuper方法:
//生成了FastClass代理类
this.init();
//拿到了当前方法对象的两个代理类
MethodProxy.FastClassInfo fci = this.fastClassInfo;
//执行对代理方法再进行包装的代理类的invoke方法---该方法也必须是直接调用的方法,而不是反射调用
//注意这里第一个参数传入的是当前被调用方法的索引值,第二个参数是代理对象,第三个参数是参数值
return fci.f2.invoke(fci.i2, obj, args);
但是这里FastClass的invoke方法都是抽象方法,我们也不方便查看其子类的具体实现,因此是动态生成的字节码,但是下面我可以给大家模拟一下这个实现的思路:
模拟生成的代理对象的代理对象类:
这里只重写了父类中两个最重要的方法
public class ProxyFastClass {
static Signature s0 = new Signature("saveSuper", "()V");
static Signature s1 = new Signature("saveSuper", "(I)V");
static Signature s2 = new Signature("saveSuper", "(J)V");
// 获取代理方法的编号
/*
Proxy
saveSuper() 0
saveSuper(int) 1
saveSuper(long) 2
signature 包括方法名字、参数返回值
*/
public int getIndex(Signature signature) {
if (s0.equals(signature)) {
return 0;
} else if (s1.equals(signature)) {
return 1;
} else if (s2.equals(signature)) {
return 2;
}
return -1;
}
// 根据方法编号, 正常调用目标对象方法
public Object invokeSuper(int index, Object proxy, Object[] args) {
if (index == 0) {
((Proxy) proxy).saveSuper();
return null;
} else if (index == 1) {
((Proxy) proxy).saveSuper((int) args[0]);
return null;
} else if (index == 2) {
((Proxy) proxy).saveSuper((long) args[0]);
return null;
} else {
throw new RuntimeException("无此方法");
}
}
public static void main(String[] args) {
ProxyFastClass fastClass = new ProxyFastClass();
//这里的方法索引是在init方法中计算出来的
int index = fastClass.getIndex(new Signature("saveSuper", "()V"));
System.out.println(index);
//调用代理类生成的代理类的invokeSuper方法
fastClass.invokeSuper(index, new Proxy(), new Object[0]);
}
}
模拟生成目标对象的代理类(不要搞混):
public class TargetFastClass {
static Signature s0 = new Signature("save", "()V");
static Signature s1 = new Signature("save", "(I)V");
static Signature s2 = new Signature("save", "(J)V");
// 获取目标方法的编号
/*
Target
save() 0
save(int) 1
save(long) 2
signature 包括方法名字、参数返回值
*/
public int getIndex(Signature signature) {
if (s0.equals(signature)) {
return 0;
} else if (s1.equals(signature)) {
return 1;
} else if (s2.equals(signature)) {
return 2;
}
return -1;
}
// 根据方法编号, 正常调用目标对象方法
public Object invoke(int index, Object target, Object[] args) {
if (index == 0) {
((Target) target).save();
return null;
} else if (index == 1) {
((Target) target).save((int) args[0]);
return null;
} else if (index == 2) {
((Target) target).save((long) args[0]);
return null;
} else {
throw new RuntimeException("无此方法");
}
}
public static void main(String[] args) {
TargetFastClass fastClass = new TargetFastClass();
//init方法中计算出来当前被调用方法的索引
int index = fastClass.getIndex(new Signature("save", "(I)V"));
System.out.println(index);
fastClass.invoke(index, new Target(), new Object[]{100});
}
}
相信大家都已经看懂了,下面我再给大家做一个小小的汇总:
当调用 MethodProxy 的 invoke 或 invokeSuper 方法时, 会动态生成两个类
TargetFastClass 记录了 Target 中方法与编号的对应关系
ProxyFastClass 记录了 Proxy 中方法与编号的对应关系,不过 Proxy 额外提供了下面几个方法
为什么有这么麻烦的一套东西呢?