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

Spring及SpringBoot中AOP的使用

Spring中AOP示例

<dependencies><!--Spring核心包--><dependency><groupId>org.springframework</groupId><artifactId>spring-core</artifactId><version>5.3.6</version></dependency><!--引入SpringBean--><dependency><groupId>org.springframework</groupId><artifactId>spring-beans</artifactId><version>5.3.6</version></dependency><!--引入context包--><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.3.6</version></dependency><!--引入表达式jar包--><dependency><groupId>org.springframework</groupId><artifactId>spring-expression</artifactId><version>5.3.6</version></dependency><!--引入日志依赖--><dependency><groupId>commons-logging</groupId><artifactId>commons-logging</artifactId><version>1.2</version></dependency><!--引入测试包--><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version></dependency><!--引入AOP 以下三个包--><!--引入AOP包--><dependency><groupId>org.springframework</groupId><artifactId>spring-aop</artifactId><version>5.3.6</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-aspects</artifactId><version>5.3.6</version></dependency><dependency><groupId>org.aspectj</groupId><artifactId>aspectjrt</artifactId><version>1.9.6</version></dependency><!--当下所有的jar包都需要手动添加依赖,并且需要确定依赖的关系,对初学者不友好-->
</dependencies>

配置类

/*** @author TB* @date 2020/2/12 0:14*/
@Configuration//标识是个配置类
@ComponentScan("com")//包扫描路径
//SpringBoot的AOP是默认开启的,不需要加注解@EnableAspectJAutoProxy” 这里用的Spring所以要加
//让Spring认识加了@Aspect类的注解  不然Spring不认识切面@Aspect
//其实是启动AOP注解创建代理
//默认启用JDK动态代理,当目标对象没有实现接口时则采用CGlib代理
//该注解有个proxyTargetClass属性,默认是false 如果改成@EnableAspectJAutoProxy(proxyTargetClass=true)则使用CGlib代理
//JDK代理创建速度快,运行时稍慢,CBlib代理创建时速度慢,运行速度快
//可以通过属性的true或false来指定JDK代理和CGlib代理  默认CBG代理  找不到实现类的话会自动用CGlib代理
@EnableAspectJAutoProxy
//@EnableAsync//告诉spring框架启动时创建线程池 然后在需要异步请求时在池里拿线程直接用 然后需要用异步请求的方法上加@Async
public class SpringConfig {}

标注的AOP类

