微信登录

切面实现 - 自定义切面 - 编写切面类

Java - Web - Spring 《切面实现 - 自定义切面 - 编写切面类》

一、引言

在软件开发中,我们常常会遇到一些横切关注点,例如日志记录、事务管理、权限验证等。这些功能与业务逻辑本身并无直接关联,但却需要在多个业务模块中重复实现。Spring AOP(面向切面编程)提供了一种优雅的解决方案,它允许我们将这些横切关注点从业务逻辑中分离出来,以一种非侵入式的方式进行统一管理。本文将详细介绍如何在 Spring 框架中编写自定义切面类来实现 AOP 功能。

二、Spring AOP 基本概念

在深入编写自定义切面类之前,我们需要了解一些 Spring AOP 的基本概念:

  • 切面(Aspect):一个切面是一个包含了通知和切入点的类,它定义了横切关注点的实现。
  • 通知(Advice):通知是切面在特定连接点上执行的操作,常见的通知类型包括前置通知、后置通知、环绕通知等。
  • 切入点(Pointcut):切入点定义了哪些连接点会被通知所影响,它可以通过方法签名、类名等条件来匹配。
  • 连接点(Join Point):连接点是程序执行过程中的一个点,例如方法调用、异常抛出等。

三、编写自定义切面类的步骤

1. 添加依赖

首先,我们需要在项目中添加 Spring AOP 的依赖。如果使用 Maven,可以在 pom.xml 中添加以下依赖:

  1. <dependencies>
  2. <!-- Spring AOP -->
  3. <dependency>
  4. <groupId>org.springframework.boot</groupId>
  5. <artifactId>spring-boot-starter-aop</artifactId>
  6. </dependency>
  7. </dependencies>

2. 创建业务类

我们先创建一个简单的业务类,模拟一个用户服务:

  1. package com.example.demo.service;
  2. import org.springframework.stereotype.Service;
  3. @Service
  4. public class UserService {
  5. public String getUserInfo(String userId) {
  6. System.out.println("获取用户信息,用户 ID:" + userId);
  7. return "用户信息:ID=" + userId;
  8. }
  9. }

3. 编写自定义切面类

接下来,我们编写一个自定义切面类,实现日志记录的功能:

  1. package com.example.demo.aspect;
  2. import org.aspectj.lang.JoinPoint;
  3. import org.aspectj.lang.annotation.After;
  4. import org.aspectj.lang.annotation.Aspect;
  5. import org.aspectj.lang.annotation.Before;
  6. import org.aspectj.lang.annotation.Pointcut;
  7. import org.springframework.stereotype.Component;
  8. @Aspect
  9. @Component
  10. public class LoggingAspect {
  11. // 定义切入点,匹配 UserService 类中的所有方法
  12. @Pointcut("execution(* com.example.demo.service.UserService.*(..))")
  13. public void userServiceMethods() {}
  14. // 前置通知,在目标方法执行之前执行
  15. @Before("userServiceMethods()")
  16. public void beforeUserServiceMethod(JoinPoint joinPoint) {
  17. String methodName = joinPoint.getSignature().getName();
  18. System.out.println("Before method: " + methodName);
  19. }
  20. // 后置通知,在目标方法执行之后执行
  21. @After("userServiceMethods()")
  22. public void afterUserServiceMethod(JoinPoint joinPoint) {
  23. String methodName = joinPoint.getSignature().getName();
  24. System.out.println("After method: " + methodName);
  25. }
  26. }

在上述代码中:

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

4. 配置 Spring AOP

如果使用 Spring Boot,AOP 会自动配置。如果使用传统的 Spring 配置,需要在配置文件中启用 AOP 自动代理:

  1. <aop:aspectj-autoproxy/>

5. 测试代码

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

  1. package com.example.demo;
  2. import com.example.demo.service.UserService;
  3. import org.springframework.beans.factory.annotation.Autowired;
  4. import org.springframework.boot.CommandLineRunner;
  5. import org.springframework.boot.SpringApplication;
  6. import org.springframework.boot.autoconfigure.SpringBootApplication;
  7. @SpringBootApplication
  8. public class DemoApplication implements CommandLineRunner {
  9. @Autowired
  10. private UserService userService;
  11. public static void main(String[] args) {
  12. SpringApplication.run(DemoApplication.class, args);
  13. }
  14. @Override
  15. public void run(String... args) throws Exception {
  16. userService.getUserInfo("123");
  17. }
  18. }

6. 运行结果

运行测试类,输出结果如下:

  1. Before method: getUserInfo
  2. 获取用户信息,用户 ID123
  3. After method: getUserInfo

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

四、常见通知类型总结

通知类型 注解 说明
前置通知 @Before 在目标方法执行之前执行
后置通知 @After 在目标方法执行之后执行,无论目标方法是否抛出异常
返回通知 @AfterReturning 在目标方法正常返回后执行
异常通知 @AfterThrowing 在目标方法抛出异常后执行
环绕通知 @Around 围绕目标方法执行,可以在目标方法执行前后进行额外操作

五、总结

通过自定义切面类,我们可以将横切关注点从业务逻辑中分离出来,提高代码的可维护性和可复用性。在 Spring 框架中,编写自定义切面类非常简单,只需要定义切入点和通知,Spring AOP 会自动将切面织入到目标方法中。希望本文能帮助你更好地理解和使用 Spring AOP 进行切面编程。