面向切面编程(Aspect-Oriented Programming,AOP)是 Spring 框架的核心特性之一。它允许开发者将横切关注点(如日志记录、事务管理、权限控制等)从业务逻辑中分离出来,实现代码的模块化和可复用。本文将深入剖析 Spring AOP 的底层实现原理,重点讲解 JDK 动态代理和 CGLIB 两种代理机制的工作方式、差异以及适用场景。
在深入源码之前,我们先快速回顾一下 AOP 的几个核心概念:
| 概念 | 说明 |
|---|---|
| Aspect(切面) | 横切关注点的模块化,包含通知和切点 |
| Join Point(连接点) | 程序执行过程中的某个特定点,如方法调用 |
| Pointcut(切点) | 匹配连接点的表达式,定义通知何时触发 |
| Advice(通知) | 切面的具体行为,如何增强目标方法 |
| Target(目标对象) | 被代理的原始对象 |
| Proxy(代理对象) | AOP 框架生成的增强后的对象 |
Spring AOP 默认采用代理模式实现,提供了两种代理机制:
JDK 动态代理是 Java 原生支持的代理机制,要求目标类必须实现至少一个接口。
// 定义业务接口
public interface UserService {
void addUser(String username);
void deleteUser(String username);
}
// 实现类
public class UserServiceImpl implements UserService {
@Override
public void addUser(String username) {
System.out.println("添加用户: " + username);
}
@Override
public void deleteUser(String username) {
System.out.println("删除用户: " + username);
}
}
// 使用 JDK 动态代理创建代理对象
public class JdkProxyFactory {
public static <T> T createProxy(T target) {
return (T) Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("[JDK代理] 方法调用前: " + method.getName());
// 执行目标方法
Object result = method.invoke(target, args);
System.out.println("[JDK代理] 方法调用后: " + method.getName());
return result;
}
}
);
}
}
// 使用
UserService userService = new UserServiceImpl();
UserService proxy = JdkProxyFactory.createProxy(userService);
proxy.addUser("张三"); // 通过代理对象调用
JDK 动态代理的核心是 java.lang.reflect.Proxy 类和 InvocationHandler 接口:
Proxy,实现了目标接口invoke() 方法生成的代理类大致结构如下:
public final class $Proxy0 extends Proxy implements UserService {
private InvocationHandler h;
public void addUser(String username) {
h.invoke(this, m3, new Object[]{username});
}
// ...
}
CGLIB(Code Generation Library)是一个基于 ASM 字节码操作库,可以代理没有实现接口的类。
public class OrderService {
public void createOrder(String orderId) {
System.out.println("创建订单: " + orderId);
}
public void cancelOrder(String orderId) {
System.out.println("取消订单: " + orderId);
}
}
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
public class CglibProxyFactory {
public static <T> T createProxy(Class<T> targetClass) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(targetClass); // 设置目标类为父类
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("[CGLIB代理] 方法调用前: " + method.getName());
// 调用父类(目标类)的方法
Object result = proxy.invokeSuper(obj, args);
System.out.println("[CGLIB代理] 方法调用后: " + method.getName());
return result;
}
});
return (T) enhancer.create();
}
}
// 使用
OrderService proxy = CglibProxyFactory.createProxy(OrderService.class);
proxy.createOrder("ORD-001");
CGLIB 通过生成目标类的子类来实现代理:
生成的代理类大致结构:
public class OrderService$$EnhancerByCGLIB extends OrderService {
private MethodInterceptor callback;
public void createOrder(String orderId) {
callback.intercept(this, method, new Object[]{orderId}, proxy);
}
}
Spring 框架通过 DefaultAopProxyFactory 决定使用哪种代理:
public class DefaultAopProxyFactory implements AopProxyFactory {
@Override
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
// 检查条件,决定是否使用 CGLIB
if (config.isOptimize() || config.isProxyTargetClass()
|| hasNoUserSuppliedProxyInterfaces(config)) {
Class<?> targetClass = config.getTargetClass();
if (targetClass == null) {
throw new AopConfigException("TargetSource cannot determine target class");
}
// 目标类是接口或已经是代理类,使用 JDK 动态代理
if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
return new JdkDynamicAopProxy(config);
}
// 否则使用 CGLIB
return new ObjenesisCglibAopProxy(config);
}
else {
// 默认使用 JDK 动态代理
return new JdkDynamicAopProxy(config);
}
}
}
| 条件 | 代理方式 |
|---|---|
| 目标类实现了接口(默认配置) | JDK 动态代理 |
| 目标类没有实现接口 | CGLIB 代理 |
设置 proxyTargetClass=true | 强制使用 CGLIB |
| 目标类是接口 | JDK 动态代理 |
// 方式1:注解配置
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class AppConfig {}
// 方式2:XML 配置
<aop:aspectj-autoproxy proxy-target-class="true"/>
// 方式3:Spring Boot 配置
spring.aop.proxy-target-class=true
| 特性 | JDK 动态代理 | CGLIB 代理 |
|---|---|---|
| 实现方式 | 实现接口 | 继承目标类 |
| 要求 | 目标类必须实现接口 | 目标类不能是 final |
| 方法限制 | 只能代理接口方法 | 不能代理 final/static 方法 |
| 性能 | 反射调用,稍慢 | 使用 MethodProxy,较快 |
| 启动速度 | 较快 | 需要生成字节码,较慢 |
| 依赖 | JDK 内置 | 需要 CGLIB 库 |
@Aspect
@Component
public class LoggingAspect {
@Pointcut("execution(* com.example.service.*.*(..))")
public void serviceMethods() {}
@Around("serviceMethods()")
public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
long start = System.currentTimeMillis();
String methodName = joinPoint.getSignature().getName();
log.info("[开始] 方法: {}, 参数: {}", methodName, Arrays.toString(joinPoint.getArgs()));
try {
Object result = joinPoint.proceed();
long duration = System.currentTimeMillis() - start;
log.info("[结束] 方法: {}, 耗时: {}ms, 返回: {}", methodName, duration, result);
return result;
} catch (Exception e) {
log.error("[异常] 方法: {}, 错误: {}", methodName, e.getMessage());
throw e;
}
}
}
@Transactional(rollbackFor = Exception.class)
public void transferMoney(String fromAccount, String toAccount, BigDecimal amount) {
// Spring 使用 AOP 代理实现事务的开启、提交和回滚
debit(fromAccount, amount);
credit(toAccount, amount);
}
@Aspect
@Component
public class PerformanceAspect {
@Around("@annotation(PerformanceLog)")
public Object measurePerformance(ProceedingJoinPoint joinPoint) throws Throwable {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
Object result = joinPoint.proceed();
stopWatch.stop();
// 记录到 Micrometer/Prometheus
Metrics.timer("method.execution",
"class", joinPoint.getTarget().getClass().getSimpleName(),
"method", joinPoint.getSignature().getName())
.record(stopWatch.getTotalTimeMillis(), TimeUnit.MILLISECONDS);
return result;
}
}
@Service
public class UserService {
public void methodA() {
// 这里调用 methodB 不会经过代理!
methodB(); // 直接调用,AOP 失效
}
@Transactional
public void methodB() {
// 事务不会生效
}
}
解决方案:
@Service
public class UserService {
@Autowired
private ApplicationContext context;
public void methodA() {
// 通过代理对象调用
UserService proxy = context.getBean(UserService.class);
proxy.methodB();
}
@Transactional
public void methodB() {
// 事务生效
}
}
@Service
public class OrderService {
// CGLIB 无法代理 final 方法
public final void processOrder() {
// 通知不会生效
}
}
AOP 代理对象的创建可能引发循环依赖,Spring 通过三级缓存机制解决,但在构造器注入时仍可能出现问题。
proxyTargetClass,但要注意 final 方法的限制Spring AOP 通过 JDK 动态代理和 CGLIB 两种方式为开发者提供了强大的横切关注点处理能力。理解这两种代理机制的原理和差异,有助于我们:
JDK 动态代理基于接口实现,符合面向接口编程的设计理念;CGLIB 通过继承实现,为没有接口的类提供了 AOP 支持。在实际项目中,应根据具体需求和约束条件选择合适的代理方式。
本文深入剖析了 Spring AOP 的底层实现原理,希望能帮助你更好地理解和使用这一强大的特性。如有疑问或建议,欢迎在评论区留言讨论。