目录
一、什么是Spring AOP
AOP(Aspect Oriented Programming)即面向切面编程,是对某一类事件的集中处理。面向切面编程(AOP)是一种编程范式,旨在将横切关注点(如日志记录、事务管理、权限验证等)从业务逻辑代码中分离出来,从而提高代码的可重用性、可维护性和模块性。Spring AOP 通过代理模式在运行时动态地将横切关注点织入到目标对象中。
AOP 的组成:切面(Aspect)、切点(Pointcut)、通知(Advice)。AOP 的主要核心概念有:
切面(Aspect):
- 定义:切面是一个模块,它封装了横切关注点,包含了定义切点和通知的代码。
- 作用:将横切关注点模块化,便于复用和管理
连接点(Join Point):
- 定义:程序执行过程中能够插入切面的点,如方法调用、异常抛出等。
- 在Spring AOP中:连接点通常是方法的调用。
切点(Pointcut):
- 定义:切点定义了一组连接点,用于确定通知应该在哪些连接点上执行。
- 表示方式:通常使用表达式来匹配连接点,如AspectJ表达式。
通知(Advice):
- 定义:通知是切面在某个连接点执行的动作,它包含了在连接点执行的代码。
类型:
- 前置通知(Before):在目标方法执行前执行。
- 后置通知(After):在目标方法执行后执行。
- 返回通知(After Returning):在目标方法成功执行后执行。
- 异常通知(After Throwing):在目标方法抛出异常后执行。
- 环绕通知(Around):在目标方法执行前后执行,并可以控制目标方法的执行。
二、Spring AOP实现
Spring AOP 实现的主要步骤有:
- 添加 SpringAOP 的依赖
- 定义切面
- 定义切点
- 实现通知
2.1 添加Spring AOP依赖
在 pom.xml 文件中加入以下 Spring AOP 依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
2.2 定义切面和切点
在 Spring AOP 中,@Pointcut 注解用于定义一个切点,它描述了哪些方法会被 AOP 切面(Aspect)拦截。切点表达式通常使用 AspectJ 的切点表达式语言来指定。@Pointcut 注解本身不执行任何操作,但它可以与其他 AOP 注解(如@Before
、@After
、@Around
等通知)结合使用,以定义在哪些连接点(Join Points)上应用横切逻辑。
//定义切面
@Aspect
@Component
public class UserAspect {
//定义切点
@Pointcut("execution(* com.example.demo.controller.UserController.* (..))")
public void pointcut(){};
}
定义切面使用 @Aspect 注解,定义切点使用 @Ponintcut 注解。
在上述代码中,@Aspect 标记了 UserAspect 为一个切面类,它包含了一个或多个横切关注点(Advice),@Component 将该切面类注册到 Spring 中为一个 Bean,使它能够被 Spring 容器管理。@Pointcut 定义了 pointcut() 方法为一个切点,切点表达式采用了 execution() 表达式即 AspectJ 表达式。
采用 AspectJ 表达式主要是为了与 Spring AOP 结合,以实现对 MyBatis 操作的拦截和增强。AspectJ 表达式定义了哪些连接点(如方法调用)会被切面(Aspect)拦截。
2.3 解释AspectJ
- * :匹配任意字符,只匹配⼀个元素(包,类,或⽅法,⽅法参数)。
- .. :匹配任意字符,可以匹配多个元素 ,在表示类时,必须和 * 联合使⽤。
- + :表示按照类型匹配指定类的所有类,必须跟在类名后⾯,如 com.cad.Car+ ,表示继承该类的 所有⼦类包括本身。
//匹配UserController类中的所有方法
execution(* com.example.demo.controller.UserController.* (..))
2.4 什么是通知
在 Spring AOP 中,通知(Advice)是切面的具体实现,用于在连接点(通常是方法的执行点)上执行特定的逻辑。通知可以分为以下几类,每类通知的作用如下:
前置通知(Before Advice)
- 作用:在目标方法执行之前执行。可以用于执行权限检查、准备资源等操作。
- 示例:在执行业务方法之前,检查用户是否具有执行该方法的权限。
后置通知(After Advice)
- 作用:在目标方法执行之后执行,无论目标方法是否抛出异常。可以用于清理资源、记录日志等操作。
- 示例:在业务方法执行后,记录方法执行的时间和结果。
返回通知(After Returning Advice)
- 作用:在目标方法正常返回后执行。可以用于处理返回值、记录日志等操作。
- 示例:在业务方法成功返回后,对返回值进行格式化处理。
异常通知(After Throwing Advice)
- 作用:在目标方法抛出异常后执行。可以用于处理异常、记录错误日志等操作。
- 示例:在业务方法抛出异常时,捕获异常并记录到错误日志中。
环绕通知(Around Advice)
- 作用:在目标方法执行前后执行。可以在方法执行前进行前置处理,在方法执行后进行后置处理,甚至可以控制目标方法是否执行。
- 示例:在业务方法执行前开启事务,在方法执行后提交或回滚事务。
创建 UserAspect 公共类:
//定义切面
@Aspect
@Component
public class UserAspect {
//定义切点
@Pointcut("execution(* com.example.demo.controller.UserController.* (..))")
public void pointcut(){
System.out.println("hello this is pointcut");
};
//定义前置通知
@Before("pointcut()")
public void before(){
System.out.println("执行了前置通知");
};
//定义后置通知
@After("pointcut()")
public void after() {
System.out.println("执行了后置通知");
}
//环绕通知
@Around("pointcut()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("环绕前通知");
Object ret = joinPoint.proceed();
System.out.println("环绕后通知");
return ret;
}
//返回之前通知
@AfterReturning("pointcut()")
public void afterReturning() {
System.out.println("执行return之前的通知");
}
}
创建 UserController 类:
@RestController
@RequestMapping("/user")
public class UserController {
@RequestMapping("/getuser")
public String getUser() {
System.out.println("hello user");
return "hello user";
}
}
浏览器输入 localhost:8080/user/getuser 后,浏览器输出:
控制台输出:
本篇博客到这里就结束了,感谢各位的观看。