Spring动态代理详解:从原理到实战
前言
动态代理是Spring框架实现AOP、事务管理、声明式缓存等核心功能的基石。理解动态代理的原理和使用方式,对于深入掌握Spring框架至关重要。本文将从代理模式的基本概念出发,详细讲解JDK动态代理和CGLIB代理的实现原理,以及Spring如何选择和使用这两种代理方式。
一、代理模式概述
1.1 什么是代理模式
代理模式是一种结构型设计模式,它为目标对象提供一个代理对象,由代理对象控制对目标对象的访问。
arduino
代理模式结构
┌─────────────────────────────────────────┐
│ Client(客户端) │
│ │ │
│ ▼ │
│ ┌───────────┐ │
│ │ Proxy │ ← 代理对象 │
│ └─────┬─────┘ │
│ │ │
│ 代理逻辑: │
│ - 前置处理(日志、权限等) │
│ - 调用目标方法 │
│ - 后置处理(结果处理等) │
│ │ │
│ ▼ │
│ ┌───────────┐ │
│ │ Target │ ← 目标对象 │
│ └───────────┘ │
└─────────────────────────────────────────┘
1.2 静态代理
java
/**
* 接口
*/
public interface UserService {
User findById(Long id);
void save(User user);
}
/**
* 目标类
*/
public class UserServiceImpl implements UserService {
@Override
public User findById(Long id) {
System.out.println("查询用户: " + id);
return new User(id, "User" + id);
}
@Override
public void save(User user) {
System.out.println("保存用户: " + user.getUsername());
}
}
/**
* 静态代理类
*/
public class UserServiceProxy implements UserService {
private UserService target;
public UserServiceProxy(UserService target) {
this.target = target;
}
@Override
public User findById(Long id) {
// 前置增强
System.out.println("[静态代理] 方法开始: findById");
long startTime = System.currentTimeMillis();
// 调用目标方法
User result = target.findById(id);
// 后置增强
long endTime = System.currentTimeMillis();
System.out.println("[静态代理] 方法结束,耗时: " + (endTime - startTime) + "ms");
return result;
}
@Override
public void save(User user) {
System.out.println("[静态代理] 方法开始: save");
target.save(user);
System.out.println("[静态代理] 方法结束");
}
}
/**
* 使用静态代理
*/
public class StaticProxyDemo {
public static void main(String[] args) {
// 创建目标对象
UserService target = new UserServiceImpl();
// 创建代理对象
UserService proxy = new UserServiceProxy(target);
// 通过代理调用
proxy.findById(1L);
proxy.save(new User(null, "张三"));
}
}
静态代理的问题:
- 每个目标类都需要一个代理类
- 接口增加方法,代理类也要修改
- 代码重复,维护困难
1.3 动态代理
动态代理在运行时动态创建代理类,无需手动编写代理类代码。
静态代理 vs 动态代理
┌─────────────────────┬──────────────────────┐
│ 静态代理 │ 动态代理 │
├─────────────────────┼──────────────────────┤
│ 编译时创建代理类 │ 运行时创建代理类 │
│ 手动编写代理类 │ 自动生成代理类 │
│ 每个类一个代理 │ 一套代理逻辑复用 │
│ 维护困难 │ 灵活易扩展 │
└─────────────────────┴──────────────────────┘
二、JDK动态代理
2.1 JDK动态代理原理
JDK动态代理基于接口实现,通过java.lang.reflect.Proxy类和InvocationHandler接口来创建代理对象。
scss
JDK动态代理原理
┌─────────────────────────────────────────┐
│ 1. 运行时生成代理类($Proxy0) │
│ 2. 代理类实现目标接口 │
│ 3. 代理类持有InvocationHandler引用 │
│ 4. 调用代理方法时,转发给InvocationHandler
│ 5. InvocationHandler中调用目标方法 │
└─────────────────────────────────────────┘
┌───────────────┐
│ Interface │
└───────┬───────┘
│
┌────────┴────────┐
│ │
┌────┴────┐ ┌─────┴─────┐
│$Proxy0 │ │ Target │
│(代理类) │ │ (目标类) │
└────┬────┘ └───────────┘
│
▼
┌─────────────────┐
│InvocationHandler│
│ (调用处理器) │
└─────────────────┘
2.2 JDK动态代理实现
java
/**
* 自定义InvocationHandler
*/
public class LogInvocationHandler implements InvocationHandler {
private Object target;
public LogInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 获取方法信息
String methodName = method.getName();
String className = target.getClass().getSimpleName();
// 前置增强
System.out.println("[JDK代理] " + className + "." + methodName + "() 开始执行");
System.out.println("[JDK代理] 参数: " + Arrays.toString(args));
long startTime = System.currentTimeMillis();
Object result;
try {
// 调用目标方法
result = method.invoke(target, args);
// 后置增强
long endTime = System.currentTimeMillis();
System.out.println("[JDK代理] 返回值: " + result);
System.out.println("[JDK代理] 执行成功,耗时: " + (endTime - startTime) + "ms");
} catch (InvocationTargetException e) {
// 异常增强
System.out.println("[JDK代理] 执行异常: " + e.getTargetException().getMessage());
throw e.getTargetException();
}
return result;
}
}
/**
* JDK动态代理工厂
*/
public class JdkProxyFactory {
/**
* 创建代理对象
*/
@SuppressWarnings("unchecked")
public static <T> T createProxy(T target) {
return (T) Proxy.newProxyInstance(
target.getClass().getClassLoader(), // 类加载器
target.getClass().getInterfaces(), // 目标类实现的接口
new LogInvocationHandler(target) // 调用处理器
);
}
}
/**
* 使用JDK动态代理
*/
public class JdkProxyDemo {
public static void main(String[] args) {
// 创建目标对象
UserService target = new UserServiceImpl();
// 创建代理对象
UserService proxy = JdkProxyFactory.createProxy(target);
// 通过代理调用
proxy.findById(1L);
System.out.println("---");
proxy.save(new User(null, "李四"));
// 打印代理类信息
System.out.println("代理类: " + proxy.getClass().getName());
System.out.println("代理类实现的接口: " + Arrays.toString(proxy.getClass().getInterfaces()));
}
}
2.3 生成的代理类分析
java
/**
* JDK动态生成的代理类(反编译)
*/
public final class $Proxy0 extends Proxy implements UserService {
private static Method m1; // equals
private static Method m2; // toString
private static Method m3; // findById
private static Method m4; // save
private static Method m0; // hashCode
static {
try {
m3 = Class.forName("com.example.UserService")
.getMethod("findById", Long.class);
m4 = Class.forName("com.example.UserService")
.getMethod("save", User.class);
// ...
} catch (Exception e) {
throw new NoSuchMethodError(e.getMessage());
}
}
public $Proxy0(InvocationHandler h) {
super(h);
}
@Override
public User findById(Long id) {
try {
// 调用InvocationHandler的invoke方法
return (User) super.h.invoke(this, m3, new Object[]{id});
} catch (Throwable e) {
throw new UndeclaredThrowableException(e);
}
}
@Override
public void save(User user) {
try {
super.h.invoke(this, m4, new Object[]{user});
} catch (Throwable e) {
throw new UndeclaredThrowableException(e);
}
}
}
2.4 JDK代理的限制
java
/**
* JDK动态代理的限制
*/
public class JdkProxyLimitation {
public static void main(String[] args) {
// ❌ 错误:目标类没有实现接口
OrderService orderService = new OrderService();
// 以下代码会抛出异常
// OrderService proxy = JdkProxyFactory.createProxy(orderService);
// 因为OrderService没有实现任何接口
System.out.println("JDK动态代理要求目标类必须实现接口");
}
}
/**
* 没有实现接口的类
*/
public class OrderService {
public void createOrder() {
System.out.println("创建订单");
}
}
三、CGLIB动态代理
3.1 CGLIB代理原理
CGLIB(Code Generation Library)通过继承目标类的方式创建代理,不需要目标类实现接口。
php
CGLIB代理原理
┌─────────────────────────────────────────┐
│ 1. 运行时生成代理类(继承目标类) │
│ 2. 通过ASM字节码操作生成子类 │
│ 3. 代理类重写父类的非final方法 │
│ 4. 方法调用时,通过MethodInterceptor拦截│
│ 5. MethodInterceptor中调用父类方法 │
└─────────────────────────────────────────┘
┌───────────────┐
│ Target │
│ (目标类) │
└───────┬───────┘
│ extends
│
┌───────┴───────┐
│ Target$$CGLIB │
│ (代理类) │
└───────┬───────┘
│
▼
┌─────────────────┐
│MethodInterceptor│
│ (方法拦截器) │
└─────────────────┘
3.2 CGLIB代理实现
java
/**
* 自定义MethodInterceptor
*/
public class LogMethodInterceptor implements MethodInterceptor {
/**
* @param obj 代理对象
* @param method 目标方法
* @param args 方法参数
* @param proxy 方法代理
*/
@Override
public Object intercept(Object obj, Method method, Object[] args,
MethodProxy proxy) throws Throwable {
// 获取方法信息
String methodName = method.getName();
String className = method.getDeclaringClass().getSimpleName();
// 前置增强
System.out.println("[CGLIB代理] " + className + "." + methodName + "() 开始执行");
System.out.println("[CGLIB代理] 参数: " + Arrays.toString(args));
long startTime = System.currentTimeMillis();
Object result;
try {
// 调用父类方法(目标方法)
result = proxy.invokeSuper(obj, args);
// 后置增强
long endTime = System.currentTimeMillis();
System.out.println("[CGLIB代理] 返回值: " + result);
System.out.println("[CGLIB代理] 执行成功,耗时: " + (endTime - startTime) + "ms");
} catch (Throwable e) {
// 异常增强
System.out.println("[CGLIB代理] 执行异常: " + e.getMessage());
throw e;
}
return result;
}
}
/**
* CGLIB代理工厂
*/
public class CglibProxyFactory {
/**
* 创建代理对象
*/
@SuppressWarnings("unchecked")
public static <T> T createProxy(Class<T> targetClass) {
// 创建Enhancer对象
Enhancer enhancer = new Enhancer();
// 设置父类(目标类)
enhancer.setSuperclass(targetClass);
// 设置回调(方法拦截器)
enhancer.setCallback(new LogMethodInterceptor());
// 创建代理对象
return (T) enhancer.create();
}
/**
* 带构造参数创建代理对象
*/
@SuppressWarnings("unchecked")
public static <T> T createProxy(Class<T> targetClass,
Class<?>[] argTypes, Object[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(targetClass);
enhancer.setCallback(new LogMethodInterceptor());
return (T) enhancer.create(argTypes, args);
}
}
/**
* 目标类(没有实现接口)
*/
public class OrderService {
public Order createOrder(Long userId, BigDecimal amount) {
System.out.println("创建订单: 用户=" + userId + ", 金额=" + amount);
return new Order(System.currentTimeMillis(), userId, amount);
}
public void cancelOrder(Long orderId) {
System.out.println("取消订单: " + orderId);
}
// final方法不能被代理
public final void finalMethod() {
System.out.println("这是final方法,不能被代理");
}
}
/**
* 使用CGLIB代理
*/
public class CglibProxyDemo {
public static void main(String[] args) {
// 创建代理对象(无需目标对象实例)
OrderService proxy = CglibProxyFactory.createProxy(OrderService.class);
// 通过代理调用
proxy.createOrder(1L, new BigDecimal("99.99"));
System.out.println("---");
proxy.cancelOrder(1001L);
System.out.println("---");
// final方法不会被代理
proxy.finalMethod();
// 打印代理类信息
System.out.println("代理类: " + proxy.getClass().getName());
System.out.println("父类: " + proxy.getClass().getSuperclass().getName());
}
}
3.3 CGLIB的高级用法
java
/**
* 多个回调(CallbackFilter)
*/
public class MultiCallbackDemo {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(OrderService.class);
// 定义多个回调
Callback[] callbacks = new Callback[]{
new LogMethodInterceptor(), // 索引0:日志拦截器
new PerformanceMethodInterceptor(), // 索引1:性能拦截器
NoOp.INSTANCE // 索引2:不做任何操作
};
enhancer.setCallbacks(callbacks);
// 回调过滤器:决定每个方法使用哪个回调
enhancer.setCallbackFilter(method -> {
String methodName = method.getName();
if (methodName.startsWith("create")) {
return 0; // 使用日志拦截器
} else if (methodName.startsWith("find")) {
return 1; // 使用性能拦截器
} else {
return 2; // 不拦截
}
});
OrderService proxy = (OrderService) enhancer.create();
proxy.createOrder(1L, new BigDecimal("100"));
}
}
/**
* 性能监控拦截器
*/
public class PerformanceMethodInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object obj, Method method, Object[] args,
MethodProxy proxy) throws Throwable {
long startTime = System.currentTimeMillis();
Object result = proxy.invokeSuper(obj, args);
long duration = System.currentTimeMillis() - startTime;
System.out.println("[性能监控] " + method.getName() + " 耗时: " + duration + "ms");
return result;
}
}
/**
* 延迟加载(LazyLoader)
*/
public class LazyLoadDemo {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(HeavyObject.class);
// 延迟加载回调
enhancer.setCallback((LazyLoader) () -> {
System.out.println("开始创建HeavyObject...");
return new HeavyObject();
});
HeavyObject proxy = (HeavyObject) enhancer.create();
System.out.println("代理对象已创建,但目标对象尚未创建");
// 第一次调用方法时才创建目标对象
proxy.doSomething();
}
}
class HeavyObject {
public HeavyObject() {
// 模拟耗时初始化
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void doSomething() {
System.out.println("HeavyObject.doSomething()");
}
}
3.4 CGLIB的限制
java
/**
* CGLIB代理的限制
*/
public class CglibLimitation {
public static void main(String[] args) {
// ❌ 限制1:不能代理final类
// FinalClass proxy = CglibProxyFactory.createProxy(FinalClass.class);
// 会抛出异常:Cannot subclass final class
// ❌ 限制2:不能代理final方法
// final方法不会被拦截,直接调用原方法
// ❌ 限制3:不能代理private方法
// private方法不能被继承,无法代理
}
}
// final类不能被CGLIB代理
final class FinalClass {
public void method() {
System.out.println("final类的方法");
}
}
四、Spring中的代理选择
4.1 Spring代理选择策略
java
/**
* Spring代理选择策略
*/
@Configuration
@EnableAspectJAutoProxy // 默认使用JDK代理
// @EnableAspectJAutoProxy(proxyTargetClass = true) // 强制使用CGLIB
public class ProxyConfig {
}
ini
Spring代理选择流程
┌─────────────────────────────────────────┐
│ 目标类是否实现接口? │
│ │ │
│ ┌────┴────┐ │
│ │ │ │
│ 是 否 │
│ │ │ │
│ ▼ ▼ │
│ JDK代理 CGLIB代理 │
│ (默认) (自动) │
│ │
│ proxyTargetClass = true ? │
│ → 强制使用CGLIB代理 │
└─────────────────────────────────────────┘
4.2 AopProxyFactory源码分析
java
/**
* Spring AOP代理工厂(简化版)
*/
public class DefaultAopProxyFactory implements AopProxyFactory {
@Override
public AopProxy createAopProxy(AdvisedSupport config) {
// 判断使用哪种代理
if (config.isOptimize() || // 优化模式
config.isProxyTargetClass() || // 强制CGLIB
hasNoUserSuppliedProxyInterfaces(config)) { // 没有接口
Class<?> targetClass = config.getTargetClass();
if (targetClass == null) {
throw new AopConfigException("...");
}
// 如果是接口或已经是代理类,使用JDK代理
if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
return new JdkDynamicAopProxy(config);
}
// 否则使用CGLIB代理
return new ObjenesisCglibAopProxy(config);
} else {
// 默认使用JDK代理
return new JdkDynamicAopProxy(config);
}
}
private boolean hasNoUserSuppliedProxyInterfaces(AdvisedSupport config) {
Class<?>[] interfaces = config.getProxiedInterfaces();
return interfaces.length == 0 ||
(interfaces.length == 1 && SpringProxy.class.isAssignableFrom(interfaces[0]));
}
}
4.3 配置代理方式
java
/**
* XML配置方式
*/
/*
<aop:aspectj-autoproxy proxy-target-class="true"/>
*/
/**
* 注解配置方式
*/
@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class AopConfig {
}
/**
* application.properties配置
*/
// spring.aop.proxy-target-class=true
/**
* 事务配置中指定代理方式
*/
@Configuration
@EnableTransactionManagement(proxyTargetClass = true)
public class TransactionConfig {
}
五、实战案例
5.1 案例1:通用日志代理
java
/**
* 通用日志代理工厂
*/
public class UniversalLogProxyFactory {
/**
* 创建代理对象(自动选择代理方式)
*/
@SuppressWarnings("unchecked")
public static <T> T createProxy(T target) {
Class<?> targetClass = target.getClass();
// 判断是否有接口
if (targetClass.getInterfaces().length > 0) {
// 使用JDK代理
return (T) Proxy.newProxyInstance(
targetClass.getClassLoader(),
targetClass.getInterfaces(),
new UniversalLogHandler(target)
);
} else {
// 使用CGLIB代理
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(targetClass);
enhancer.setCallback(new UniversalLogInterceptor(target));
return (T) enhancer.create();
}
}
}
/**
* JDK代理处理器
*/
class UniversalLogHandler implements InvocationHandler {
private Object target;
public UniversalLogHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return LogHelper.logAndInvoke(target, method, args, () -> method.invoke(target, args));
}
}
/**
* CGLIB方法拦截器
*/
class UniversalLogInterceptor implements MethodInterceptor {
private Object target;
public UniversalLogInterceptor(Object target) {
this.target = target;
}
@Override
public Object intercept(Object obj, Method method, Object[] args,
MethodProxy proxy) throws Throwable {
return LogHelper.logAndInvoke(target, method, args, () -> proxy.invokeSuper(obj, args));
}
}
/**
* 日志辅助类
*/
class LogHelper {
public static Object logAndInvoke(Object target, Method method, Object[] args,
Callable<Object> invoker) throws Throwable {
String className = target.getClass().getSimpleName();
String methodName = method.getName();
System.out.println("┌─────────────────────────────────────");
System.out.println("│ 方法调用: " + className + "." + methodName);
System.out.println("│ 参数: " + Arrays.toString(args));
long startTime = System.currentTimeMillis();
try {
Object result = invoker.call();
long duration = System.currentTimeMillis() - startTime;
System.out.println("│ 返回值: " + result);
System.out.println("│ 耗时: " + duration + "ms");
System.out.println("└─────────────────────────────────────");
return result;
} catch (Exception e) {
System.out.println("│ 异常: " + e.getMessage());
System.out.println("└─────────────────────────────────────");
throw e;
}
}
}
/**
* 使用示例
*/
public class UniversalProxyDemo {
public static void main(String[] args) {
// 有接口的类 - 使用JDK代理
UserService userProxy = UniversalLogProxyFactory.createProxy(new UserServiceImpl());
userProxy.findById(1L);
System.out.println();
// 无接口的类 - 使用CGLIB代理
OrderService orderProxy = UniversalLogProxyFactory.createProxy(new OrderService());
orderProxy.createOrder(1L, new BigDecimal("100"));
}
}
5.2 案例2:方法执行重试代理
java
/**
* 重试注解
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Retryable {
int maxAttempts() default 3; // 最大重试次数
long delay() default 1000; // 重试间隔(毫秒)
Class<? extends Throwable>[] value() default {}; // 需要重试的异常
}
/**
* 重试代理工厂
*/
public class RetryProxyFactory {
@SuppressWarnings("unchecked")
public static <T> T createProxy(T target) {
Class<?> targetClass = target.getClass();
if (targetClass.getInterfaces().length > 0) {
return (T) Proxy.newProxyInstance(
targetClass.getClassLoader(),
targetClass.getInterfaces(),
new RetryHandler(target)
);
} else {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(targetClass);
enhancer.setCallback(new RetryInterceptor(target));
return (T) enhancer.create();
}
}
}
/**
* JDK重试处理器
*/
class RetryHandler implements InvocationHandler {
private Object target;
public RetryHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return RetryExecutor.executeWithRetry(target, method, args,
() -> method.invoke(target, args));
}
}
/**
* CGLIB重试拦截器
*/
class RetryInterceptor implements MethodInterceptor {
private Object target;
public RetryInterceptor(Object target) {
this.target = target;
}
@Override
public Object intercept(Object obj, Method method, Object[] args,
MethodProxy proxy) throws Throwable {
return RetryExecutor.executeWithRetry(target, method, args,
() -> proxy.invokeSuper(obj, args));
}
}
/**
* 重试执行器
*/
class RetryExecutor {
public static Object executeWithRetry(Object target, Method method, Object[] args,
Callable<Object> invoker) throws Throwable {
// 获取方法上的@Retryable注解
Method targetMethod = target.getClass().getMethod(method.getName(), method.getParameterTypes());
Retryable retryable = targetMethod.getAnnotation(Retryable.class);
if (retryable == null) {
// 没有注解,直接执行
return invoker.call();
}
int maxAttempts = retryable.maxAttempts();
long delay = retryable.delay();
Class<? extends Throwable>[] retryExceptions = retryable.value();
int attempt = 0;
Throwable lastException = null;
while (attempt < maxAttempts) {
attempt++;
try {
System.out.println("[重试代理] 第" + attempt + "次尝试执行: " + method.getName());
return invoker.call();
} catch (Throwable e) {
Throwable cause = e instanceof InvocationTargetException
? ((InvocationTargetException) e).getTargetException() : e;
// 检查是否需要重试
boolean shouldRetry = retryExceptions.length == 0 ||
Arrays.stream(retryExceptions).anyMatch(ex -> ex.isInstance(cause));
if (!shouldRetry || attempt >= maxAttempts) {
System.out.println("[重试代理] 重试失败: " + cause.getMessage());
throw cause;
}
lastException = cause;
System.out.println("[重试代理] 执行失败," + delay + "ms后重试: " + cause.getMessage());
try {
Thread.sleep(delay);
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
throw cause;
}
}
}
throw lastException;
}
}
/**
* 远程服务(演示重试)
*/
public class RemoteService {
private int callCount = 0;
@Retryable(maxAttempts = 3, delay = 500)
public String callRemoteApi(String param) {
callCount++;
System.out.println("调用远程API,第" + callCount + "次");
// 模拟偶发失败
if (callCount < 3) {
throw new RuntimeException("网络超时");
}
return "调用成功: " + param;
}
}
/**
* 使用示例
*/
public class RetryProxyDemo {
public static void main(String[] args) {
RemoteService proxy = RetryProxyFactory.createProxy(new RemoteService());
String result = proxy.callRemoteApi("test");
System.out.println("最终结果: " + result);
}
}
5.3 案例3:缓存代理
java
/**
* 缓存注解
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Cacheable {
String key() default "";
int expire() default 3600; // 过期时间(秒)
}
/**
* 缓存代理工厂
*/
public class CacheProxyFactory {
private static final Map<String, CacheEntry> cache = new ConcurrentHashMap<>();
@SuppressWarnings("unchecked")
public static <T> T createProxy(T target) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(target.getClass());
enhancer.setCallback(new CacheInterceptor(target));
return (T) enhancer.create();
}
static class CacheEntry {
Object value;
long expireTime;
CacheEntry(Object value, int expireSeconds) {
this.value = value;
this.expireTime = System.currentTimeMillis() + expireSeconds * 1000L;
}
boolean isExpired() {
return System.currentTimeMillis() > expireTime;
}
}
static class CacheInterceptor implements MethodInterceptor {
private Object target;
CacheInterceptor(Object target) {
this.target = target;
}
@Override
public Object intercept(Object obj, Method method, Object[] args,
MethodProxy proxy) throws Throwable {
// 获取@Cacheable注解
Method targetMethod = target.getClass().getMethod(
method.getName(), method.getParameterTypes());
Cacheable cacheable = targetMethod.getAnnotation(Cacheable.class);
if (cacheable == null) {
return proxy.invokeSuper(obj, args);
}
// 构建缓存key
String cacheKey = buildCacheKey(cacheable.key(), method, args);
// 尝试从缓存获取
CacheEntry entry = cache.get(cacheKey);
if (entry != null && !entry.isExpired()) {
System.out.println("[缓存代理] 缓存命中: " + cacheKey);
return entry.value;
}
// 缓存未命中,执行方法
System.out.println("[缓存代理] 缓存未命中: " + cacheKey);
Object result = proxy.invokeSuper(obj, args);
// 存入缓存
if (result != null) {
cache.put(cacheKey, new CacheEntry(result, cacheable.expire()));
System.out.println("[缓存代理] 缓存写入: " + cacheKey);
}
return result;
}
private String buildCacheKey(String keyPattern, Method method, Object[] args) {
if (keyPattern.isEmpty()) {
// 默认key:类名.方法名(参数)
return method.getDeclaringClass().getSimpleName() + "." +
method.getName() + "(" + Arrays.toString(args) + ")";
}
// 替换参数占位符
String key = keyPattern;
for (int i = 0; i < args.length; i++) {
key = key.replace("#p" + i, String.valueOf(args[i]));
}
return key;
}
}
}
/**
* 用户服务(带缓存)
*/
public class CachedUserService {
@Cacheable(key = "user:#p0", expire = 60)
public User findById(Long id) {
System.out.println("从数据库查询用户: " + id);
// 模拟数据库查询
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
return new User(id, "User" + id);
}
@Cacheable(expire = 300)
public List<User> findAll() {
System.out.println("从数据库查询所有用户");
return Arrays.asList(
new User(1L, "User1"),
new User(2L, "User2")
);
}
}
/**
* 使用示例
*/
public class CacheProxyDemo {
public static void main(String[] args) {
CachedUserService proxy = CacheProxyFactory.createProxy(new CachedUserService());
// 第一次调用,缓存未命中
User user1 = proxy.findById(1L);
System.out.println("查询结果: " + user1);
System.out.println();
// 第二次调用,缓存命中
User user2 = proxy.findById(1L);
System.out.println("查询结果: " + user2);
System.out.println();
// 不同参数,缓存未命中
User user3 = proxy.findById(2L);
System.out.println("查询结果: " + user3);
}
}
5.4 案例4:权限校验代理
java
/**
* 权限注解
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RequiresPermission {
String[] value();
}
/**
* 用户上下文(模拟)
*/
public class UserContext {
private static final ThreadLocal<Set<String>> permissions = new ThreadLocal<>();
public static void setPermissions(Set<String> perms) {
permissions.set(perms);
}
public static Set<String> getPermissions() {
return permissions.get();
}
public static boolean hasPermission(String permission) {
Set<String> perms = permissions.get();
return perms != null && perms.contains(permission);
}
public static void clear() {
permissions.remove();
}
}
/**
* 权限代理工厂
*/
public class PermissionProxyFactory {
@SuppressWarnings("unchecked")
public static <T> T createProxy(T target) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(target.getClass());
enhancer.setCallback(new PermissionInterceptor(target));
return (T) enhancer.create();
}
static class PermissionInterceptor implements MethodInterceptor {
private Object target;
PermissionInterceptor(Object target) {
this.target = target;
}
@Override
public Object intercept(Object obj, Method method, Object[] args,
MethodProxy proxy) throws Throwable {
// 获取@RequiresPermission注解
Method targetMethod = target.getClass().getMethod(
method.getName(), method.getParameterTypes());
RequiresPermission annotation = targetMethod.getAnnotation(RequiresPermission.class);
if (annotation != null) {
String[] required = annotation.value();
// 检查权限
for (String permission : required) {
if (!UserContext.hasPermission(permission)) {
throw new SecurityException("缺少权限: " + permission);
}
}
System.out.println("[权限代理] 权限校验通过: " + Arrays.toString(required));
}
return proxy.invokeSuper(obj, args);
}
}
}
/**
* 管理服务
*/
public class AdminService {
@RequiresPermission("user:delete")
public void deleteUser(Long userId) {
System.out.println("删除用户: " + userId);
}
@RequiresPermission({"user:create", "user:update"})
public void createOrUpdateUser(User user) {
System.out.println("创建或更新用户: " + user.getUsername());
}
public void queryUser(Long userId) {
System.out.println("查询用户: " + userId);
}
}
/**
* 使用示例
*/
public class PermissionProxyDemo {
public static void main(String[] args) {
AdminService proxy = PermissionProxyFactory.createProxy(new AdminService());
// 设置用户权限
UserContext.setPermissions(new HashSet<>(Arrays.asList("user:query", "user:create")));
try {
// 无需权限的方法
proxy.queryUser(1L);
System.out.println();
// 权限不足
proxy.deleteUser(1L);
} catch (SecurityException e) {
System.out.println("权限校验失败: " + e.getMessage());
}
System.out.println();
// 添加权限后重试
UserContext.setPermissions(new HashSet<>(Arrays.asList("user:delete")));
proxy.deleteUser(1L);
UserContext.clear();
}
}
六、性能对比
6.1 JDK代理 vs CGLIB性能测试
java
/**
* 性能测试
*/
public class ProxyPerformanceTest {
private static final int ITERATIONS = 1000000;
public static void main(String[] args) {
// 预热
warmup();
// 测试直接调用
testDirectCall();
// 测试JDK代理
testJdkProxy();
// 测试CGLIB代理
testCglibProxy();
}
private static void warmup() {
UserService target = new UserServiceImpl();
UserService jdkProxy = createJdkProxy(target);
UserService cglibProxy = createCglibProxy(target);
for (int i = 0; i < 10000; i++) {
target.findById(1L);
jdkProxy.findById(1L);
cglibProxy.findById(1L);
}
}
private static void testDirectCall() {
UserService target = new UserServiceImpl();
long start = System.currentTimeMillis();
for (int i = 0; i < ITERATIONS; i++) {
target.findById(1L);
}
long duration = System.currentTimeMillis() - start;
System.out.println("直接调用耗时: " + duration + "ms");
}
private static void testJdkProxy() {
UserService target = new UserServiceImpl();
UserService proxy = createJdkProxy(target);
long start = System.currentTimeMillis();
for (int i = 0; i < ITERATIONS; i++) {
proxy.findById(1L);
}
long duration = System.currentTimeMillis() - start;
System.out.println("JDK代理耗时: " + duration + "ms");
}
private static void testCglibProxy() {
UserService target = new UserServiceImpl();
UserService proxy = createCglibProxy(target);
long start = System.currentTimeMillis();
for (int i = 0; i < ITERATIONS; i++) {
proxy.findById(1L);
}
long duration = System.currentTimeMillis() - start;
System.out.println("CGLIB代理耗时: " + duration + "ms");
}
@SuppressWarnings("unchecked")
private static UserService createJdkProxy(UserService target) {
return (UserService) Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
(proxy, method, args) -> method.invoke(target, args)
);
}
@SuppressWarnings("unchecked")
private static UserService createCglibProxy(UserService target) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(target.getClass());
enhancer.setCallback((MethodInterceptor) (obj, method, args, proxy) ->
proxy.invokeSuper(obj, args));
return (UserService) enhancer.create();
}
}
6.2 性能对比结果
markdown
典型测试结果(100万次调用)
┌──────────────┬──────────┬─────────┐
│ 调用方式 │ 耗时 │ 说明 │
├──────────────┼──────────┼─────────┤
│ 直接调用 │ ~50ms │ 基准 │
│ JDK代理 │ ~150ms │ 约3倍 │
│ CGLIB代理 │ ~120ms │ 约2.4倍 │
└──────────────┴──────────┴─────────┘
说明:
1. 现代JVM对JDK代理有优化
2. CGLIB在方法调用上略快(使用FastClass)
3. CGLIB创建代理类较慢(需要生成字节码)
4. 实际业务中,代理开销通常可忽略
七、最佳实践
7.1 选择代理方式
java
/**
* 代理方式选择建议
*/
/*
1. 优先使用JDK代理
- 面向接口编程
- Spring默认选择
- 更符合设计原则
2. 以下情况使用CGLIB代理
- 目标类没有实现接口
- 需要代理非接口方法
- 性能敏感场景
3. Spring Boot 2.x默认使用CGLIB
- spring.aop.proxy-target-class=true
*/
@Configuration
public class ProxyRecommendation {
// 推荐:面向接口编程
@Bean
public UserService userService() {
return new UserServiceImpl();
}
// 不推荐:直接注入实现类
@Bean
public UserServiceImpl userServiceImpl() {
return new UserServiceImpl();
}
}
7.2 避免常见问题
java
/**
* 代理常见问题及解决方案
*/
@Service
public class ProxyPitfalls {
/**
* 问题1:同类方法调用不走代理
*/
@Transactional
public void outerMethod() {
// ❌ 直接调用,不走代理
this.innerMethod();
}
@Transactional
public void innerMethod() {
// 事务不生效
}
// 解决方案1:注入自己
@Autowired
private ProxyPitfalls self;
public void outerMethodFixed1() {
self.innerMethod(); // ✓ 走代理
}
// 解决方案2:使用AopContext
public void outerMethodFixed2() {
((ProxyPitfalls) AopContext.currentProxy()).innerMethod(); // ✓
}
/**
* 问题2:final方法不能被代理
*/
public final void finalMethod() {
// CGLIB无法代理final方法
}
/**
* 问题3:private方法不能被代理
*/
private void privateMethod() {
// 任何代理都无法代理private方法
}
}
/**
* 启用AopContext
*/
@Configuration
@EnableAspectJAutoProxy(exposeProxy = true)
public class AopConfig {
}
7.3 调试代理
java
/**
* 代理调试技巧
*/
public class ProxyDebug {
public static void main(String[] args) {
// 1. 查看代理类名
Object proxy = createProxy();
System.out.println("代理类: " + proxy.getClass().getName());
// 2. 判断是否为代理对象
System.out.println("是JDK代理: " + Proxy.isProxyClass(proxy.getClass()));
System.out.println("是CGLIB代理: " + proxy.getClass().getName().contains("$$"));
// 3. 获取原始对象
if (AopUtils.isAopProxy(proxy)) {
try {
Object target = ((Advised) proxy).getTargetSource().getTarget();
System.out.println("目标对象: " + target.getClass().getName());
} catch (Exception e) {
e.printStackTrace();
}
}
// 4. 保存生成的代理类
// JDK代理
System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
// CGLIB代理
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "/tmp/cglib");
}
private static Object createProxy() {
// ...
return null;
}
}
八、总结
核心知识点回顾
swift
Spring动态代理核心要点
│
├── 代理模式基础
│ ├── 静态代理
│ └── 动态代理
│
├── JDK动态代理
│ ├── 基于接口
│ ├── Proxy + InvocationHandler
│ └── 限制:必须有接口
│
├── CGLIB代理
│ ├── 基于继承
│ ├── Enhancer + MethodInterceptor
│ └── 限制:不能代理final类/方法
│
├── Spring代理选择
│ ├── 默认JDK代理(有接口)
│ ├── 自动CGLIB代理(无接口)
│ └── 强制CGLIB代理(配置)
│
├── 实战应用
│ ├── 日志代理
│ ├── 重试代理
│ ├── 缓存代理
│ └── 权限代理
│
└── 最佳实践
├── 面向接口编程
├── 避免同类调用
├── 注意final/private方法
└── 合理选择代理方式
动态代理是Spring框架的核心技术,它使得AOP、事务管理、缓存等功能的实现成为可能。理解JDK动态代理和CGLIB代理的原理和区别,掌握正确的使用方法,能够帮助我们更好地使用Spring框架,并在需要时实现自定义的代理功能。