/***  AOP(面向切面编程) 主要利用**动态代理**的模式 **降低程序的耦合度,扩展业务功能方法.*** 1.AOP需要被Spring容器管理 @Component* 2.标识该类是AOP切面   @Aspect* 关于AOP名词介绍* 1).连接点: 用户可以被扩展的方法    其实我们将自定义注解放到目标方法上做标识,那么该注解其实就是个连接点* 2).切入点: 用户实际扩展的方法      确定了连接点,那么该方法也就是个切入点* 3).通知:  扩展方法的具体实现       5个通知* 4).切面: 将通知应用到切入点的过程**   通知类型(必会)* 1. before:  在目标方法执行之前执行* 2. afterReturning: 在目标方法执行之后返回时执行* 3. afterThrowing:  在目标方法执行之后,抛出异常时执行* 4. after:	无论程序是否执行成功,都要最后执行的通知* 5. around: 在目标方法执行前后 都要执行的通知(完美体现了动态代理模式)* 	功能最为强大 只有环绕通知可以控制目标方法的执行** 关于通知方法总结:* 	1.环绕通知是处理业务的首选.  可以修改程序的执行轨迹* 	2.另外的四大通知一般用来做程序的监控.(监控系统) 只做记录* @author TB* @date 2020/2/12 0:24*/
@Component
//虽然标识了该类为AOP切面 但是Spring容器默认不能识别切面注解,需要手动配置
//需要在配置类SpringConfig里加上注解@EnableAspectJAutoProxy
@Aspect
public class SpringAOP {/*** 切入点表达式* 概念:当程序满足切入点表达式,才能进入切面,执行通知方法.** 1.bean("bean的ID")  根据beanId进行拦截  只能匹配一个* 2.within("包名.类名") 可以使用通配符*?      能匹配多个.* 	粒度: 上述的切入点表达式 粒度是类级别的.  粗粒度.* 3.execution(返回值类型   包名.类名.方法名(参数列表...))* 	粒度: 控制的是方法参数级别. 所以粒度较细.   最常用的.* 4.@annotation(包名.注解名)     只拦截注解.* 	粒度: 注解是一种标记 根据规则标识某个方法/属性/类    细粒度*//*** 切入点表达式练习* within:*  1.within(com.jt.*.DeptServiceImpl)   一级包下的类*  2.within(com.jt..*.DeptServiceImpl)  ..代表多级包下的类*  3.within(com.jt..*)  包下的所有的类** execution(返回值类型 包名.类名.方法名(参数列表))*  1.execution(* com.jt..*.DeptServiceImpl.add*())*  注释: 返回值类型任意的, com.jt下的所有包中的DeptServiceImpl的类*        的add开头的方法 ,并且没有参数.**  2.execution(* com.jt..*.*(..))*  注释: 返回值类型任意,com.jt包下的所有包的所有类的所有方法 任意参数.**  3.execution(int com.jt..*.*(int))*  4.execution(Integer com.jt..*.*(Integer))*  强调: 在Spring表达式中没有自动拆装箱功能! 注意参数类型** @annotation(包名.注解名)*     @Before("@annotation(com.jt.anno.Cache)")*    只拦截特定注解的内容.*///1.定义before通知//@Before("bean(deptServiceImpl)")//扫描的是一个类 因此该类里所有方法都被扩展到了//@Before("within(com.jt.service.DeptServiceImpl)")//和上面效果一样//@Before("execution(* com.jt.service.DeptServiceImpl.add*())")//*表示返回值类型任意 add*表示以add开头的方法名 最后()表示参数是空的//@Before("@annotation(com.jt.anno.Cache)")//意思有该注解 就作为切入点   因此用注解标识最常用(自定义个注解)/*** spring为了AOP动态获取目标对象及方法中的数据,则通过Joinpoint* JoinPoint是所有通知的公共参数,无论哪种通知里都可以使用* 在Before里可以获取* 对象做数据传递获取如:* 1.获取目标对象的类型* 2.获取目标方法的名称* 3.获取目标方法的参数* @param joinPoint*/@Before("pointcut()")public void before(JoinPoint joinPoint){//前置方法一般作用获取参数,方法名,等等System.out.println("目标对象的Class类对象: "+joinPoint.getTarget().getClass());System.out.println("获取目标方法的方法签名: "+joinPoint.getSignature());System.out.println("获取目标对象的类名: "+ joinPoint.getSignature().getDeclaringTypeName());System.out.println("获取目标对象方法名: "+ joinPoint.getSignature().getName());System.out.println("获取目标方法参数: "+ Arrays.toString(joinPoint.getArgs()));System.out.println("我是before通知");}//1.定义一个切入点@Pointcut("@annotation(com.jt.anno.Cache)")public void pointcut(){}//如果每个通知前都加个切入点表达式  那么也太冗余了 因此我们可以定义个切入点 其他通知都围绕切入点//@BafterReturning("@annotation(com.jt.anno.Cache)")/*** JoinPoint参数是所有通知方法公有的*AfterReturning是目标方法返回执行之后返回时执行* 可以记录方法的返回值* AfterReturning注解里 value和pointcut是相同的效果:也就是说* @AfterReturning(value="pointcut()",returning="result")和@AfterReturning(pointcut="pointcut()",returning="result")* 效果一样* returning:将方法的返回值,通过形参result(这个随便取名)来进行传递(Spring会将返回值赋值给你定义的这个变量)*/@AfterReturning(value="pointcut()",returning="result")public void afterReturning(JoinPoint joinPoint,Object result){//这里注意 如果有需要用到JointPoint参数 那么必须放在第一个位置  不用可以去掉System.out.println("目标返回值结果是: "+result);System.out.println("我是AfterReturning的通知");}@AfterThrowing(pointcut = "pointcut()",throwing="e")//当目标方法执行时,抛出异常时,可以用AfterThrowing记录public void afterThrowing(Exception e){System.out.println("获取目标异常信息: "+e.getMessage());System.out.println("获取目标异常类型: "+e.getClass());System.out.println("我是AfterThrowing的通知,出现异常了");}@After("pointcut()")public void after(){System.out.println("我是After的通知");}/*** 关于环绕通知的说明* 作用: 可以控制目标方法是否执行.* 参数: ProceedingJoinPoint 通过proceed方法控制目标方法执行.* 注意事项:*  ProceedingJoinPoint 只能适用环绕通知* @return*/@Around("pointcut()")public Object around(ProceedingJoinPoint joinPoint){//注意多参情况ProceedingJoinPoint要放第一位Object result = null;try {System.out.println("环绕通知开始");//1.执行下一个通知  2.执行目标方法 3.接收返回值Long start = System.currentTimeMillis();result = joinPoint.proceed();Long end = System.currentTimeMillis();System.out.println("耗时:"+(end-start));} catch (Throwable throwable) {throwable.printStackTrace();}System.out.println("环绕通知结束");return result;}
}

