广告位联系
返回顶部
分享到

Spring AOP中的环绕通知介绍

java 来源:互联网 作者:佚名 发布时间:2024-07-09 18:02:29 人浏览
摘要

一、什么是AOP? Aspect Oriented Programming(面向切面编程) AOP是Spring框架的第??核?(第??核?是IoC) AOP是一种思想,是对某一类事情的集中处理。 其中在下面的学习中我们会学习到拦截器、统

一、什么是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:异常后通知,此注解标注的通知方法发?异常后执行

如下图所示:

在该方法上的@Around,表示的是环绕处理,是一种通知类型
其后面的execution(* com.example.demo.controller..(…)) 表示的是该通知类型作用的范围,是切点表达式

2.2 连接点

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

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

2.3 通知(Advice)

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

如图所示:

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

2.4 切面(Aspect)

首先使用注解@Aspect来表示该类是一个切面类
然后使用不同的通知类型进行处理,如图表示的是环绕通知类型
在处理过程中ProceedingJoinPoint.proceed()让原始方法执行

切面(Aspect)=切点(Pointcut)+通知(Advice)
以下代码表示一个完整的切面类:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

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 标识的通知方法执行

2.5.2异常情况下

程序发?异常的情况下:
• @AfterReturning 标识的通知方法不会执行, @AfterThrowing 标识的通知方法执行了
• @Around 环绕通知中原始方法调?时有异常,通知中的环绕后的代码逻辑也不会在执行了(因为
原始方法调?出异常了)

注意事项:
• @Around 环绕通知需要调? ProceedingJoinPoint.proceed() 来让原始方法执行,其他
通知不需要考虑目标方法执行.
• @Around 环绕通知方法的返回值,必须指定为Object,来接收原始方法的返回值,否则原始方法执
行完毕,是获取不到返回值的.
• ?个切面类可以有多个切点

2.6 统一管理切点@PointCut

• @Around 环绕通知需要调? ProceedingJoinPoint.proceed() 来让原始方法执行,其他
通知不需要考虑目标方法执行.
• @Around 环绕通知方法的返回值,必须指定为Object,来接收原始方法的返回值,否则原始方法执
行完毕,是获取不到返回值的.
• ?个切面类可以有多个切点

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

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

@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 {

        //...代码省略

    }

}

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

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

@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 控制切面的优先级,先执行优先级较?的切面,再执行优先级较低的切面,最终执行目标方法.


版权声明 : 本文内容来源于互联网或用户自行发布贡献,该文观点仅代表原作者本人。本站仅提供信息存储空间服务和不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权, 违法违规的内容, 请发送邮件至2530232025#qq.cn(#换@)举报,一经查实,本站将立刻删除。
原文链接 :
相关文章
  • 本站所有内容来源于互联网或用户自行发布,本站仅提供信息存储空间服务,不拥有版权,不承担法律责任。如有侵犯您的权益,请您联系站长处理!
  • Copyright © 2017-2022 F11.CN All Rights Reserved. F11站长开发者网 版权所有 | 苏ICP备2022031554号-1 | 51LA统计