Skip to content

Spring AOP

标签
SpringBoot
AOP
字数
953 字
阅读时间
5 分钟

1 AOP 概念

面向切面编程(Aspect-Oriented Programming, AOP)是一种编程范式,旨在通过分离关注点(如日志记录、安全性、事务管理等)来增强代码的可重用性和可维护性。AOP 允许开发者在不修改原始代码的情况下,为现有代码添加新的行为。

2 Spring AOP 组件

  • 切面(Aspect):切面是 AOP 的核心概念,它是一个模块,包含切点和通知的定义,用于定义横切关注点。

  • 切点(Pointcut):切点是一个表达式,定义了在哪些连接点(如方法调用)上应用通知。可以使用表达式语言(如 AspectJ 表达式)来精确控制切点的范围和执行时机。例如,可以指定某个包下的所有方法,或者特定名称的方法。使用 @Pointcut 注解可以定义一个可复用的切点,以便在多个通知中引用,避免重复代码。

  • 通知(Advice):通知是切面中定义的在切点处执行的代码,主要有以下几种类型:

    • 前置通知(Before):在目标方法执行之前执行。
    • 后置通知(After):在目标方法执行之后执行(无论方法是否成功)。
    • 返回通知(After Returning):在目标方法成功执行后执行。
    • 异常通知(After Throwing):在目标方法抛出异常后执行。
    • 环绕通知(Around):在目标方法执行前后都可以执行,能够控制目标方法的执行。
  • 连接点(Joinpoint):连接点是程序执行的特定点,通常是方法调用。AOP 框架会在连接点上执行通知。连接点可以是方法调用、异常抛出等,具体取决于定义的切点。

  • 目标对象(Target Object):目标对象是被切面增强的对象。通常是一个 Spring 管理的 bean,AOP 会在目标对象的方法执行前后插入通知逻辑。目标对象可以是任何被 Spring 管理的 bean,切面将对其进行增强。

3 引入依赖

在你的 pom.xml 中添加 Spring AOP 依赖:

INFO

AOP 已包含于 spring-boot-starter

xml
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

4 创建切面

创建一个切面类,定义切点和通知:

java
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.ProceedingJoinPoint;
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 logBeforeMethod() {
        System.out.println("方法执行前的日志记录…");
    }

    // 后置通知
    @After("serviceMethods()")
    public void logAfterMethod() {
        System.out.println("方法执行后的日志记录…");
    }

    // 返回通知
    @AfterReturning(pointcut = "serviceMethods()", returning = "result")
    public void logAfterReturning(Object result) {
        System.out.println("方法返回值: " + result);
    }

    // 异常通知
    @AfterThrowing(pointcut = "serviceMethods()", throwing = "ex")
    public void logAfterThrowing(Exception ex) {
        System.out.println("方法抛出异常: " + ex.getMessage());
    }

    // 环绕通知
    @Around("serviceMethods()")
    public Object logAroundMethod(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("方法执行前…");
        Object result = joinPoint.proceed(); // 执行目标方法
        System.out.println("方法执行后…");
        return result;
    }
}

5 使用切面

在你的服务类中,定义一些方法以供切面增强:

java
import org.springframework.stereotype.Service;

@Service
public class MyService {

    public String performTask() {
        System.out.println("执行任务…");
        return "任务完成";
    }

    public void throwException() {
        throw new RuntimeException("这是一个异常");
    }
}

6 测试切面

在主应用类或测试类中调用服务方法:

java
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;

@Component
public class AppRunner implements CommandLineRunner {

    private final MyService myService;

    public AppRunner(MyService myService) {
        this.myService = myService;
    }

    @Override
    public void run(Stringargs) {
        myService.performTask();
        try {
            myService.throwException();
        } catch (Exception e) {
            // 捕获异常以避免程序终止
        }
    }
}