Spring Aop
2026/7/14 4:54:39 网站建设 项目流程

一、AOP(面向切面编程)

切面是指某一个特定的问题,AOP就是面向特定的方法编程,例如登录拦截器,统一数据返回,统一异常处理,都是使用AOP思想。AOP是Spring框架的第二大核心。

二、Spring AOP也是AOP的一种实现

编写AOP程序,记录Controller每个方法执行的时间

切点(Pointcut):

切点就是一组规则告诉程序哪些方法进行功能加强

切点表达式(例如代码中):

连接点:

满足切点表达式的方法就是连接点

路径下的方法都是连接点

通知( Advice)

通知就是具体要做的工作,即共同重复的逻辑,例如记录多个方法的耗时

切面:(切点+通知)

即:

切面类:

切面所在的类,即被@Aspect修饰的类

通知类型

1. @Before 前置通知

执行时机:目标方法执行之前运行

2. @AfterReturning 后置返回通知

执行时机:目标方法正常执行完毕、成功返回结果后,异常不执行

3. @AfterThrowing 异常通知

执行时机:目标方法抛出异常时执行

4. @After 最终通知

执行时机:无论目标方法是正常结束还是抛出异常,该通知都会在最后执行。

5. @Around 环绕通知(功能最强)

执行时机:包裹目标方法,手动控制方法执行

package com.bite.springaopdemo.aspect; import lombok.extern.slf4j.Slf4j; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.*; import org.springframework.core.annotation.Order; import org.springframework.stereotype.Component; @Aspect @Component @Slf4j public class AspectDemo1 { //添加环绕通知 @Around("execution(* com.bite.springaopdemo.controller.*.*(..))") public Object recordTime(ProceedingJoinPoint pjp)throws Throwable{//pjp指的是连接点对象 //记录方法开始时间 long begin=System.currentTimeMillis(); log.info("Around方法开始执行"); //执行原始方法 Object result=pjp.proceed(); //记录方法执行结束时间 long end=System.currentTimeMillis(); log.info(pjp.getSignature()+"执行耗时{}ms",end-begin);//getSignature()被拦截对象的类名方法名 log.info("Around方法执行结束"); return result; } //前置通知 @Before("execution(* com.bite.springaopdemo.controller.*.*(..))") public void doBefore(){ log.info("执行before方法"); } //后置通知 @After("execution(* com.bite.springaopdemo.controller.*.*(..))") public void doAfter(){ log.info("执行after方法"); } // //返回后通知 @AfterReturning("execution(* com.bite.springaopdemo.controller.*.*(..))") public void doAfterReturning(){ log.info("执行afterReturning方法"); } //抛出异常后通知 @AfterThrowing("execution(* com.bite.springaopdemo.controller.*.*(..))") public void doafterThrowing(){ log.info("执行AfterThrowing方法"); } }

测试接口:

package com.bite.springaopdemo.controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/test") public class TestController { @RequestMapping("/t1") public Integer t1(){ System.out.println("执行方法"); return 1; } }

出现异常时候:

@afterReturning标识方法不会执行,@AfterThrowing标识的通知执行

@Around环绕方法有异常的时候,环绕后的代码不会在执行了

@PointCut将公共代码提取出来

没有规定切面类的优先级的时候:

1、@Before(目标方法执行之前)

多个切面的前置通知,按照切面类名称字典顺序执行: Demo1 排在前面 → Demo2 → Demo3,越靠后的切面,Before 越晚执行。

2、@After(目标方法执行之后,最终通知)

Spring AOP 采用栈结构执行:先进后出。 想象成套盒子: 先套上 Demo1 盒子 → 再套 Demo2 盒子 → 最后套 Demo3 盒子; 执行完目标方法之后,拆盒子要从最外层开始拆: 先拆掉最外面的 Demo3 → 再拆 Demo2 → 最后拆 Demo1。

所以: Before 入栈顺序:1 → 2 → 3 After 出栈顺序:3 → 2 → 1

可以使用注解:@Order(优先级数值)数值越小,优先级越高,执行越早。

切点表达式(具体)

1.execution

execution(访问修饰符? 返回值类型 包路径.类名.方法名(参数列表) 异常抛出?)

@Pointcut("execution(* com.bite.springaopdemo.controller.*.*(..))")
  • *匹配任意单个内容
  • ..匹配多层包 / 任意个参数

2.annotation

1.创建有一个注解类:

2.切面类:(定义切点只对@MyAspect生效)

3.添加自定义注解

4.测试结果

Spring AOP实现方式:

1.基于注解@Aspect

2.基于自定义注解

3.基于Spring API(通过xml配置的方式)

4.基于代理实现

三、Spring AOP原理:

基于动态代理实现的,分为

1.代理模式

提供一个代理类,让我们调用目标方法的时候可以通过目标方法简介调用

根据代理创建的时期分为静态代理和动态代理:

静态代理:程序运行前,代理类的.class文件就已经存在

动态代理:不需要为每个对象目标创建一个代理对象,而是把代理对象的工作推迟到程序运行时,由JVM实现,即”业务来了再创建“

java对动态代理进行了实现,常见实现方式时JDK动态代理CGLIB动态代理

CGLIB和JDK区别:

1.CGLIB能够代理接口和类,JDK只能代理接口

2.据说CGLIB的性能更高

StringAOP 使用的是 JDK 还是 CGlib 什么时候使用 JDK 什么时候使用 CGlib

proxyTargetClass是 Spring AOP 的一个配置项

proxyTargetClass 值目标类情况使用代理方式
false实现了接口JDK 代理
false没有实现接口(只有类)CGLIB 代理
true实现了接口CGLIB 代理
true没有实现接口CGLIB 代理
  1. proxyTargetClass=false(默认旧版本规则):优先 JDK 代理(类实现接口才用 JDK,没接口自动用 CGLIB);
  2. proxyTargetClass=true:强制全部使用 CGLIB 代理,不管这个类有没有实现接口。

三、版本差异重点(SpringBoot2.x 和 3.x 不一样)

  1. Spring Framework 原生框架:proxyTargetClass=false默认值;
  2. SpringBoot 2.x 版本:默认也是false,优先 JDK 代理;
  3. SpringBoot 3.x 默认值改成 trueSpringBoot3 全局默认强制使用 CGLIB 代理,不管你的 Bean 有没有实现接口,统一 CGLIB。

这就是为什么你用 SpringBoot3 做 AOP 的时候,不需要写接口也能正常切面拦截,SpringBoot2 不写接口就会报错。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询