Spring AOP在项目中的代码案例

1. 添加依赖

在 Maven 项目的 pom.xml 文件中添加 Spring AOP 的依赖:

<dependencies>
    <!-- Spring Boot Starter AOP -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-aop</artifactId>
    </dependency>
</dependencies>

2. 创建切面类

创建一个切面类,用于定义日志记录和性能监控的逻辑。

日志记录切面
package com.example.aspect;

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.Pointcut;
import org.aspectj.lang.JoinPoint;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class LoggingAspect {
    // 定义切点,匹配 com.example.service 包下所有类的所有方法
    @Pointcut("execution(* com.example.service.*.*(..))")
    public void serviceMethods() {}

    // 前置通知
    @Before("serviceMethods()")
    public void logBefore(JoinPoint joinPoint) {
        String methodName = joinPoint.getSignature().getName();
        String className = joinPoint.getTarget().getClass().getName();
        System.out.println("Before: Calling method " + methodName + " in class " + className);
    }

    // 后置通知
    @After("serviceMethods()")
    public void logAfter(JoinPoint joinPoint) {
        String methodName = joinPoint.getSignature().getName();
        System.out.println("After: Method " + methodName + " executed");
    }

    // 返回通知
    @AfterReturning(pointcut = "serviceMethods()", returning = "result")
    public void logAfterReturning(JoinPoint joinPoint, Object result) {
        String methodName = joinPoint.getSignature().getName();
        System.out.println("AfterReturning: Method " + methodName + " returned with result: " + result);
    }

    // 异常通知
    @AfterThrowing(pointcut = "serviceMethods()", throwing = "ex")
    public void logAfterThrowing(JoinPoint joinPoint, Throwable ex) {
        String methodName = joinPoint.getSignature().getName();
        System.out.println("AfterThrowing: Method " + methodName + " threw exception: " + ex.getMessage());
    }
}
性能监控切面
package com.example.aspect;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

import java.util.logging.Logger;

@Aspect
@Component
public class PerformanceMonitorAspect {
    private static final Logger logger = Logger.getLogger(PerformanceMonitorAspect.class.getName());

    // 定义切点,匹配 com.example.service 包下所有类的所有方法
    @Pointcut("execution(* com.example.service.*.*(..))")
    public void serviceMethods() {}

    // 环绕通知
    @Around("serviceMethods()")
    public Object profile(ProceedingJoinPoint pjp) throws Throwable {
        long startTime = System.currentTimeMillis();
        Object result = pjp.proceed(); // 调用目标方法
        long elapsedTime = System.currentTimeMillis() - startTime;
        logger.info("Method execution time: " + elapsedTime + " ms for method: " + pjp.getSignature().toShortString());
        return result;
    }
}

3. 创建服务类

创建一个简单的服务类,用于测试切面功能。

package com.example.service;

import org.springframework.stereotype.Service;

@Service
public class MyService {
    public String doSomething() {
        System.out.println("Executing doSomething method");
        return "Result";
    }
}

4. 创建控制器类

创建一个控制器类,用于触发服务类的方法调用。

package com.example.controller;

import com.example.service.MyService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class MyController {
    @Autowired
    private MyService myService;

    @GetMapping("/do-something")
    public String doSomething() {
        return myService.doSomething();
    }
}

5. 启动类

创建一个 Spring Boot 启动类,启动应用。

package com.example;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

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

6. 测试

启动 Spring Boot 应用后,访问 http://localhost:8080/do-something,观察控制台输出。

输出示例

控制台输出类似以下内容:

Before: Calling method doSomething in class com.example.service.MyService
Executing doSomething method
AfterReturning: Method doSomething returned with result: Result
After: Method doSomething executed
Method execution time: 10 ms for method: doSomething()

说明

  1. 日志记录切面

    • 使用 @Before@After@AfterReturning@AfterThrowing 注解,分别在方法执行前后、返回后和抛出异常后记录日志。

    • JoinPoint 提供了方法的上下文信息,如方法名、目标对象等。

  2. 性能监控切面

    • 使用 @Around 注解,记录方法的执行时间。

    • ProceedingJoinPoint 允许我们手动调用目标方法,并在调用前后插入逻辑。

  3. 切点表达式

    • execution(* com.example.service.*.*(..)) 表示匹配 com.example.service 包下所有类的所有方法。

  4. Spring AOP 的动态代理

    • Spring AOP 使用动态代理技术(JDK 动态代理或 CGLIB 代理)来创建代理对象。

    • 代理对象会拦截目标方法的调用,并在调用前后执行切面逻辑。

通过以上代码,你可以轻松地在 Spring Boot 项目中使用 Spring AOP 实现日志记录、性能监控等功能,而无需修改业务逻辑代码。