🎭 Spring AOP:代理模式的双面人生!

34 阅读7分钟

副标题:JDK动态代理 vs CGLIB,谁更胜一筹?🎯


🎬 开场:AOP是什么?

面向切面编程

传统OOP(面向对象编程):
将功能封装成一个个对象

问题:
很多横切关注点(Cross-cutting Concerns)
散落在各个对象中

例如:日志、事务、权限、性能监控
┌───────────┐
│ UserService │ → 记录日志 📝
│  login()   │ → 开启事务 💾
│  register()│ → 权限校验 🔐
│  delete()  │ → 性能监控 ⏱️
└───────────┘

每个方法都要写这些重复代码 ❌

AOP(面向切面编程):
将横切关注点抽取出来,统一处理

┌───────────┐      ┌──────────┐
│ UserService │ ──→ │  日志切面 │
│  login()   │ ──→ │ 事务切面  │
│  register()│ ──→ │ 权限切面  │
│  delete()  │ ──→ │ 监控切面  │
└───────────┘      └──────────┘

代码清爽,关注点分离 ✅

📚 AOP核心概念

术语解释

1. 切面(Aspect)
   - 横切关注点的模块化
   - 例如:日志切面、事务切面
   
2. 连接点(Join Point)
   - 程序执行的某个点
   - 例如:方法调用、方法执行
   
3. 切点(Pointcut)
   - 匹配连接点的表达式
   - 例如:execution(* com.example.service.*.*(..))
   
4. 通知(Advice)
   - 在切点执行的动作
   - 类型:Before、After、Around、AfterReturning、AfterThrowing
   
5. 目标对象(Target Object)
   - 被代理的对象
   
6. 代理对象(Proxy Object)
   - AOP创建的对象
   
7. 织入(Weaving)
   - 将切面应用到目标对象的过程

通知类型

/**
 * 五种通知类型
 */
@Aspect
@Component
public class LogAspect {
    
    /**
     * 前置通知(Before)
     * 在目标方法执行之前执行
     */
    @Before("execution(* com.example.service.*.*(..))")
    public void before(JoinPoint joinPoint) {
        String methodName = joinPoint.getSignature().getName();
        System.out.println("【Before】方法执行前:" + methodName);
    }
    
    /**
     * 后置通知(After)
     * 在目标方法执行之后执行(无论成功还是异常)
     */
    @After("execution(* com.example.service.*.*(..))")
    public void after(JoinPoint joinPoint) {
        System.out.println("【After】方法执行后");
    }
    
    /**
     * 返回通知(AfterReturning)
     * 在目标方法成功返回后执行
     */
    @AfterReturning(
        pointcut = "execution(* com.example.service.*.*(..))",
        returning = "result"
    )
    public void afterReturning(JoinPoint joinPoint, Object result) {
        System.out.println("【AfterReturning】方法返回:" + result);
    }
    
    /**
     * 异常通知(AfterThrowing)
     * 在目标方法抛出异常后执行
     */
    @AfterThrowing(
        pointcut = "execution(* com.example.service.*.*(..))",
        throwing = "ex"
    )
    public void afterThrowing(JoinPoint joinPoint, Exception ex) {
        System.out.println("【AfterThrowing】方法异常:" + ex.getMessage());
    }
    
    /**
     * 环绕通知(Around)
     * 最强大的通知,可以控制目标方法是否执行
     */
    @Around("execution(* com.example.service.*.*(..))")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("【Around】方法执行前");
        
        long startTime = System.currentTimeMillis();
        
        try {
            // 执行目标方法
            Object result = joinPoint.proceed();
            
            long endTime = System.currentTimeMillis();
            System.out.println("【Around】方法执行后,耗时:" + (endTime - startTime) + "ms");
            
            return result;
            
        } catch (Throwable ex) {
            System.out.println("【Around】方法异常:" + ex.getMessage());
            throw ex;
        }
    }
}

执行顺序

正常流程:
┌────────────────────────────────────┐
│         Around - Before            │
│  ┌──────────────────────────────┐ │
│  │       Before                 │ │
│  │  ┌────────────────────────┐  │ │
│  │  │  目标方法执行          │  │ │
│  │  └────────────────────────┘  │ │
│  │       AfterReturning         │ │
│  └──────────────────────────────┘ │
│         Around - After             │
└────────────────────────────────────┘
           After

