具体实践

Spring AOP是Spring框架中一个支持实现面向切面编程的模块,由于Spring Boot已经把Spring框架组合得非常好用,所以在基于Spring Boot框架的项目中实现AOP编程也是非常方便,具体来说可以分为如下几步:

第一步: 在项目中引入依赖配置。

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>

第二步: 定义切面,切入点和通知,三者可以直接在一个类中完成。

使用注解@Aspect来定义切面(注:需要在应用注解@Aspect的类上使用注解@Component标识这是一个容器管理的Bean),使用注解@Pointcut来定义切入点,使用注解@Before@After@Around@AfterReturning@AfterThrowing来定义不同的通知。

@Aspect // 使用注解@Aspect定义切面
@Component // 同时使用注解@Component标识这是一个IoC容器管理的Bean
public class AspectSample {
// 定义切点
// 使用@Pointcut注解定义切点
// 切点的表达式有多种,如:execution,within,@annotation,bean()
// 详见:https://docs.spring.io/spring-framework/reference/core/aop/ataspectj/pointcuts.html#aop-pointcuts-combining // 匹配类org.chench.springboot.scaffolding.controller.AOPController的所有方法
@Pointcut("execution(* org.chench.springboot.scaffolding.controller.AOPController.*(..))")
public void pointcutSample() {} // 定义通知
// 可以用于定义通知的注解有5个:
// 1. @Before(前置通知:目标方法执行前执行)
// 2. @After(后知通知:无论目标方法正常返回还是抛出异常都执行)
// 3. @Around(环绕通知:目标方法执行前和返回后执行)
// 4. @AfterReturning(返回通知:目标方法正常返回后执行)
// 5. @AfterThrowing(异常通知:目标方法抛出异常后执行) // 前置通知:目标方法执行前执行
@Before("pointcutSample()")
public void beforeAdvice(JoinPoint joinPoint) {
System.out.println(String.format("这里是前置通知执行:%s", new Date()));
} // 后置通知:无论目标方法正常返回还是抛出异常都执行
@After("pointcutSample()")
public void afterAdvice(JoinPoint joinPoint) {
System.out.println(String.format("这里是后置通知执行:%s", new Date()));
} // 环绕通知执行:目标方法执行前和返回后执行
// 注意:
// 1.在环绕通知中一定要返回目标方法的返回值,否则客户端就无法接收到结果啦
// 2.如果在环绕通知中捕获了目标方法执行时抛出的异常,则异常通知对应的切面逻辑将得不到执行
@Around("pointcutSample()")
public Object aroundAdvice(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
System.out.println(String.format("这里是环绕通知执行前:%s", new Date()));
Object result = proceedingJoinPoint.proceed();
System.out.println(String.format("这里是环绕通知执行后:%s", new Date()));
return result;
} // 返回通知:目标方法正常返回后执行
@AfterReturning(value = "pointcutSample()", returning = "r")
public void afterRunningAdvice(JoinPoint joinPoint, Object r) {
System.out.println(String.format("这里是返回通知执行:%s %s", new Date(), r));
} // 异常通知:目标方法抛出异常后执行
// 有意思的是:
// 如果在项目中同时使用了@ControllerAdvice注解进行全局异常处理,同时也定义了异常通知
// 那么异常通知将在@ControllerAdvice之前得到执行
@AfterThrowing(value = "pointcutSample()", throwing = "e")
public void afterThrowingAdvice(JoinPoint joinPoint, Throwable e) {
System.out.println(String.format("这里是异常通知执行:%s", new Date()));
}
}

写在最后

关于Spring对AOP的支持有如下几点总结:

1.切入点只支持方法

2.不同类型的通知执行的先后顺序不同,假设对于一个相同的切入点定义了所有通知类型。

  • 当目标方法没有抛出异常时,各个通知的执行顺序如下:
这里是aroundAdvice中目标方法执行前:Tue Jul 18 00:20:31 CST 2023
这里是beforeAdvice执行:Tue Jul 18 00:20:31 CST 2023
执行目标方法m1:Tue Jul 18 00:20:31 CST 2023
这里是aroundAdvice中目标方法执行后:Tue Jul 18 00:20:31 CST 2023
这里是afterAdvice执行:Tue Jul 18 00:20:31 CST 2023
这里是afterRunningAdvice执行:Tue Jul 18 00:20:31 CST 2023
  • 当目标方法执行时抛出异常,且在环绕通知中没有明确捕获该异常,则各个通知的执行顺序如下:
