【Spring Boot】Spring AOP中的环绕通知

06-01 1315阅读

目录

    • 一、什么是AOP?
    • 二、AOP 的环绕通知
      • 2.1 切点以及切点表达式
      • 2.2 连接点
      • 2.3 通知(Advice)
      • 2.4 切面(Aspect)
      • 2.5 不同通知类型的区别
        • 2.5.1 正常情况下
        • 2.5.2异常情况下
        • 2.6 统一管理切点@PointCut

          一、什么是AOP?

          Aspect Oriented Programming(面向切面编程)

          AOP是Spring框架的第⼆⼤核⼼(第⼀⼤核⼼是IoC)

          AOP是一种思想,是对某一类事情的集中处理。

          其中在下面的学习中我们会学习到拦截器、统一异常处理,统一结果处理等,这些都是运用了AOP的统一思想来实现的。

          拦截器实现AOP思想作用的维度是前端对后端进行的一次请求和一次响应,主要是检索前端传来的URL,如果检索后返回True,则可以进入Controller开始执行代码,如果返回的为False,则表示失败,直接被拦截在外面,无法执行代码。

          统一结果处理则是使用注解@ControllerAdvice(通知类注解),实现ResponseBodyAdvice接口,对响应的结果进行统一处理,对齐进行统一的包装后响应,其中如果数据类型为String类型的话要进行特殊处理,使用ObjectMapper的方法将String格式转为Json格式再次响应。

          统一异常处理也是使用注解@ControllerAdvice(通知类注解),以及在方法上使用@ExceptionHandler注解,在全部的程序中如果遇到运行时异常则会自动捕捉,进行抛出,注:编译时异常是写代码过程中出现的,不手动解决就无法运行程序。

          接下来我们要学习的是运用了AOP思想,进行AOP的环绕处理

          二、AOP 的环绕通知

          2.1 切点以及切点表达式

          切点=通知类型+切点表达式

          切点表示了为该方法提供一组规则,来对程序进行功能增强

          通知类型有以下5种:

          @Around:环绕通知,此注解标注的通知方法在目标方法前,后都被执行

          @Before:前置通知,此注解标注的通知方法在目标方法前被执行

          @After:后置通知,此注解标注的通知方法在目标方法后被执行,⽆论是否有异常都会执行

          @AfterReturning:返回后通知,此注解标注的通知方法在目标方法后被执行,有异常不会执行

          @AfterThrowing:异常后通知,此注解标注的通知方法发⽣异常后执行

          如下图所示:

          【Spring Boot】Spring AOP中的环绕通知

          在该方法上的@Around,表示的是环绕处理,是一种通知类型

          其后面的execution(* com.example.demo.controller..(…)) 表示的是该通知类型作用的范围,是切点表达式

          2.2 连接点

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

          【Spring Boot】Spring AOP中的环绕通知

          在该图中,public recordTime方法就是连接点

          2.3 通知(Advice)

          通知就是具体要做的内容,简单来说就是方法内执行的代码内容

          如图所示:

          【Spring Boot】Spring AOP中的环绕通知

          该图中标红的位置就是通知内容,在AOP面向切面编程当中,我们把这部分重复的代码逻辑抽取出来单独定义,这部分代码就是通知的内容

          2.4 切面(Aspect)

          首先使用注解@Aspect来表示该类是一个切面类

          然后使用不同的通知类型进行处理,如图表示的是环绕通知类型

          在处理过程中ProceedingJoinPoint.proceed()让原始方法执行

          【Spring Boot】Spring AOP中的环绕通知

          切面(Aspect)=切点(Pointcut)+通知(Advice)

          以下代码表示一个完整的切面类:

          import lombok.extern.slf4j.Slf4j;
          import org.aspectj.lang.ProceedingJoinPoint;
          import org.aspectj.lang.annotation.Around;
          import org.aspectj.lang.annotation.Aspect;
          import org.springframework.stereotype.Component;
          @Slf4j
          @Aspect
          @Component
          public class TimeAspect {
          	/**
          	* 记录方法耗时
          	*/
          	@Around("execution(* com.example.demo.controller.*.*(..))")
          	public Object recordTime(ProceedingJoinPoint pjp) throws Throwable {
          		//记录方法执行开始时间
          		long begin = System.currentTimeMillis();
          		
          		//执行原始方法
          		Object result = pjp.proceed();
          		
          		//记录方法执行结束时间
          		long end = System.currentTimeMillis();
          		
          		//记录方法执行耗时
          		log.info(pjp.getSignature() + "执行耗时: {}ms", end - begin);
          		
          		return result;
          	}
          }
          

          2.5 不同通知类型的区别

          2.5.1 正常情况下

          环绕通知 @Around 标识的通知方法包含两部分,⼀个"前置逻辑",⼀个"后置逻辑".其

          中"前置逻辑"会先于 @Before 标识的通知方法执行,"后置逻辑"会晚于 @After 标识的通知方法执行

          【Spring Boot】Spring AOP中的环绕通知

          2.5.2异常情况下

          程序发⽣异常的情况下:

          • @AfterReturning 标识的通知方法不会执行, @AfterThrowing 标识的通知方法执行了

          • @Around 环绕通知中原始方法调⽤时有异常,通知中的环绕后的代码逻辑也不会在执行了(因为

          原始方法调⽤出异常了)

          【Spring Boot】Spring AOP中的环绕通知

          注意事项:

          • @Around 环绕通知需要调⽤ ProceedingJoinPoint.proceed() 来让原始方法执行,其他

          通知不需要考虑目标方法执行.

          • @Around 环绕通知方法的返回值,必须指定为Object,来接收原始方法的返回值,否则原始方法执

          行完毕,是获取不到返回值的.

          • ⼀个切面类可以有多个切点

          2.6 统一管理切点@PointCut

          统一管理切点就是对于复杂的切点表达式进行提取,然后用@PointCut注解方法进行单独声明,再本类下面的程序中如果用到该切点表达式,则直接使用注解后的方法即可,如果在其他类中使用该声明过后的切点表达式则需要使用全路径限定名+该方法即可

          本类使用注解后的切点表达式代码如下:

          @Slf4j
          @Aspect
          @Component
          public class AspectDemo {
          		//定义切点(公共的切点表达式)
          		@Pointcut("execution(* com.example.demo.controller.*.*(..))")
          		private void pt(){}
          		
          		//前置通知
          		@Before("pt()")
          		public void doBefore() {
          		//...代码省略
          		}
          		
          		//后置通知
          		@After("pt()")
          		public void doAfter() {
          		//...代码省略
          		}
          		
          		//返回后通知
          		@AfterReturning("pt()")
          		public void doAfterReturning() {
          		//...代码省略
          		}
          		
          		//抛出异常后通知
          		@AfterThrowing("pt()")
          		public void doAfterThrowing() {
          		//...代码省略
          		}
          		
          		//添加环绕通知
          		@Around("pt()")
          		public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
          		//...代码省略
          	}
          }
          

          在其他类中调用该切点表达式代码如下:

          @Slf4j
          @Aspect
          @Component
          	public class AspectDemo2 {
          		//前置通知
          		@Before("com.example.demo.aspect.AspectDemo.pt()")
          		public void doBefore() {
          		log.info("执行 AspectDemo2 -> Before 方法");
          	}
          	
          }```
          当切点定义使⽤private修饰时,仅能在当前切面类中使⽤,当其他切面类也要使⽤当前切点定义时,就需要把private改为public.引⽤方式为:全限定类名.方法名()
          ### 2.7     切面优先级@Order
          在切面类中会有多个切点同时匹配成功,那么该如何进行执行顺序呢?
          应该使用注解@Order(数字)来表示优先级顺序
          @Order注解标识的切面类,执行顺序如下:
          • @Before 通知:数字越⼩先执行
          • @After 通知:数字越⼤先执行
          @Order 控制切面的优先级,先执行优先级较⾼的切面,再执行优先级较低的切面,最终执行目标方法.
          
免责声明:我们致力于保护作者版权,注重分享,被刊用文章因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理! 部分文章是来自自研大数据AI进行生成,内容摘自(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供学习参考,不准确地方联系删除处理! 图片声明:本站部分配图来自人工智能系统AI生成,觅知网授权图片,PxHere摄影无版权图库和百度,360,搜狗等多加搜索引擎自动关键词搜索配图,如有侵权的图片,请第一时间联系我们。

目录[+]

取消
微信二维码
微信二维码
支付宝二维码