切入点注解类

//控制注解的生命周期,什么时候起作用
@Retention(RetentionPolicy.RUNTIME)
//注解的作用对象:  类上   方法上 变量上
@Target({ElementType.METHOD,ElementType.TYPE,ElementType.FIELD})
public @interface Cache {}

测试

SpringBoot中使用AOP

/*** @author hrui* @date 2023/10/30 21:58*/
@Component
@Aspect
public class SpringAOP {@Before("pointcut()")public void before(JoinPoint joinPoint){//前置方法一般作用获取参数,方法名,等等
//        System.out.println("目标对象的Class类对象: "+joinPoint.getTarget().getClass());
//        System.out.println("获取目标方法的方法签名: "+joinPoint.getSignature());
//        System.out.println("获取目标对象的类名: "+ joinPoint.getSignature().getDeclaringTypeName());
//        System.out.println("获取目标对象方法名: "+ joinPoint.getSignature().getName());
//        System.out.println("获取目标方法参数: "+ Arrays.toString(joinPoint.getArgs()));System.out.println("我是before通知");}//1.定义一个切入点@Pointcut("@annotation(com.example.demo.aop.AnyAnno)")public void pointcut(){}@AfterReturning(value="pointcut()",returning="result")public void afterReturning(JoinPoint joinPoint, Object result){//这里注意 如果有需要用到JointPoint参数 那么必须放在第一个位置  不用可以去掉// System.out.println("目标返回值结果是: "+result);System.out.println("我是AfterReturning的通知");}@AfterThrowing(pointcut = "pointcut()",throwing="e")//当目标方法执行时,抛出异常时,可以用AfterThrowing记录public void afterThrowing(Exception e){
//        System.out.println("获取目标异常信息: "+e.getMessage());
//        System.out.println("获取目标异常类型: "+e.getClass());System.out.println("我是AfterThrowing的通知,出现异常了");}@After("pointcut()")public void after(){System.out.println("我是After的通知");}/*** 关于环绕通知的说明* 作用: 可以控制目标方法是否执行.* 参数: ProceedingJoinPoint 通过proceed方法控制目标方法执行.* 注意事项:*  ProceedingJoinPoint 只能适用环绕通知* @return*/@Around("pointcut()")public Object around(ProceedingJoinPoint joinPoint){//注意多参情况ProceedingJoinPoint要放第一位Object result = null;try {System.out.println("环绕通知开始");//1.执行下一个通知  2.执行目标方法 3.接收返回值result = joinPoint.proceed();System.out.println("环绕通知里方法执行完了");} catch (Throwable throwable) {throwable.printStackTrace();}System.out.println("环绕通知结束");return result;}
}

切入点注解类

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE,ElementType.METHOD,ElementType.FIELD})
public @interface AnyAnno {}

