当前位置: 首页 > news >正文

【springBoot】springAOP

AOP的概述

AOP是面向切面编程。切面就是指某一类特定的问题,所以AOP也可以理解为面向特定方法编程。AOP是一种思想,拦截器,统一数据返回和统一异常处理是AOP思想的一种实现。简单来说:AOP是一种思想,对某一类事务的集中处理。

spring对AOP进行了实现,并且提供了一些API,这就是spring AOP.

spring AOP的简单使用

预先准备:

@RestController
public class TestController {@RequestMapping("/t1")public String t1(){String sum = "";for (int i = 1; i <= 10000; i++) {sum += 'a';}return "t1";}@RequestMapping("/t2")public String t2(){StringBuilder sum = new StringBuilder();for (int i = 1; i <= 10000; i++) {sum.append('a');}return "t2";}
}
  1. 引入springAOP依赖
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId>
</dependency>
  1. 编写AOP程序
@Slf4j
@Aspect//表示是一个切面类
@Component
public class TimeAspect {@Around("execution(* com.example.aopdemo.controller.*.*(..))")//作用域public Object timeCost(ProceedingJoinPoint joinPoint) throws Throwable {long start = System.currentTimeMillis();//方法执行前的逻辑Object result = joinPoint.proceed();//执行目标方法long end = System.currentTimeMillis();//方法执行后的逻辑log.info(joinPoint + "消耗时间:" + (end - start) + "ms");return result;}
}

我们通过AOP⼊⻔程序完成了业务接⼝执⾏耗时的统计.

@Around:环绕通知,在⽬标⽅法的前后都会被执⾏.后⾯的表达式表⽰对哪些⽅法进⾏增强

优点:

  • 代码⽆侵⼊:不修改原始的业务⽅法,就可以对原始的业务⽅法进⾏了功能的增强或者是功能的改变
  • 减少了重复代码
  • 提⾼开发效率
  • 维护⽅便

spring AOP 详解

spring AOP 的核心概念(了解)

  1. 切点:一组规则,通过表达式来描述
  2. 连接点: 切面要作用的方法,目标方法切点描述的方法.
  3. 通知:具体的逻辑,要做什么事情
  4. 切面:切点+通知

image-20240201200843227

com.example.aopdemo.controller目录下的方法就是连接点。

切点和连接点的关系:连接点是满⾜切点表达式的元素.切点可以看做是保存了众多连接点的⼀个集合.

spring AOP通知类型

  • @Around:环绕通知,此注解标注的通知方法在目标方法前,后都被执行 --> 使用最多
  • @Before:前置通知,此注解标注的通知方法在目标方法前被执行
  • @After:后置通知,此注解标注的通知方法在目标方法后被执行,无论是否有异常都会执行
  • @AfterReturning:返回后通知,此注解标注的通知方法在目标方法后被执行,有异常不会执行
  • @AfterThrowing:异常后通知,此注解标注的通知方法发⽣异常后执行
@Slf4j
@Aspect
@Component
public class AspectDemo {//定义切点@Pointcut("execution(* com.example.aopdemo.controller.*.*(..))")public void pt(){}@Before("pt()")//后置通知public void doBefore(){log.info("执行AspectDemo doBefore……");}@After("pt()")//前置通知public void doAfter(){log.info("执行AspectDemo doAfter……");}@AfterReturning("pt()")//返回后通知public void doAfterReturning(){log.info("执行AspectDemo doAfterReturning……");}@AfterThrowing("pt()")//异常后通知public void doAfterThrowing(){log.info("执行AspectDemo doAfterThrowing……");}@SneakyThrows@Around("pt()")//环绕通知public Object doAround(ProceedingJoinPoint joinPoint){log.info("执行AspectDemo doAround前……");Object result = joinPoint.proceed();log.info("执行AspectDemo doAround后……");return result;}
}

正常执行结果:

先执行around,再执行before。先执行after,再执行Around。

image-20240201204352259

异常执行结果:

当发生异常时,不执行AfterReturning,也不执行Around的方法后的逻辑

image-20240201204522479

@PointCut,定义切点

