第十四讲-Cglib代理原理
第十四讲-Cglib代理原理
1. Cglib代理类的模拟实现
首先准备一个目标类:
package com.cherry.chapter1.a14;
public class Target {
public void save(){
System.out.println("save()");
}
public void save(int i){
System.out.println("save(int)");
}
public void save(long i){
System.out.println("save(long)");
}
}
编写一个代理类来模拟Cglib的代理类,继承目标类并重写父类中的方法:
package com.cherry.chapter1.a14;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.UndeclaredThrowableException;
import java.lang.reflect.Method;
public class CglibProxy extends Target{
private MethodInterceptor methodInterceptor;
public CglibProxy(MethodInterceptor methodInterceptor) {
this.methodInterceptor = methodInterceptor;
}
static Method save0;
static Method save1;
static Method save2;
static {
try{save0 = Target.class.getDeclaredMethod("save");
save1 = Target.class.getDeclaredMethod("save", int.class);
save2 = Target.class.getDeclaredMethod("save", long.class);
} catch (NoSuchMethodException e){
throw new NoSuchMethodError(e.getMessage());
}
}
@Override
public void save() {
try {
methodInterceptor.intercept(this, save0, new Object[0], null);
} catch (Throwable e) {
throw new UndeclaredThrowableException(e);
}
}
@Override
public void save(int i) {
try {
methodInterceptor.intercept(this, save1, new Object[]{i}, null);
} catch (Throwable e) {
throw new UndeclaredThrowableException(e);
}
}
@Override
public void save(long j) {
try {
methodInterceptor.intercept(this, save2, new Object[]{j}, null);
} catch (Throwable e) {
throw new UndeclaredThrowableException(e);
}
}
}
编写一个测试类测试一下:
package com.cherry.chapter1.a14;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class A14 {
public static void main(String[] args) {
Target target = new Target();
CglibProxy proxy = new CglibProxy(new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("before....");
return method.invoke(target, args);
}
});
proxy.save();
proxy.save(100);
proxy.save(1000L);
}
}
before....
save()
before....
save(int)
before....
save(long)
上面我们还是采用了Cglib中利用反射调用来调用方法,但是Cglib除了利用反射实现方法调用,还有一个属性,就是MethodProxy
,MethodProxy可以让我们不经过反射来调用方法。现在我们来看一下MethodProxy对象是如何被创建的
package com.cherry.chapter1.a14;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import org.springframework.cglib.proxy.UndeclaredThrowableException;
import java.lang.reflect.Method;
public class CglibProxy extends Target {
private MethodInterceptor methodInterceptor;
public CglibProxy(MethodInterceptor methodInterceptor) {
this.methodInterceptor = methodInterceptor;
}
static Method save0;
static Method save1;
static Method save2;
// 创建MethodProxy对象
static MethodProxy save0Proxy;
static MethodProxy save1Proxy;
static MethodProxy save2Proxy;
static {
try {
save0 = Target.class.getDeclaredMethod("save");
save1 = Target.class.getDeclaredMethod("save", int.class);
save2 = Target.class.getDeclaredMethod("save", long.class);
/**
* create(目标类型,代理类型, 参数类型和返回值类型,带增强功能的方法名,带原始功能的方法名)
*/
save0Proxy = MethodProxy.create(Target.class, CglibProxy.class, "()V", "save", "saveSuper");
save1Proxy = MethodProxy.create(Target.class, CglibProxy.class, "(I)V", "save", "saveSuper");
save2Proxy = MethodProxy.create(Target.class, CglibProxy.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);
}
}
}
接下来我们测试一下:
package com.cherry.chapter1.a14;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class A14 {
public static void main(String[] args) {
Target target = new Target();
CglibProxy proxy = new CglibProxy(new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("before....");
// return method.invoke(target, args); ---> 反射调用
return methodProxy.invoke(target, args);// ---> 无反射调用,结合目标用
// methodProxy.invokeSuper(o, args); //内部无反射,结合代理用
}
});
proxy.save();
proxy.save(100);
proxy.save(1000L);
}
}
before....
save()
before....
save(int)
before....
save(long)
我们发现增强功能有了。
2. Cglib是如何避免反射调用的
刚才我们讲了,MethodProxy在调用invoke或者invokeSuper在内部是不会走反射的,而是正常调用, 那么其内部是怎么实现的呢?其实是它们两者的内部又会产生两个代理类,这两个代理类一个配合invoke和目标使用,另一个配合invokeSuper和代理对象使用。值得注意的是,这两个代理类都继承于FastClass
这个类。我们为FastClass提供两个子类,,来看一下其中的index和invoke方法是怎么实现的。
package com.cherry.chapter1.a14;
import org.springframework.cglib.core.Signature;
import java.lang.reflect.InvocationTargetException;
/**
* 在代理中中的MethodProxy.create()方法就会生成一个FastClass对象
*/
// 配合目标一起使用
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 i) 1
* save(long j) 2
* 针对每一个方法给定一个唯一的编号,
* getIndex根据方法签名信息获取方法的编号
* 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; // 表示没有找到该方法签名
}
/**
* invoke根据getIndex()返回的方法编号去正常调用目标对象中的方法
*/
public Object invoke(int index, Object target, Object[] args) throws InvocationTargetException{
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;
}
}
}
这里呢,我们梳理一下:
-
我们在使用MethodProxy对象时,底层就会创建一个FastClass对象(代理类对象),我们会将每一个方法封装为每一个方法签名,并且给定对应的唯一编号
-
当我们调用methodProxy.invoke(target, args)方法时,底层会调用fastClass对象的invoke方法,会根据传递过来的方法名和方法签名进行匹配,如果匹配上了,就会正常调用该方法,从而避免了反射调用。
分类:
Spring 高级49讲
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构