Spring AOP 实现方式

Spring AOP(面向切面编程)主要有以下几种实现方式:

1. 基于代理的 AOP 实现

  • JDK 动态代理
    • 原理:JDK 动态代理是基于接口的代理方式。它利用 Java 的反射机制,在运行时创建一个实现了目标对象所实现接口的代理对象。代理对象会拦截对目标对象方法的调用,并在调用前后执行额外的增强逻辑。
    • 使用场景:适用于目标对象实现了接口的情况。
    • 示例代码
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

// 接口
interface UserService {
    void addUser();
}

// 目标对象
class UserServiceImpl implements UserService {
    @Override
    public void addUser() {
        System.out.println("添加用户");
    }
}

// 调用处理器
class MyInvocationHandler implements InvocationHandler {
    private Object target;

    public MyInvocationHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("方法调用前");
        Object result = method.invoke(target, args);
        System.out.println("方法调用后");
        return result;
    }
}

public class JdkProxyExample {
    public static void main(String[] args) {
        UserService target = new UserServiceImpl();
        MyInvocationHandler handler = new MyInvocationHandler(target);
        UserService proxy = (UserService) Proxy.newProxyInstance(
                target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                handler
        );
        proxy.addUser();
    }
}
  • CGLIB 代理
    • 原理:CGLIB(Code Generation Library)是一个强大的、高性能的代码生成库。CGLIB 代理通过继承目标对象类,在运行时生成一个子类作为代理对象。它会重写目标对象的方法,在方法调用前后插入增强逻辑。
    • 使用场景:适用于目标对象没有实现接口的情况。
    • 示例代码
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

// 目标对象
class UserService {
    public void addUser() {
        System.out.println("添加用户");
    }
}

// 方法拦截器
class MyMethodInterceptor implements MethodInterceptor {
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("方法调用前");
        Object result = proxy.invokeSuper(obj, args);
        System.out.println("方法调用后");
        return result;
    }
}

public class CglibProxyExample {
    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(UserService.class);
        enhancer.setCallback(new MyMethodInterceptor());
        UserService proxy = (UserService) enhancer.create();
        proxy.addUser();
    }
}

2. 基于 AspectJ 的 AOP 实现

  • 原理:AspectJ 是一个功能强大的 AOP 框架,Spring AOP 集成了 AspectJ 的部分功能。它可以通过注解或 XML 配置来定义切面、切点和增强逻辑。与基于代理的 AOP 不同,AspectJ 可以在编译时、类加载时或运行时织入增强逻辑。
  • 使用场景:当需要更复杂的切面定义和更细粒度的织入控制时使用。
  • 示例代码(注解方式)
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

// 切面类
@Aspect
@Component
public class LoggingAspect {
    // 定义切点
    @Pointcut("execution(* com.example.service.*.*(..))")
    public void serviceMethods() {}

    // 前置增强
    @Before("serviceMethods()")
    public void beforeAdvice() {
        System.out.println("方法调用前");
    }

    // 后置增强
    @After("serviceMethods()")
    public void afterAdvice() {
        System.out.println("方法调用后");
    }
}

动态代理(JDK 动态代理)和 CGLIB 代理的区别

1. 代理机制

  • JDK 动态代理:基于接口实现,代理对象实现了目标对象所实现的接口,通过反射调用目标对象的方法。
  • CGLIB 代理:基于继承实现,代理对象是目标对象的子类,通过重写目标对象的方法来实现增强逻辑。

2. 适用场景

  • JDK 动态代理:要求目标对象必须实现接口,只能代理接口中定义的方法。
  • CGLIB 代理:不要求目标对象实现接口,可以代理普通类的方法。但如果目标对象的方法是finalstatic的,CGLIB 无法对其进行代理。

3. 性能

  • JDK 动态代理:在创建代理对象时,由于使用反射机制,性能相对较低。但在方法调用时,性能较好。
  • CGLIB 代理:在创建代理对象时,由于需要生成子类字节码,性能相对较高。但在方法调用时,由于需要通过方法代理调用父类方法,性能相对较低。

4. 代码复杂度

  • JDK 动态代理:代码相对简单,只需要实现InvocationHandler接口即可。
  • CGLIB 代理:代码相对复杂,需要引入 CGLIB 库,并实现MethodInterceptor接口。