️ 场景启航:你的应用就像一辆赛车,每次调用@Service注解的Bean都像进入维修区——代理工厂的师傅们必须快速完成性能调校。今天我们将拆解Spring的代理车间,看看它如何选择JDK和CGLIB两种改装套件,以及如何榨出最后10%的性能潜力。
// 手动创建JDK代理示例
public class JdkProxyDemo implements InvocationHandler {
private Object target;
public Object wrap(Object target) {
this.target = target;
return Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
this
);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
long start = System.nanoTime();
Object result = method.invoke(target, args);
System.out.printf("方法%s执行耗时%d ns\n", method.getName(), System.nanoTime()-start);
return result;
}
}
// 使用限制:必须实现接口
设计特点:
基于接口反射调用
代理对象轻量化
JVM有深度优化
// 手工CGLIB代理示例
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(UserService.class);
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy)
throws Throwable {
System.out.println("Before:" + method.getName());
Object result = proxy.invokeSuper(obj, args);
System.out.println("After:" + method.getName());
return result;
}
});
UserService proxy = (UserService) enhancer.create();
性能耗材:
字节码生成开销较大
生成FastClass绕过反射
支持非接口类代理
// 基准测试框架JMH测试
@BenchmarkMode(Mode.SingleShotTime)
public class ProxyBenchmark {
@Benchmark
public Object jdkProxy() {
return Proxy.newProxyInstance(...);
}
@Benchmark
public Object cglibProxy() {
return new Enhancer().create();
}
}
// 测试结果(i9-13900K):
// JDK Proxy平均耗时:1523 ns/op
// CGLIB平均耗时:58234 ns/op(首次生成)
// 方法调用基准测试
@BenchmarkMode(Mode.Throughput)
public class InvokeBenchmark {
private UserService jdkProxy;
private UserService cglibProxy;
@Setup
public void init() {
// 初始化两种代理对象
}
@Benchmark
public void jdkInvoke() {
jdkProxy.findUser(1001);
}
@Benchmark
public void cglibInvoke() {
cglibProxy.findUser(1001);
}
}
// 结果数据:
// JDK Proxy调用吞吐:1,234,567 ops/s
// CGLIB调用吞吐:1,189,234 ops/s
Spring源码决策逻辑(AbstractAutoProxyCreator类片段):
protected Object createProxy(Class<?> beanClass, String beanName, Object[] specificInterceptors) {
// 判断是否应该使用CGLIB
if (!targetClass.isInterface() && !Proxy.isProxyClass(targetClass)) {
if (this.proxyTargetClass || hasNoUserSuppliedProxyInterfaces()) {
return buildCglibProxy(beanClass);
}
}
return buildJdkProxy(beanClass);
}
自动选型规则:
Spring在内部维护了代理类缓存:
// DefaultAopProxyFactory类
public AopProxy createAopProxy(AdvisedSupport config) {
if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxiedInterfaces()) {
// 使用CGLIB并检查缓存
return new ObjenesisCglibAopProxy(config);
} else {
// JDK代理缓存由JVM管理
return new JdkDynamicAopProxy(config);
}
}
缓存层级:
@SpringBootApplication
@EnableAspectJAutoProxy(proxyTargetClass = true) // 强制所有代理使用CGLIB
public class Application { ... }
适用场景:
• 存在循环依赖问题
• 需要代理非接口方法
• 已准备好承担启动性能损失
// 预生成CGLIB代理类到磁盘
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "/tmp/cglib");
缓存预热效果:
首次启动生成耗时5s → 后续启动直接加载缓存耗时200ms
生成的FastClass绕过反射调用:
// 生成的FastMethod实现
public Object invoke(int index, Object obj, Object[] args) {
switch(index) {
case 0: // findUser()方法索引
return ((UserService)obj).findUser((Integer)args[0]);
case 1: // updateUser()
return ((UserService)obj).updateUser((UserDTO)args[0]);
// ...其他方法
}
}
// 对比传统反射调用:
Method method = clazz.getMethod("findUser", Integer.class);
method.invoke(target, 1001);
性能测试结果:
反射调用:256 ns/op
FastClass调用:89 ns/op
事故现象 | 错误配置原因 | 修复方案 |
---|---|---|
启动时间飙升 | @Async方法没有接口 | 添加接口或接受启动耗时 |
Full GC频繁 | CGLIB缓存无限膨胀 | 配置-XX:MetaspaceSize=256m |
方法调用变慢 | 混合使用两种代理 | 统一代理实现方式 |
// 代理性能监控切面
@Aspect
@Component
public class ProxyPerfAspect {
@Around("@within(org.springframework.stereotype.Service)")
public Object monitorProxy(ProceedingJoinPoint pjp) throws Throwable {
long start = System.nanoTime();
try {
return pjp.proceed();
} finally {
long cost = System.nanoTime() - start;
Metrics.record(pjp.getSignature().toShortString(), cost);
}
}
}
当JDK21的虚拟线程遇上Spring代理:
站在赛道的维修区,看着你的应用经过代理优化后一骑绝尘——这就是Spring框架调校的艺术。现在,拿出你的代码诊断工具箱,开始这场速度与激情的调优之旅吧!