异常流程:
┌────────────────────────────────────┐
│         Around - Before            │
│  ┌──────────────────────────────┐ │
│  │       Before                 │ │
│  │  ┌────────────────────────┐  │ │
│  │  │  目标方法抛出异常      │  │ │
│  │  └────────────────────────┘  │ │
│  │       AfterThrowing          │ │
│  └──────────────────────────────┘ │
│         Around - Exception         │
└────────────────────────────────────┘
           After

🎯 JDK动态代理

原理

JDK动态代理:
- 基于接口
- 使用java.lang.reflect.Proxy
- 目标类必须实现接口
- 生成接口的实现类

原理图:
┌──────────────┐
│  Interface   │  ← 定义接口
└──────┬───────┘
       │
┌──────▼───────┐
│ 目标类实现   │  ← 实现接口
└──────────────┘
       │
       │ Spring生成代理
       ↓
┌──────────────┐
│  代理类实现   │  ← 也实现接口
└──────────────┘

调用链:
Client → Proxy → InvocationHandler → 目标方法

代码实现

/**
 * 接口
 */
public interface UserService {
    void addUser(String name);
    void deleteUser(Long id);
}

/**
 * 目标类(实现接口)
 */
public class UserServiceImpl implements UserService {
    
    @Override
    public void addUser(String name) {
        System.out.println("添加用户:" + name);
    }
    
    @Override
    public void deleteUser(Long id) {
        System.out.println("删除用户:" + id);
    }
}

/**
 * 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 {
        // 前置通知
        System.out.println("【前置】方法执行前:" + method.getName());
        
        // 执行目标方法
        Object result = method.invoke(target, args);
        
        // 后置通知
        System.out.println("【后置】方法执行后");
        
        return result;
    }
}

/**
 * 创建代理
 */
public class JdkProxyTest {
    public static void main(String[] args) {
        // 1. 创建目标对象
        UserService target = new UserServiceImpl();
        
        // 2. 创建InvocationHandler
        InvocationHandler handler = new LogInvocationHandler(target);
        
        // 3. 创建代理对象
        UserService proxy = (UserService) Proxy.newProxyInstance(
            target.getClass().getClassLoader(),  // 类加载器
            target.getClass().getInterfaces(),   // 接口列表
            handler                              // InvocationHandler
        );
        
        // 4. 使用代理对象
        proxy.addUser("张三");
        
        // 输出:
        // 【前置】方法执行前:addUser
        // 添加用户:张三
        // 【后置】方法执行后
    }
}

JDK动态代理的限制

限制1:必须有接口 ❌

// 没有接口的类
public class OrderService {
    public void createOrder() {
        System.out.println("创建订单");
    }
}

// JDK动态代理无法代理!❌

限制2:只能代理接口方法 ❌

public interface UserService {
    void publicMethod();  // 可以代理 ✅
}

public class UserServiceImpl implements UserService {
    
    @Override
    public void publicMethod() { }
    
    public void nonInterfaceMethod() { }  // 无法代理 ❌
}

🎯 CGLIB代理

原理

CGLIB(Code Generation Library):
- 基于继承
- 使用字节码技术
- 不需要接口
- 生成目标类的子类

原理图:
┌──────────────┐
│   目标类     │
└──────┬───────┘
       │
       │ CGLIB生成子类
       ↓
┌──────────────┐
│  代理子类    │  ← 继承目标类
└──────────────┘

调用链:
Client → Proxy子类 → MethodInterceptor → 目标方法

代码实现

/**
 * 目标类(不需要接口)
 */
public class OrderService {
    
    public void createOrder(Long userId) {
        System.out.println("创建订单:userId=" + userId);
    }
    
    public void cancelOrder(Long orderId) {
        System.out.println("取消订单:orderId=" + orderId);
    }
}

/**
 * MethodInterceptor(代理逻辑)
 */
public class LogMethodInterceptor implements MethodInterceptor {
    
    @Override
    public Object intercept(Object obj, Method method, Object[] args, 
                          MethodProxy proxy) throws Throwable {
        
        // 前置通知
        System.out.println("【前置】方法执行前:" + method.getName());
        
        // 执行目标方法(通过父类方法)
        Object result = proxy.invokeSuper(obj, args);
        
        // 后置通知
        System.out.println("【后置】方法执行后");
        
        return result;
    }
}

/**
 * 创建代理
 */
