你是怎么理解 AOP 的?

 作者简介:大家好,我是码炫码哥,前中兴通讯、美团架构师,现任某互联网公司CTO,兼职码炫课堂主讲源码系列专题


代表作:《jdk源码&多线程&高并发》,《深入tomcat源码解析》,《深入netty源码解析》,《深入dubbo源码解析》,《深入springboot源码解析》,《深入spring源码解析》,《深入redis源码解析》等


联系qq:184480602,加我进群,大家一起学习,一起进步,一起对抗互联网寒冬。码炫课堂的个人空间-码炫码哥个人主页-面试,源码等

回答

AOP 是 Spring 中两大核心之一,它主要是用于解决代码中的横切关注点问题。简单来说就是,AOP 可以让我们将那些在多个模块中都可能会重复出现的功能,比如日志、事务管理、安全检查等功能,抽取出来统一管理,从而避免重复代码,减少耦合性。

AOP 是通过“切面”(Aspect)来实现的,“切面”就像一个切割点,它可以在不改变核心业务逻辑的情况下,将某些功能插入到目标方法的执行前、执行后或者抛出异常时等地方。

举个例子,比如我们有一个方法 saveOrder(),我们希望在执行该方法前后打印日志,而不是直接在 saveOrder() 里写日志逻辑。那么我们就可以通过 AOP 来定义一个 “切面”,让日志功能在执行 saveOrder() 方法之前和之后被自动调用。这就是AOP带来的好处,我们不需要修改 saveOrder() 的核心业务逻辑,就能实现额外的功能。

AOP 通常是通过动态代理实现的,有两种实现方式:基于 JDK 的动态代理和 CGLIB 动态代理。如果目标类实现了接口,AOP 默认会使用 JDK 动态代理;否则,会使用 CGLIB。

详解

AOP 的核心概念

要理解 AOP,就必须理解 AOP 的几个核心概念:

  • Aspect(切面)

切面代表了横切关注点的模块化,它由多个通知(Advice)和切入点(Pointcut)组成,我们可以这样理解:一个切面就是一段横切关注点的逻辑,它会被应用到目标对象的某些地方。

  • Join Point(连接点)

连接点是指程序执行的某一个具体位置,比如方法调用、异常抛出等。在 Spring AOP 中,连接点通常是指方法的执行点,因为 Spring AOP 只支持方法级别的连接点。

  • Advice(通知)

通知是定义在切面中的代码,它描述了切面在什么时候执行。Spring AOP 中,通知类型由多种:

*- 前置通知(Before Advice):在目标方法执行之前执行。

*- 后置通知(After Advice):在目标方法执行之后执行,无论是否抛出异常。

*- 返回通知(After Returning Advice):在目标方法成功返回之后执行。

*- 异常通知(After Throwing Advice):在目标方法抛出异常之后执行。

*- 环绕通知(Around Advice):在目标方法执行之前和执行之后都能执行,可以控制目标方法是否执行。

  • Pointcut(切入点)

切入点用于定义“切面”应该应用到哪些连接点。它通常使用表达式来匹配一组方法或者类,比如通过方法名、参数等来进行匹配。切入点决定了哪些方法会被拦截并应用切面。

  • Target Object(目标对象)

目标对象是指那些被切面功能增强的类。在 Spring AOP 中,目标对象可以是任何一个被代理的 Bean。

  • Weaving(织入)

织入是指将切面代码应用到目标对象的过程。这个过程可以在编译时、类加载时或者运行时进行。Spring AOP 是基于运行时的动态代理实现的。

举个例子吧!

@Aspect
@Component
public class TransferAspect {

    // 定义切点
    @Pointcut("execution(* TransferService.transfer(..))")
    public void transferMethod() {}

    // 前置通知:在转账前记录日志
    @Before("transferMethod()")
    public void beforeTransfer(JoinPoint joinPoint) {
        System.out.println("前置通知: " + joinPoint.getSignature().getName());
    }

    // 后置通知:在转账成功后记录日志
    @AfterReturning("transferMethod()")
    public void afterSuccessfulTransfer(JoinPoint joinPoint) {
        System.out.println("后置通知: " + joinPoint.getSignature().getName());
    }

    // 异常通知:在转账失败时处理异常
    @AfterThrowing(pointcut = "transferMethod()", throwing = "ex")
    public void handleTransferException(JoinPoint joinPoint, Exception ex) {
        System.out.println("异常通知: " + ex.getMessage());
        // 发送警告邮件的逻辑
    }
}

在这个类里面,包含了如下几个概念:

  • 切面TransferAspect ,这里它负责处理转账的日志记录和异常处理。
  • 切入点@Pointcut("execution(* TransferService.transfer(..))"),它指定了 TransferService 类中的 transfer() 方法为目标点。
  • 通知@Before("transferMethod()"),这个是前置通知,下面还有两个 后置返回通知(@AfterReturning)和 异常通知(@AfterThrowing
public class TransferService {

    public void transfer(String fromAccount, String toAccount, double amount) {
        // 转账逻辑
        System.out.println("Transferring " + amount + " from " + fromAccount + " to " + toAccount);
        
        // 模拟异常
        if (amount > 10000) {
            throw new RuntimeException("Transfer amount exceeds limit!");
        }
    }
}

这里包含的概念有:

  • 目标对象TransferService
  • 连接点:当方法执行到 transfer() 时,就会触发一个连接点,在这个连接点上,我们可以通过切点和通知来实现增强。

AOP 的实现原理

AOP 的实现原理是基于代理模式,它通过动态代理在运行时为目标对象生成代理对象,代理对象则负责在适当的时机执行增强逻辑,并调用目标对象的方法。在 Spring AOP 中,有两种主要的代理实现方式:

  • JDK 动态代理

JDK 动态代理是一种基于 Java 反射机制的代理方式,适用于为实现了接口的类创建代理对象。其实现原理是 Spring AOP 通过 JDK 提供的 java.lang.reflect.Proxy 类,动态地创建一个实现了相同接口的代理对象。代理对象通过实现 InvocationHandler 接口的 invoke 方法来控制对目标方法的调用。每次调用目标对象的方法时,都会先调用代理对象的 invoke 方法,这使得 AOP 可以在调用目标方法之前或之后插入通知逻辑。

  • CGLIB 动态代理(适用于没有实现接口的类)

CGLIB 是一种基于字节码生成的代理方式,适用于那些没有实现接口的类,它通过创建目标类的子类来实现代理,所以它不能代理final 类或 final 方法。

AOP 的运行机制

Spring 容器启动时,会扫描 @Aspect 注解,同时解析切面定义的 @Pointcut 和 @Advice 注解,将这些定义保存到 Spring 容器中。当某个 Bean 需要 AOP 增强时,Spring 会为其创建代理对象。然后,Spring 将该代理对象注入到依赖对象的 Bean 中,从而在目标对象方法执行时,触发切面的增强逻辑。

当调用目标对象的方法时,其实调用的是代理对象的方法,代理对象会检查该方法是否匹配某个切点表达式。如果匹配,执行通知。如果没有环绕通知拦截目标方法的执行,代理对象将调用目标对象的原始方法。方法执行完成后,执行后置通知(如 @After@AfterReturning@AfterThrowing 等)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值