spring boot 中如何进行 AOP 面向切面编程?

154 阅读2分钟

面向切面编程(Aspect-Oriented Programming, AOP)是 Spring Framework 的核心功能之一。它允许你将横切关注点(如日志记录、事务管理、权限控制等)从业务逻辑中分离出来,使代码更加模块化和易于维护。下面是如何在 Spring Boot 项目中实现 AOP 的基本步骤:

1. 添加依赖

首先,在 pom.xml 文件中添加 Spring AOP 相关的依赖:

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

2. 定义切面(Aspect)

创建一个切面类,使用 @Aspect 注解标识这个类为切面,并使用 @Component 注解将其注册为 Spring Bean。

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.springframework.stereotype.Component;

@Aspect
@Component
public class LoggingAspect {

    @Before("execution(* com.example.service.*.*(..))")
    public void logBefore() {
        System.out.println("LoggingAspect.logBefore() : Before method execution");
    }

    @After("execution(* com.example.service.*.*(..))")
    public void logAfter() {
        System.out.println("LoggingAspect.logAfter() : After method execution");
    }

    @AfterReturning(pointcut = "execution(* com.example.service.*.*(..))", returning = "result")
    public void logAfterReturning(Object result) {
        System.out.println("LoggingAspect.logAfterReturning() : After method returns: " + result);
    }

    @AfterThrowing(pointcut = "execution(* com.example.service.*.*(..))", throwing = "error")
    public void logAfterThrowing(Throwable error) {
        System.out.println("LoggingAspect.logAfterThrowing() : Exception: " + error);
    }

    @Around("execution(* com.example.service.*.*(..))")
    public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("LoggingAspect.logAround() : Before method execution");
        Object result = joinPoint.proceed();
        System.out.println("LoggingAspect.logAround() : After method execution");
        return result;
    }
}

3. 定义切点(Pointcut)

切点定义了一个或多个连接点(Join Point),这些连接点表示在程序执行时某个特定点的执行。例如,上面示例中的 "execution(* com.example.service.*.*(..))" 是一个切点表达式,表示 com.example.service 包下的所有类的所有方法。

切点表达式的一些常见语法:

  • execution(* com.example.service.*.*(..)):表示匹配 com.example.service 包下所有类的所有方法。
  • execution(public * *(..)):表示匹配所有公共方法。
  • execution(* com.example.service.*.save*(..)):表示匹配 com.example.service 包下以 save 开头的方法。
  • within(com.example.service..*):表示匹配 com.example.service 包及其子包中的所有类。

4. 使用切面

定义了切面之后,它们会自动应用于 Spring 容器中的目标对象。例如:

package com.example.service;

import org.springframework.stereotype.Service;

@Service
public class MyService {

    public void performTask() {
        System.out.println("MyService.performTask() : Performing task");
    }

    public String getTaskStatus() {
        System.out.println("MyService.getTaskStatus() : Getting task status");
        return "Task Completed";
    }

    public void throwException() {
        System.out.println("MyService.throwException() : Throwing exception");
        throw new RuntimeException("Exception in MyService");
    }
}

5. 测试

编写一个测试类来验证 AOP 的功能:

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

@SpringBootApplication
public class AopExampleApplication implements CommandLineRunner {

    @Autowired
    private MyService myService;

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

    @Override
    public void run(String... args) throws Exception {
        myService.performTask();
        myService.getTaskStatus();
        try {
            myService.throwException();
        } catch (Exception e) {
            // Handle exception
        }
    }
}

运行应用程序后,你应该能看到控制台输出的日志,显示 AOP 切面在方法执行前、执行后、返回后以及抛出异常时的日志。