微信登录

AOP 配置 - 注解配置 AOP - 使用注解定义切面

Java - Web - Spring 《AOP 配置 - 注解配置 AOP - 使用注解定义切面》

一、引言

在软件开发中,我们经常会遇到一些与业务核心逻辑无关,但又贯穿于多个业务模块的功能,比如日志记录、事务管理、权限验证等。面向切面编程(AOP)就是为了解决这类问题而诞生的一种编程范式。在 Spring 框架中,我们可以通过注解的方式很方便地实现 AOP 功能,下面我们就来详细介绍如何使用注解定义切面。

二、AOP 基本概念

在深入讲解注解配置 AOP 之前,我们先了解一些 AOP 的基本概念:
| 概念 | 解释 |
| —— | —— |
| 切面(Aspect) | 一个横切关注点(如日志记录)的模块化,它将那些影响多个类的行为封装到一个可重用的模块中。 |
| 连接点(Join Point) | 程序执行过程中的某个特定位置,如方法调用、异常抛出等。 |
| 切点(Pointcut) | 一组连接点的集合,用于定义哪些连接点会被增强。 |
| 通知(Advice) | 在切点处执行的代码,根据执行时机的不同,可分为前置通知、后置通知、环绕通知等。 |
| 织入(Weaving) | 将切面应用到目标对象并创建新的代理对象的过程。 |

三、环境准备

首先,我们需要创建一个 Spring 项目,并添加相关依赖。如果你使用的是 Maven 项目,可以在 pom.xml 中添加以下依赖:

  1. <dependencies>
  2. <!-- Spring Context -->
  3. <dependency>
  4. <groupId>org.springframework</groupId>
  5. <artifactId>spring-context</artifactId>
  6. <version>5.3.18</version>
  7. </dependency>
  8. <!-- Spring AOP -->
  9. <dependency>
  10. <groupId>org.springframework</groupId>
  11. <artifactId>spring-aop</artifactId>
  12. <version>5.3.18</version>
  13. </dependency>
  14. <!-- AspectJ -->
  15. <dependency>
  16. <groupId>org.aspectj</groupId>
  17. <artifactId>aspectjweaver</artifactId>
  18. <version>1.9.7</version>
  19. </dependency>
  20. </dependencies>

四、使用注解定义切面的步骤

1. 创建目标对象

首先,我们创建一个简单的业务类作为目标对象:

  1. package com.example.demo;
  2. public class UserService {
  3. public void addUser(String username) {
  4. System.out.println("Adding user: " + username);
  5. }
  6. }

2. 创建切面类

接下来,我们创建一个切面类,使用注解来定义切面、切点和通知:

  1. package com.example.demo;
  2. import org.aspectj.lang.annotation.After;
  3. import org.aspectj.lang.annotation.Aspect;
  4. import org.aspectj.lang.annotation.Before;
  5. import org.aspectj.lang.annotation.Pointcut;
  6. import org.springframework.stereotype.Component;
  7. @Aspect
  8. @Component
  9. public class LoggingAspect {
  10. // 定义切点
  11. @Pointcut("execution(* com.example.demo.UserService.addUser(..))")
  12. public void addUserPointcut() {}
  13. // 前置通知
  14. @Before("addUserPointcut()")
  15. public void beforeAddUser() {
  16. System.out.println("Before adding user...");
  17. }
  18. // 后置通知
  19. @After("addUserPointcut()")
  20. public void afterAddUser() {
  21. System.out.println("After adding user...");
  22. }
  23. }

在上面的代码中:

  • @Aspect 注解表示这是一个切面类。
  • @Component 注解将该类纳入 Spring 容器的管理。
  • @Pointcut 注解定义了一个切点,这里使用了 AspectJ 的表达式 execution(* com.example.demo.UserService.addUser(..)),表示匹配 UserService 类中的 addUser 方法。
  • @Before 注解表示前置通知,在目标方法执行之前执行。
  • @After 注解表示后置通知,在目标方法执行之后执行。

3. 配置 Spring 启用 AOP 注解

我们需要在 Spring 配置类中启用 AOP 注解支持:

  1. package com.example.demo;
  2. import org.springframework.context.annotation.ComponentScan;
  3. import org.springframework.context.annotation.Configuration;
  4. import org.springframework.context.annotation.EnableAspectJAutoProxy;
  5. @Configuration
  6. @ComponentScan(basePackages = "com.example.demo")
  7. @EnableAspectJAutoProxy
  8. public class AppConfig {
  9. }

@EnableAspectJAutoProxy 注解用于启用 AspectJ 自动代理功能。

4. 测试代码

最后,我们编写一个测试类来验证 AOP 功能:

  1. package com.example.demo;
  2. import org.springframework.context.annotation.AnnotationConfigApplicationContext;
  3. public class Main {
  4. public static void main(String[] args) {
  5. AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
  6. UserService userService = context.getBean(UserService.class);
  7. userService.addUser("John");
  8. context.close();
  9. }
  10. }

5. 运行结果

运行 Main 类,输出结果如下:

  1. Before adding user...
  2. Adding user: John
  3. After adding user...

从输出结果可以看出,在调用 UserServiceaddUser 方法前后,分别执行了前置通知和后置通知。

五、其他类型的通知

除了前置通知和后置通知,Spring AOP 还支持其他类型的通知:
| 通知类型 | 注解 | 说明 |
| —— | —— | —— |
| 前置通知 | @Before | 在目标方法执行之前执行。 |
| 后置通知 | @After | 在目标方法执行之后执行,无论目标方法是否抛出异常。 |
| 返回通知 | @AfterReturning | 在目标方法正常返回后执行。 |
| 异常通知 | @AfterThrowing | 在目标方法抛出异常后执行。 |
| 环绕通知 | @Around | 环绕目标方法执行,可以在目标方法执行前后进行增强。 |

下面是一个环绕通知的示例:

  1. package com.example.demo;
  2. import org.aspectj.lang.ProceedingJoinPoint;
  3. import org.aspectj.lang.annotation.Around;
  4. import org.aspectj.lang.annotation.Aspect;
  5. import org.springframework.stereotype.Component;
  6. @Aspect
  7. @Component
  8. public class AroundLoggingAspect {
  9. @Around("execution(* com.example.demo.UserService.addUser(..))")
  10. public Object aroundAddUser(ProceedingJoinPoint joinPoint) throws Throwable {
  11. System.out.println("Around advice: Before method execution");
  12. Object result = joinPoint.proceed();
  13. System.out.println("Around advice: After method execution");
  14. return result;
  15. }
  16. }

六、总结

通过使用注解配置 AOP,我们可以很方便地将横切关注点模块化,提高代码的可维护性和可复用性。在实际开发中,我们可以根据不同的需求选择合适的通知类型和切点表达式,实现更加灵活和强大的 AOP 功能。

以上就是关于使用注解定义切面的详细介绍,希望对你有所帮助。