如果其他类需要使用,需要把切点声明为public.使用时,类的全限定名称+切点名称 ==> 包+类名

@Slf4j
@Aspect
@Component
public class AspectDemo1 {@After("com.example.aopdemo.aspect.AspectDemo.pt()")//前置通知public void doAfter(){log.info("执行AspectDemo1 doAfter……");}
}

切面的优先级

定义三个切面类:

@Slf4j
@Aspect
@Component
public class AspectDemo1 {@Before("com.example.aopdemo.aspect.AspectDemo.pt()")//后置通知public void doBefore(){log.info("执行AspectDemo1 doBefore……");}@After("com.example.aopdemo.aspect.AspectDemo.pt()")//前置通知public void doAfter(){log.info("执行AspectDemo1 doAfter……");}
}
@Slf4j
@Aspect
@Component
public class AspectDemo2 {@Before("com.example.aopdemo.aspect.AspectDemo.pt()")//后置通知public void doBefore(){log.info("执行AspectDemo2 doBefore……");}@After("com.example.aopdemo.aspect.AspectDemo.pt()")//前置通知public void doAfter(){log.info("执行AspectDemo2 doAfter……");}
}
@Slf4j
@Aspect
@Component
public class AspectDemo3 {@Before("com.example.aopdemo.aspect.AspectDemo.pt()")//后置通知public void doBefore(){log.info("执行AspectDemo3 doBefore……");}@After("com.example.aopdemo.aspect.AspectDemo.pt()")//前置通知public void doAfter(){log.info("执行AspectDemo3 doAfter……");}
}

存在多个切面类时,默认按照切面类的类名字母排序:

  • @Before通知:字母排名靠前的先执行
  • @After通知:字母排名靠前的后执行

image-20240201211100129

优先级高:先执行before,后执行After

Spring给我们提供了一个新的注解,来控制这些切面通知的执行顺序:@Order。使用@Order时,数字越小,优先级越高

@Order(2)
public class AspectDemo1 {
}
@Order(3)
public class AspectDemo2 {
}
@Order(1)
public class AspectDemo3 {
}

执行结果:

image-20240201211631225

@Order控制切面的优先级,先执行优先级较高的切面,再执行优先级较低的切面,最终执行目标方法.

image-20240201211754075

切点表达式

切点表达式常见有两种表达方式

  1. execution(…):根据方法的签名来匹配
  2. @annotation(…):根据注解匹配

execution表达式上面已经用过.