自己随便测试下

相关文章:

Spring及SpringBoot中AOP的使用

Spring中AOP示例 <dependencies><!--Spring核心包--><dependency><groupId>org.springframework</groupId><artifactId>spring-core</artifactId><version>5.3.6</version></dependency><!--引入SpringBean--&…...

cmake多目录构建初步成功

目录和代码和 首次cmake 多目录构建失败 此文一样&#xff1b; 只有一个CMakeLists.txt&#xff1b; cmake_minimum_required(VERSION 3.10) project(mytest3 VERSION 1.0) include_directories("${PROJECT_SOURCE_DIR}/include") add_executable(mytest3 src/main…...

idea插件(一)-- SequenceDiagram(UML自动生成工具)

目录 1. 安装 2. 默认快捷键 3. 操作说明 4. 导出为图片与UML类图 4.1 导出为图片&#xff1a; 4.2 导出 UML 类图 SequenceDiagram是从java、kotlin、scala&#xff08;Beta&#xff09;和groovy&#xff08;limited&#xff09;代码生成简单序列图&#xff08;UML&…...

STM32 APP跳转到Bootloader

stm32 app跳转到bootloade 【STM32】串口IAP功能的实现&#xff0c;BootLoader与App相互跳转 STM32 从APP跳入BootLoader问题...

[RISC-V]verilog

小明教IC-1天学会verilog(7)_哔哩哔哩_bilibili task不可综合&#xff0c;function可以综合...

Log4j-tag丢失

一、引言 最近有个线上日志丢失tag的问题&#xff0c;是组内封装了后置请求的拦截器把请求的响应结果存到ClickHouse里面去&#xff0c;但是日志总有一些tag丢失。 作者提出父级线程的threadlocal被清空&#xff0c;同事认为可能是threadlocal的弱引用在gc的时候被回收。两种想…...

代码随想录算法训练营第五十六天|1143.最长公共子序列 ● 1035.不相交的线 ● 53. 最大子序和 动态规划