这里是aroundAdvice中目标方法执行前:Tue Jul 18 00:22:54 CST 2023
这里是beforeAdvice执行:Tue Jul 18 00:22:54 CST 2023
执行目标方法m2:Tue Jul 18 00:22:54 CST 2023
这里是afterAdvice执行:Tue Jul 18 00:22:54 CST 2023
这里是afterThrowingAdvice执行:Tue Jul 18 00:22:54 CST 2023

显然,此时就缺少了这里是aroundAdvice中目标方法执行后这里是afterRunningAdvice执行,但是这里是afterThrowingAdvice执行却出现了。

3.如果使用了@ControllerAdvice实现全局异常拦截,同时也定义了异常通知@AfterThrowing,那么异常通知@AfterThrowing将在@ControllerAdvice之前得到执行。

@ControllerAdvice
public class GlobalExceptionAdvice {
@ResponseBody
@ExceptionHandler(value = Exception.class)
public Object handleException(HttpServletRequest req, HttpServletResponse resp, Exception ex) {
System.out.println(String.format("这里是@ControllerAdvice:%s", new Date()));
return JsonVO.build().setCode(-1).setMessage("error");
}
}

当目标方法抛出异常后,则输出:

这里是aroundAdvice中目标方法执行前:Tue Jul 18 00:28:48 CST 2023
这里是beforeAdvice执行:Tue Jul 18 00:28:48 CST 2023
执行目标方法m2:Tue Jul 18 00:28:48 CST 2023
这里是afterAdvice执行:Tue Jul 18 00:28:48 CST 2023
这里是afterThrowingAdvice执行:Tue Jul 18 00:28:48 CST 2023 # @AfterThrowing将在@ControllerAdvice之前得到执行
这里是@ControllerAdvice:Tue Jul 18 00:28:48 CST 2023

4.每种通知方法中都可以通过第一个参数获取JoinPoint对象(环绕通知的方法参数ProceedingJoinPoint也是JoinPoint类型),并且在@AfterReturning通知中还可以获取目标方法的返回值,在@AfterThrowing通知中可以获取目标方法抛出的异常。

5.拦截器HandlerInterceptor也是Spring AOP的一个具体应用,当同时存在拦截器HandlerInterceptor和通知Advice时,拦截器会在通知之前执行,因为拦截器处理的是uri,而通知处理的是方法,一个在前,一个在后。Spring提供的拦截器机制跟Servlet中的Filter机制很像,但是他们属于不同范畴(后者属于Servlet规范中的内容,而前者属于Spring框架提供的支持)。

【参考】

aop-pointcuts-combining

Spring AOP - 注解方式使用介绍

原来这才是Spring Boot使用AOP的正确姿势

在SpringBoot中使用AOP——通知中的参数

AOP通知获取数据(参数、返回值、异常)

Spring AOP使用:自定义注解、通知(简单使用和原理了解)

SpringBoot之Filter注册

优雅的使用SpringBoot 中的Filter

Spring Boot拦截器(Interceptor)详解

Spring 过滤器 拦截器 AOP区别