public class CglibProxyTest {
    public static void main(String[] args) {
        // 1. 创建Enhancer
        Enhancer enhancer = new Enhancer();
        
        // 2. 设置父类(目标类)
        enhancer.setSuperclass(OrderService.class);
        
        // 3. 设置回调
        enhancer.setCallback(new LogMethodInterceptor());
        
        // 4. 创建代理对象
        OrderService proxy = (OrderService) enhancer.create();
        
        // 5. 使用代理对象
        proxy.createOrder(1001L);
        
        // 输出:
        // 【前置】方法执行前:createOrder
        // 创建订单:userId=1001
        // 【后置】方法执行后
    }
}

CGLIB的限制

限制1:无法代理final类 ❌

public final class FinalService {
    public void method() { }
}

// CGLIB无法继承final类,代理失败!❌

限制2:无法代理final方法 ❌

public class UserService {
    
    public final void finalMethod() { }  // 无法代理 ❌
    
    public void normalMethod() { }  // 可以代理 ✅
}

限制3:无法代理private方法 ❌

public class UserService {
    
    private void privateMethod() { }  // 无法代理 ❌
    
    public void publicMethod() { }  // 可以代理 ✅
}

🆚 JDK vs CGLIB 对比

对比表

维度JDK动态代理CGLIB
实现方式接口实现子类继承
要求必须有接口不需要接口
性能⭐⭐⭐⭐⭐⭐⭐
限制只能代理接口方法不能代理final类/方法
字节码操作不需要需要(依赖CGLIB库)
代理对象实现接口继承目标类
Spring默认有接口时使用无接口时使用

性能对比

JDK 1.6之前:
CGLIB性能更好 ⭐⭐⭐⭐

JDK 1.6-1.8:
JDK动态代理优化,性能相当 ⭐⭐⭐⭐

JDK 1.8之后:
JDK动态代理更快 ⭐⭐⭐⭐⭐

结论:
现在JDK动态代理性能更好

🎯 Spring AOP的选择逻辑

选择规则

/**
 * Spring AOP代理选择逻辑
 * 
 * 源码位置:DefaultAopProxyFactory.createAopProxy()
 */
@Override
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
    
    // 条件1:配置强制使用CGLIB
    if (config.isOptimize() || 
        config.isProxyTargetClass() ||  // @EnableAspectJAutoProxy(proxyTargetClass = true)
        hasNoUserSuppliedProxyInterfaces(config)) {
        
        Class<?> targetClass = config.getTargetClass();
        
        if (targetClass == null) {
            throw new AopConfigException("TargetSource cannot determine target class");
        }
        
        // 条件2:目标类是接口,使用JDK动态代理
        if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
            return new JdkDynamicAopProxy(config);
        }
        
        // 条件3:使用CGLIB
        return new ObjenesisCglibAopProxy(config);
    }
    else {
        // 条件4:有接口,使用JDK动态代理
        return new JdkDynamicAopProxy(config);
    }
}

决策树

Spring AOP代理选择:

                  开始
                   │
         ┌─────────▼─────────┐
         │ 有接口吗?        │
         └─────┬────┬────────┘
               │    │
          有 ◀─┘    └─▶ 没有
           │              │
           ↓              ↓
    ┌─────────────┐   ┌────────────┐
    │强制CGLIB?  │   │  使用CGLIB │
    └──┬────┬─────┘   └────────────┘
       │    │
   是◀─┘    └─▶否
    │           │
    ↓           ↓
┌────────┐  ┌────────────┐
│ CGLIB  │  │ JDK动态代理│
└────────┘  └────────────┘

强制使用CGLIB

/**
 * 配置强制使用CGLIB
 */

// 方式1:@EnableAspectJAutoProxy注解
@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = true)  // 强制CGLIB
public class AppConfig {
}

// 方式2:XML配置
<aop:aspectj-autoproxy proxy-target-class="true"/>

// 方式3:SpringBoot配置
spring.aop.proxy-target-class=true

示例

/**
 * 示例1:有接口,默认JDK动态代理
 */
public interface UserService {
    void addUser(String name);
}

@Service
public class UserServiceImpl implements UserService {
    @Override
    public void addUser(String name) {
        System.out.println("添加用户:" + name);
    }
}

// Spring生成的代理:JDK动态代理 ✅
// 原因:有接口

/**
 * 示例2:无接口,自动使用CGLIB
 */
@Service
public class OrderService {
    public void createOrder() {
        System.out.println("创建订单");
    }
}

// Spring生成的代理:CGLIB ✅
// 原因:没有接口

/**
 * 示例3:有接口 + 强制CGLIB
 */
@EnableAspectJAutoProxy(proxyTargetClass = true)
@Configuration
public class AppConfig {
}

public interface ProductService {
    void addProduct(String name);
}

