
在软件开发中,我们常常会遇到一些与核心业务逻辑无关,但又贯穿于多个业务模块的功能,比如日志记录、事务管理、权限验证等。传统的编程方式会导致这些功能代码分散在各个业务方法中,造成代码的冗余和可维护性降低。AOP(Aspect-Oriented Programming,面向切面编程)应运而生,它可以将这些横切关注点(如日志、事务等)从业务逻辑中分离出来,以一种模块化的方式进行管理。
Spring AOP 提供了多种通知类型,每种通知类型在不同的时机执行。下面我们详细介绍几种常见的通知类型。
前置通知在目标方法执行之前执行,通常用于进行一些预处理操作,如权限验证、参数检查等。
后置通知在目标方法执行之后执行,无论目标方法是否抛出异常都会执行,常用于资源清理等操作。
返回通知在目标方法正常返回后执行,它可以访问目标方法的返回值。
异常通知在目标方法抛出异常时执行,常用于记录异常信息等操作。
环绕通知是最强大的通知类型,它可以在目标方法执行前后都进行操作,甚至可以决定是否执行目标方法。
下面是一个通知类型的总结表格:
| 通知类型 | 执行时机 |
| —— | —— |
| 前置通知 | 目标方法执行之前 |
| 后置通知 | 目标方法执行之后(无论是否抛出异常) |
| 返回通知 | 目标方法正常返回后 |
| 异常通知 | 目标方法抛出异常时 |
| 环绕通知 | 目标方法执行前后 |
在 pom.xml 中添加 Spring AOP 相关依赖:
<dependencies><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.3.23</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-aop</artifactId><version>5.3.23</version></dependency><dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.9.7</version></dependency></dependencies>
创建一个简单的业务类 UserService:
package com.example.demo.service;public class UserService {public String addUser(String username) {System.out.println("Adding user: " + username);return "User added successfully";}}
创建一个切面类 LoggingAspect,包含各种通知类型:
package com.example.demo.aspect;import org.aspectj.lang.JoinPoint;import org.aspectj.lang.ProceedingJoinPoint;import org.aspectj.lang.annotation.*;import org.springframework.stereotype.Component;@Aspect@Componentpublic class LoggingAspect {// 前置通知@Before("execution(* com.example.demo.service.UserService.addUser(..))")public void beforeAdvice(JoinPoint joinPoint) {System.out.println("Before method: " + joinPoint.getSignature().getName());}// 后置通知@After("execution(* com.example.demo.service.UserService.addUser(..))")public void afterAdvice(JoinPoint joinPoint) {System.out.println("After method: " + joinPoint.getSignature().getName());}// 返回通知@AfterReturning(pointcut = "execution(* com.example.demo.service.UserService.addUser(..))", returning = "result")public void afterReturningAdvice(JoinPoint joinPoint, Object result) {System.out.println("Method returned: " + result);}// 异常通知@AfterThrowing(pointcut = "execution(* com.example.demo.service.UserService.addUser(..))", throwing = "ex")public void afterThrowingAdvice(JoinPoint joinPoint, Exception ex) {System.out.println("Method threw exception: " + ex.getMessage());}// 环绕通知@Around("execution(* com.example.demo.service.UserService.addUser(..))")public Object aroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable {System.out.println("Around advice before method");Object result = joinPoint.proceed();System.out.println("Around advice after method");return result;}}
package com.example.demo.config;import org.springframework.context.annotation.ComponentScan;import org.springframework.context.annotation.Configuration;import org.springframework.context.annotation.EnableAspectJAutoProxy;@Configuration@ComponentScan(basePackages = "com.example.demo")@EnableAspectJAutoProxypublic class AppConfig {}
package com.example.demo;import com.example.demo.config.AppConfig;import com.example.demo.service.UserService;import org.springframework.context.annotation.AnnotationConfigApplicationContext;public class Main {public static void main(String[] args) {AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);UserService userService = context.getBean(UserService.class);String result = userService.addUser("John");System.out.println("Final result: " + result);context.close();}}
LoggingAspect 类使用 @Aspect 注解标记为切面类,@Component 注解将其注册为 Spring 组件。@Before、@After、@AfterReturning、@AfterThrowing 和 @Around 注解分别定义了前置通知、后置通知、返回通知、异常通知和环绕通知。execution(* com.example.demo.service.UserService.addUser(..)) 是切入点表达式,表示匹配 UserService 类的 addUser 方法。运行 Main 类,输出结果如下:
Around advice before methodBefore method: addUserAdding user: JohnAround advice after methodAfter method: addUserMethod returned: User added successfullyFinal result: User added successfully
从输出结果可以看出,各种通知类型按照预期的顺序执行。环绕通知在目标方法执行前后都有输出,前置通知在目标方法执行前输出,后置通知在目标方法执行后输出,返回通知在目标方法正常返回后输出。
通过本文的介绍和示例代码,你应该对 Spring AOP 的通知类型有了更深入的理解。AOP 可以帮助我们将横切关注点从业务逻辑中分离出来,提高代码的可维护性和可复用性。