1143. 最长公共子序列 int longestCommonSubsequence(char * text1, char * text2){int len1 strlen(text1);int len2 strlen(text2);int dp[len11][len21];for (int i 0; i < len1; i){for (int j 0; j < len2; j){dp[i][j] 0;}}for (int i 1; i < len1; i){f…...

虚拟机和Windows的文件传输

拖拽/复制粘贴 直接将虚拟机linux系统的文件拖曳到windows桌面&#xff0c;或者直接将windows的文件拖曳到虚拟机linux系统当中&#xff0c;可以实现文件传输。当然复制粘贴方式也可以&#xff0c;但是前提是需要下载安装好VMware tools。 共享文件夹 概念&#xff1a;在Win…...

leetcode分类刷题:二叉树(八、二叉搜索树特有的自顶向下遍历)

二叉搜索树是一个有序树&#xff1a;每个二叉树都满足左子树上所有节点的值均小于它的根节点的值&#xff0c;右子树上所有节点的值均大于它的根节点的值&#xff1b;利用该性质&#xff0c;可以实现二叉搜索树特有的自顶向下遍历 700. 二叉搜索树中的搜索 思路1、自顶向下的遍…...

Vue 插槽 组件插入不固定内容

定义好一个组件&#xff0c;如果想插入图片或视频这非常不好的控制应该显示什么&#xff0c;这个时候可以使用插槽插入自定义内容 默认插槽 <Login><template><h1>我是插入的内容</h1></template></Login >组件 <slot></slot>…...

webpack打包时配置环境变量

webpack打包时配置环境变量 一、常规环境变量配置1. 使用webpack.DefinePlugin定义全局常量2. 在Vue静态页面中使用该环境变量 二、纯静态文件配置环境变量1. 使用npm或yarn安装html-webpack-plugin2. 在Webpack配置中引入并使用插件3. 使用htmlwebpackplugin.options方式配置环…...

【c++|opencv】一、基础操作---3.访问图像元素

every blog every motto: You can do more than you think. https://blog.csdn.net/weixin_39190382?typeblog 0. 前言 访问图像元素 1. 访问图像像素 1.1 访问某像素 //灰度图像&#xff1a; image.at<uchar>(j, i) //j为行数&#xff0c;i为列数 //BGR彩色图像 i…...

机器视觉3D项目评估的基本要素及测量案例分析

目录 一. 检测需求确认 1、产品名称&#xff1a;【了解是什么产品上的零件&#xff0c;功能是什么】 2、*产品尺寸&#xff1a;【最大兼容尺寸】 3、*测量项目&#xff1a;【确认清楚测量点位】 4、*精度要求&#xff1a;【若客户提出的精度值过大或者过小&#xff0c;可以和客…...

力扣日记10.31-【栈与队列篇】前 K 个高频元素

力扣日记&#xff1a;【栈与队列篇】前 K 个高频元素 日期&#xff1a;2023.10.31 参考&#xff1a;代码随想录、力扣 347. 前 K 个高频元素 题目描述 难度&#xff1a;中等 给你一个整数数组 nums 和一个整数 k &#xff0c;请你返回其中出现频率前 k 高的元素。你可以按 任意…...

TensorFlow案例学习:简单的音频识别

前言 以下内容均来源于官方教程&#xff1a;简单的音频识别&#xff1a;识别关键字 音频识别 下载数据集 下载地址&#xff1a;http://storage.googleapis.com/download.tensorflow.org/data/mini_speech_commands.zip 可以直接浏览器访问下载。 下载完成后将其解压到项目…...

css小程序踩坑记录

写标签设置距离 一直设置不动 写个双层 设置动了 神奇 好玩...

Android sqlite分页上传离线订单后删除

1、判断订单表的的总数是否大于0&#xff0c;如果大于0开始上传订单 public int getOrderCount() {String query "SELECT COUNT(*) FROM " TABLE_NAME;Cursor cursor db.rawQuery(query, null);int count 0;if (cursor.moveToFirst()) {count cursor.getInt(0);…...

Flask基本教程以及Jinjia2模板引擎简介

flask基本使用 直接看代码吧&#xff0c;非常容易上手&#xff1a; # 创建flask应用 app Flask(__name__)# 路由 app.route("/index", methods[GET]) def index():return "FLASK&#xff1a;欢迎访问主页&#xff01;"if __name__ "__main__"…...

Django实战项目-学习任务系统-兑换物品管理

接着上期代码框架&#xff0c;开发第5个功能&#xff0c;兑换物品管理&#xff0c;再增加一个学习兑换物品表&#xff0c;主要用来维护兑换物品&#xff0c;所需积分&#xff0c;物品状态等信息&#xff0c;还有一个积分流水表&#xff0c;完成任务奖励积分&#xff0c;兑换物品…...

jmeter和postman你选哪个做接口测试?

软件测试行业做功能测试和接口测试的人相对比较多。在测试工作中&#xff0c;有高手&#xff0c;自然也会有小白&#xff0c;但有一点我们无法否认&#xff0c;就是每一个高手都是从小白开始的&#xff0c;所以今天我们就来谈谈一大部分人在做的接口测试&#xff0c;小白变高手…...

基于算法竞赛的c++编程(28)结构体的进阶应用

结构体的嵌套与复杂数据组织 在C中&#xff0c;结构体可以嵌套使用&#xff0c;形成更复杂的数据结构。例如&#xff0c;可以通过嵌套结构体描述多层级数据关系&#xff1a; struct Address {string city;string street;int zipCode; };struct Employee {string name;int id;…...

Redis数据倾斜问题解决

Redis 数据倾斜问题解析与解决方案 什么是 Redis 数据倾斜 Redis 数据倾斜指的是在 Redis 集群中&#xff0c;部分节点存储的数据量或访问量远高于其他节点&#xff0c;导致这些节点负载过高&#xff0c;影响整体性能。 数据倾斜的主要表现 部分节点内存使用率远高于其他节…...

LeetCode - 199. 二叉树的右视图

题目 199. 二叉树的右视图 - 力扣&#xff08;LeetCode&#xff09; 思路 右视图是指从树的右侧看&#xff0c;对于每一层&#xff0c;只能看到该层最右边的节点。实现思路是&#xff1a; 使用深度优先搜索(DFS)按照"根-右-左"的顺序遍历树记录每个节点的深度对于…...

【SSH疑难排查】轻松解决新版OpenSSH连接旧服务器的“no matching...“系列算法协商失败问题

【SSH疑难排查】轻松解决新版OpenSSH连接旧服务器的"no matching..."系列算法协商失败问题 摘要&#xff1a; 近期&#xff0c;在使用较新版本的OpenSSH客户端连接老旧SSH服务器时&#xff0c;会遇到 "no matching key exchange method found"​, "n…...

jmeter聚合报告中参数详解

sample、average、min、max、90%line、95%line,99%line、Error错误率、吞吐量Thoughput、KB/sec每秒传输的数据量 sample&#xff08;样本数&#xff09; 表示测试中发送的请求数量&#xff0c;即测试执行了多少次请求。 单位&#xff0c;以个或者次数表示。 示例&#xff1a;…...

淘宝扭蛋机小程序系统开发:打造互动性强的购物平台

淘宝扭蛋机小程序系统的开发&#xff0c;旨在打造一个互动性强的购物平台&#xff0c;让用户在购物的同时&#xff0c;能够享受到更多的乐趣和惊喜。 淘宝扭蛋机小程序系统拥有丰富的互动功能。用户可以通过虚拟摇杆操作扭蛋机&#xff0c;实现旋转、抽拉等动作&#xff0c;增…...

Modbus RTU与Modbus TCP详解指南

目录 1. Modbus协议基础 1.1 什么是Modbus? 1.2 Modbus协议历史 1.3 Modbus协议族 1.4 Modbus通信模型 🎭 主从架构 🔄 请求响应模式 2. Modbus RTU详解 2.1 RTU是什么? 2.2 RTU物理层 🔌 连接方式 ⚡ 通信参数 2.3 RTU数据帧格式 📦 帧结构详解 🔍…...

LLaMA-Factory 微调 Qwen2-VL 进行人脸情感识别(二)

在上一篇文章中,我们详细介绍了如何使用LLaMA-Factory框架对Qwen2-VL大模型进行微调,以实现人脸情感识别的功能。本篇文章将聚焦于微调完成后,如何调用这个模型进行人脸情感识别的具体代码实现,包括详细的步骤和注释。 模型调用步骤 环境准备:确保安装了必要的Python库。…...

macOS 终端智能代理检测

&#x1f9e0; 终端智能代理检测&#xff1a;自动判断是否需要设置代理访问 GitHub 在开发中&#xff0c;使用 GitHub 是非常常见的需求。但有时候我们会发现某些命令失败、插件无法更新&#xff0c;例如&#xff1a; fatal: unable to access https://github.com/ohmyzsh/oh…...

书籍“之“字形打印矩阵(8)0609

题目 给定一个矩阵matrix&#xff0c;按照"之"字形的方式打印这个矩阵&#xff0c;例如&#xff1a; 1 2 3 4 5 6 7 8 9 10 11 12 ”之“字形打印的结果为&#xff1a;1&#xff0c;…...