@Service
public class ProductServiceImpl implements ProductService {
    @Override
    public void addProduct(String name) {
        System.out.println("添加商品:" + name);
    }
}

// Spring生成的代理:CGLIB ✅
// 原因:配置强制使用CGLIB

💻 手写AOP

简化版实现

/**
 * 简化版AOP框架
 */
public class SimpleAopFramework {
    
    /**
     * 创建代理(自动选择JDK或CGLIB)
     */
    public static <T> T createProxy(T target, MethodInterceptor interceptor) {
        Class<?> targetClass = target.getClass();
        
        // 检查是否有接口
        Class<?>[] interfaces = targetClass.getInterfaces();
        
        if (interfaces.length > 0) {
            // 有接口,使用JDK动态代理
            return createJdkProxy(target, interfaces, interceptor);
        } else {
            // 无接口,使用CGLIB
            return createCglibProxy(target, interceptor);
        }
    }
    
    /**
     * JDK动态代理
     */
    @SuppressWarnings("unchecked")
    private static <T> T createJdkProxy(T target, Class<?>[] interfaces, 
                                       MethodInterceptor interceptor) {
        return (T) Proxy.newProxyInstance(
            target.getClass().getClassLoader(),
            interfaces,
            (proxy, method, args) -> interceptor.invoke(target, method, args)
        );
    }
    
    /**
     * CGLIB代理
     */
    @SuppressWarnings("unchecked")
    private static <T> T createCglibProxy(T target, MethodInterceptor interceptor) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(target.getClass());
        enhancer.setCallback(new net.sf.cglib.proxy.MethodInterceptor() {
            @Override
            public Object intercept(Object obj, Method method, Object[] args, 
                                  MethodProxy proxy) throws Throwable {
                return interceptor.invoke(target, method, args);
            }
        });
        return (T) enhancer.create();
    }
}

/**
 * 方法拦截器接口
 */
@FunctionalInterface
public interface MethodInterceptor {
    Object invoke(Object target, Method method, Object[] args) throws Throwable;
}

/**
 * 测试
 */
public class AopTest {
    
    public static void main(String[] args) {
        // 测试1:有接口(JDK动态代理)
        UserService userService = new UserServiceImpl();
        UserService proxy1 = SimpleAopFramework.createProxy(userService, 
            (target, method, args) -> {
                System.out.println("【JDK代理】方法执行前:" + method.getName());
                Object result = method.invoke(target, args);
                System.out.println("【JDK代理】方法执行后");
                return result;
            });
        
        proxy1.addUser("张三");
        System.out.println("代理类型:" + proxy1.getClass().getName());
        // 输出:com.sun.proxy.$Proxy0(JDK动态代理)
        
        // 测试2:无接口(CGLIB)
        OrderService orderService = new OrderService();
        OrderService proxy2 = SimpleAopFramework.createProxy(orderService,
            (target, method, args) -> {
                System.out.println("【CGLIB代理】方法执行前:" + method.getName());
                Object result = method.invoke(target, args);
                System.out.println("【CGLIB代理】方法执行后");
                return result;
            });
        
        proxy2.createOrder();
        System.out.println("代理类型:" + proxy2.getClass().getName());
        // 输出:OrderService$$EnhancerByCGLIB$$xxx(CGLIB代理)
    }
}

🎉 总结

核心要点

1. AOP实现方式
   - JDK动态代理:基于接口
   - CGLIB:基于继承

2. Spring选择逻辑
   - 有接口:JDK动态代理
   - 无接口:CGLIB
   - 强制CGLIB:配置proxyTargetClass=true

3. 性能
   - JDK 1.8+:JDK动态代理更快
   - 实际差异不大

4. 使用建议
   - 推荐定义接口(解耦 + JDK代理)
   - 无接口场景:CGLIB自动使用
   - 不用纠结性能,差异很小

记忆口诀

Spring AOP有两种,
JDK代理和CGLIB

JDK动态基于接口,
Proxy生成实现类
InvocationHandler拦截,
invoke方法做代理

CGLIB基于继承,
Enhancer生成子类
MethodInterceptor拦截,
intercept来代理

Spring选择有规则,
有接口用JDK,
没接口用CGLIB,
强制CGLIB可配置

JDK代理性能好,
字节码不用生成
CGLIB功能强,
不需接口也能用

final类和方法,
CGLIB无法代理
private方法也不行,
继承访问不到

推荐定义接口,
代码解耦更清晰
JDK代理是首选,
性能更好没问题!

愿你的代码面向切面,关注点完美分离! 🎭✨