在SpringBoot中实践AOP编程的更多相关文章

  1. Springboot中使用AOP统一处理Web请求日志

    title: Springboot中使用AOP统一处理Web请求日志 date: 2017-04-26 16:30:48 tags: ['Spring Boot','AOP'] categories: ...

  2. SpringBoot图文教程5—SpringBoot 中使用Aop

    有天上飞的概念,就要有落地的实现 概念+代码实现是本文的特点,教程将涵盖完整的图文教程,代码案例 文章结尾配套自测面试题,学完技术自我测试更扎实 概念十遍不如代码一遍,朋友,希望你把文中所有的代码案例 ...

  3. 在SpringBoot中配置aop

    前言 aop作为spring的一个强大的功能经常被使用,aop的应用场景有很多,但是实际的应用还是需要根据实际的业务来进行实现.这里就以打印日志作为例子,在SpringBoot中配置aop 已经加入我 ...

  4. 编写SpringBoot 中的AOP

    编写SpringBoot 中的AOP 在程序开发的过程中会使用到AOP的思想,面向切面进行开发,比如登录的验证,记录日志等等-频繁需要操作的步骤,在遇到这种情况时就要使用Spring 的AOP了 Sp ...

  5. 聊Javascript中的AOP编程

    Duck punch 我们先不谈AOP编程,先从duck punch编程谈起. 如果你去wikipedia中查找duck punch,你查阅到的应该是monkey patch这个词条.根据解释,Mon ...

  6. 聊聊Javascript中的AOP编程

    Duck punch 我们先不谈AOP编程,先从duck punch编程谈起. 如果你去wikipedia中查找duck punch,你查阅到的应该是monkey patch这个词条.根据解释,Mon ...

  7. spring-boot中的AOP

    public class User { private Integer id; private String username; private String note; public User(In ...

  8. SpringBoot中搭配AOP实现自定义注解

    1 springBoot的依赖 确定项目中包含可以注解的依赖 <dependency> <groupId>org.springframework.boot</groupI ...

  9. .NET Core中实现AOP编程

    AOP全称Aspect Oriented Progarmming(面向切面编程),其实AOP对ASP.NET程序员来说一点都不神秘,你也许早就通过Filter来完成一些通用的功能,例如你使用Autho ...

  10. (转).NET Core中实现AOP编程

    原文地址:https://www.cnblogs.com/xiandnc/p/10088159.html AOP全称Aspect Oriented Progarmming(面向切面编程),其实AOP对 ...

随机推荐

  1. [转帖]Jmeter性能测试:高并发分布式性能测试

    一.为什么要进行分布式性能测试 当进行高并发性能测试的时候,受限于Jmeter工具本身和电脑硬件的原因,无法满足我们对大并发性能测试的要求.基于这种场景下,我们就需要采用分布式的方式来实现我们高并发的 ...

  2. 【转帖】Linux多链路聚合技术

    https://www.jianshu.com/p/dd8587ecf54f 一般而言,在单体结构的操作系统中,一块物理磁盘会接在总线设备上,并经由总线分配 PCI-Bus 号,这个时候一个 bus  ...

  3. [转帖]redis操作 + StrictRedis使用

    https://www.cnblogs.com/szhangli/p/9979600.html Redis string类型 字符串类型是 Redis 中最为基础的数据存储类型. 它在 Redis 中 ...

  4. 我们开源了一个 Ant Design 的单元测试工具库

    我们是袋鼠云数栈 UED 团队,致力于打造优秀的一站式数据中台产品.我们始终保持工匠精神,探索前端道路,为社区积累并传播经验价值. 本文作者:佳岚 欢迎大家点一个小小的 Star ant-design ...

  5. vue中diff算法处理新旧节点的流程

    vue中diff算法处理新旧节点的流程 patch函数的作用 function patch(oldVnode: VNode | Element, vnode: VNode): VNode { let ...

  6. 在web中,用户输入的文字过多 和页面排版一行显示不下 怎么办

    在写管理系统中, 如果用户在input中,表单输入过多,应该如何去处理 常用的解决办法是: 1==>用户只能够输入一定范围内的字数 2==>超出几个字后(宽度)使用省略号显示. 3==&g ...

  7. 每日一道Java面试题:Java是值传递还是引用传递?

    写在开头 Java是值传递还是引用传递?这个问题几乎100%的出现在了各大主流Java面试题中,知识点很小,但很考验面试者对于Java运行的理解,今晚趁着生产投产的空子,过来小聊一下. 实参与形参 所 ...

  8. Fabric区块链浏览器(1)

    本文是区块链浏览器系列的第三篇,本文介绍区块链浏览器的主体部分,即区块数据的解析. 这一版本的区块链浏览器是基于gin实现的,只提供三种接口: /block/upload:POST,上传Protobu ...

  9. 策略模式学习,使用go实现策略模式

    策略模式 定义 优点 缺点 使用场景 代码实现 策略模式和工厂模式的区别 参考 策略模式 定义 策略模式定义了算法家族,分别封装起来,让他们之间可以相互替换,此模式让算法的变化,不会影响到客户端的使用 ...

  10. 深度学习应用篇-计算机视觉-语义分割综述[6]:DeepLab系列简介、DeepLabV3深入解读创新点、训练策略、主要贡献

    深度学习应用篇-计算机视觉-语义分割综述[6]:DeepLab系列简介.DeepLabV3深入解读创新点.训练策略.主要贡献 0.DeepLabV3深入解读 1.DeepLab系列简介 1.1.Dee ...