主要介绍切点表达式支持通配符表达:

    • :匹配任意字符,只匹配一个元素(返回类型,包,类名,方法或者方法去参数
    • 包名使用 * 表示任意包(一层包使用一个*)
    • 类名使用 * 表示任意类
    • 返回值使用 *表示任意返回值类型
    • 方法名使用 * 表示任意方法
    • 参数使用 * 表示一个任意类型的参数
  1. … :匹配多个连续的任意符号,可以通配任意层级的包,或任意类型,任意个数的参数

    • 使用 … 配置包名,标识此包以及此包下的所有子包
    • 可以使用 … 配置参数,任意个任意类型的参数

execution表达式更适用有规则的,如果我们要匹配多个无规则的方法,比如:TestController中的t1和UserController中的u1()这两个方法。这个时候我们使用execution这种切点表达式来描述就不是是很方便了。我们可以借助自定义注解的方式以及另一种切点表达式@annotation来描述这一类的切点。

实现步骤:

  1. 编写自定义注解
  2. 使用@annotation表达式来描述切点
  3. 在连接点的方法上添加自定义注解

准备测试代码:

@RestController
public class UserController {@RequestMapping("/hello")public String hello(){return "hello";}@RequestMapping("/h1")public String h1(){return "h1";}@RequestMapping("/h2")public String h2(){return "h2";}
}

编写自定义注解:

@Target({ElementType.METHOD})//标识了 Annotation所修饰的对象范围,即该注解可以用在什么地方(方法上)
@Retention(RetentionPolicy.RUNTIME)//指Annotation被保留的时间长短,标明注解的生命周期(运行时注解)
public @interface MyAspect {
}

使用@annotation表达式来描述切点

@Slf4j
@Component
@Aspect
public class MyAspectDemo {@Before("@annotation(com.example.aopdemo.aspect.MyAspect)")public void doBefore(){log.info("执行MyAspectDemo before...");}@After("@annotation(com.example.aopdemo.aspect.MyAspect)")public void doAfter1(){log.info("执行MyAspectDemo doAfter...");}//所有使用RequestMapping注解都会触发@After("@annotation(org.springframework.web.bind.annotation.RequestMapping)")public void doAfter(){log.info("执行RequestMapping doAfter...");}
}

在连接点的方法上添加自定义注解:

@MyAspect    
@RequestMapping("/h1")
public String h1(){return "h1";
}

相关文章:

  • 信息学奥赛一本通1209:分数求和
  • 【智慧零售】门店管理设备解决方案,为企业数字化运营升级赋能
  • 逻辑回归算法概述
  • Ubuntu20.04 屏幕截图快捷键
  • GptSoVits音频教程
  • 设计模式-创建型模式-原型模式
  • 宏观视角下的浏览器
  • Selenium定位不到元素怎么办?一定要这么做
  • 电路设计(26)——速度表的multisim仿真
  • 计算机设计大赛 深度学习卷积神经网络的花卉识别
  • fastApi笔记01-路径参数
  • 分类预测 | Matlab实现CWT-DSCNN-MSA基于时序特征、cwt小波时频图的双流卷积融合注意力机制的分类预测
  • 外贸消息多发工具开发常用源代码!
  • JSON语法
  • 【Nginx】Nginx配置反向代理 和 https
  • 网络传输文件的问题
  • Android单元测试 - 几个重要问题
  • Golang-长连接-状态推送
  • MaxCompute访问TableStore(OTS) 数据
  • MySQL Access denied for user 'root'@'localhost' 解决方法
  • Redis字符串类型内部编码剖析
  • 日剧·日综资源集合(建议收藏)
  • 使用 QuickBI 搭建酷炫可视化分析
  • 算法-插入排序
  • 一些基于React、Vue、Node.js、MongoDB技术栈的实践项目
  • 做一名精致的JavaScripter 01:JavaScript简介
  • ionic入门之数据绑定显示-1
  • ​软考-高级-信息系统项目管理师教程 第四版【第14章-项目沟通管理-思维导图】​
  • #define
  • (07)Hive——窗口函数详解
  • (3)选择元素——(14)接触DOM元素(Accessing DOM elements)
  • (Note)C++中的继承方式
  • (二)换源+apt-get基础配置+搜狗拼音
  • (附源码)spring boot球鞋文化交流论坛 毕业设计 141436
  • (附源码)springboot高校宿舍交电费系统 毕业设计031552
  • (机器学习的矩阵)(向量、矩阵与多元线性回归)
  • (免费领源码)Java#ssm#MySQL 创意商城03663-计算机毕业设计项目选题推荐
  • (求助)用傲游上csdn博客时标签栏和网址栏一直显示袁萌 的头像
  • (转)【Hibernate总结系列】使用举例
  • (转载)VS2010/MFC编程入门之三十四(菜单:VS2010菜单资源详解)
  • .Family_物联网
  • .netcore如何运行环境安装到Linux服务器
  • .NET程序员迈向卓越的必由之路
  • @autowired注解作用_Spring Boot进阶教程——注解大全(建议收藏!)
  • @ConditionalOnProperty注解使用说明
  • @Transactional类内部访问失效原因详解
  • [ vulhub漏洞复现篇 ] Hadoop-yarn-RPC 未授权访问漏洞复现
  • [ web基础篇 ] Burp Suite 爆破 Basic 认证密码
  • [2010-8-30]
  • [android] 请求码和结果码的作用
  • [AutoSar]BSW_OS 02 Autosar OS_STACK
  • [BJDCTF 2020]easy_md5
  • [BZOJ3223]文艺平衡树
  • [C++11 多线程同步] --- 条件变量的那些坑【条件变量信号丢失和条件变量虚假唤醒(spurious wakeup)】
  • [gdc19]《战神4》中的全局光照技术