Spring AOP(面向切面编程,Aspect-Oriented Programming)是 Spring 框架中用于实现横切关注点分离的一种机制。它允许开发者将那些影响多个类的通用功能(如日志记录、事务管理、安全性等)从业务逻辑中分离出来,从而提高代码的模块化和可维护性。

Spring AOP 的核心概念

  1. 切面(Aspect):包含横切关注点的类。通常通过注解或 XML 配置来定义。
  2. 连接点(Join Point):程序执行过程中可以插入横切逻辑的点。例如方法调用、异常抛出等。
  3. 通知(Advice):在特定连接点执行的动作。常见的通知类型有:
  • Before:在方法执行前运行。
  • After Returning:在方法成功返回后运行。
  • After Throwing:在方法抛出异常后运行。
  • After (Finally):无论方法是否抛出异常都会运行。
  • Around:环绕方法执行,在方法调用前后都可以运行自定义逻辑。
  1. 切入点(Pointcut):定义哪些连接点会被通知拦截。通常使用表达式语言来指定。
  2. 引入(Introduction):为现有类添加新方法或属性的能力。

示例:日志记录切面

假设我们有一个简单的服务类 UserService,其中包含几个业务方法。我们将创建一个切面来为这些方法添加日志记录功能。

1. 引入依赖

确保你的 pom.xmlbuild.gradle 文件中有 Spring AOP 和 AspectJ 的依赖:

<!-- Maven -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>
// Gradle
implementation 'org.springframework.boot:spring-boot-starter-aop'
2. 创建服务类
package com.example.demo.service;

import org.springframework.stereotype.Service;

@Service
public class UserService {

    public void addUser() {
        System.out.println("Adding a new user...");
    }

    public void updateUser() {
        System.out.println("Updating an existing user...");
    }

    public void deleteUser() {
        System.out.println("Deleting a user...");
    }
}
3. 创建切面类
package com.example.demo.aspect;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
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.springframework.stereotype.Component;

@Aspect
@Component
public class LoggingAspect {

    // Before advice: 在目标方法执行之前打印日志
    @Before("execution(* com.example.demo.service.UserService.*(..))")
    public void logBefore() {
        System.out.println("Logging before method execution.");
    }

    // After returning advice: 在目标方法成功返回之后打印日志
    @AfterReturning("execution(* com.example.demo.service.UserService.*(..))")
    public void logAfterReturning() {
        System.out.println("Logging after method returned successfully.");
    }

    // After throwing advice: 在目标方法抛出异常之后打印日志
    @AfterThrowing(pointcut = "execution(* com.example.demo.service.UserService.*(..))", throwing = "ex")
    public void logAfterThrowing(Exception ex) {
        System.out.println("Logging after method threw exception: " + ex.getMessage());
    }

    // Around advice: 环绕目标方法执行,可以在方法调用前后添加逻辑
    @Around("execution(* com.example.demo.service.UserService.*(..))")
    public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("Logging around method execution - before.");
        try {
            Object result = joinPoint.proceed(); // 执行目标方法
            System.out.println("Logging around method execution - after.");
            return result;
        } catch (Throwable throwable) {
            System.out.println("Logging around method execution - exception caught.");
            throw throwable;
        }
    }
}
4. 使用服务类
package com.example.demo;

import com.example.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class DemoApplication implements CommandLineRunner {

    @Autowired
    private UserService userService;

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }

    @Override
    public void run(String... args) throws Exception {
        userService.addUser();
        userService.updateUser();
        userService.deleteUser();
    }
}

运行结果

当你运行这个应用程序时,你会看到类似以下的日志输出:

Logging around method execution - before.
Logging before method execution.
Adding a new user...
Logging around method execution - after.
Logging after method returned successfully.

Logging around method execution - before.
Logging before method execution.
Updating an existing user...
Logging around method execution - after.
Logging after method returned successfully.

Logging around method execution - before.
Logging before method execution.
Deleting a user...
Logging around method execution - after.
Logging after method returned successfully.