Spring基于注解实现 AOP 切面功能
前言
在Spring AOP(Aspect-Oriented Programming)中,动态代理是常用的技术之一,用于在运行时动态地为目标对象生成代理对象, 并拦截其方法调用。Spring AOP 默认使用两种类型的动态代理机制:JDK 动态代理和 CGLIB 代理。 JDK 动态代理: JDK 动态代理是 Java 原生提供的动态代理机制,它只能代理接口。如果你的目标对象实现了某个接口,Spring AOP 会默认使用 JDK 动态代理。 JDK 动态代理机制通过 java.lang.reflect.Proxy 类来创建代理对象,并将方法调用委托给 InvocationHandler 实现。 CGLIB 代理: 如果目标对象没有实现接口,Spring AOP 会使用 CGLIB(Code Generation Library)来生成代理对象。CGLIB 是一个强大的库, 可以生成目标对象的子类,并覆盖其方法以实现代理功能。通过 CGLIB,Spring AOP 能够代理没有实现接口的类(即具体的类)。 默认代理机制的选择 Spring AOP 在选择使用哪种代理机制时,遵循以下原则: 如果目标对象实现了至少一个接口,则默认使用 JDK 动态代理。 如果目标对象没有实现任何接口,则默认使用 CGLIB 代理。 配置示例 在大多数情况下,你不需要显式地指定使用哪种代理机制,因为 Spring 会自动为你选择。但是,如果你有特殊需求,可以通过配置来强制使用某种代理机制。
一、Spring AOP 注解概述
1.Spring 的 AOP 功能除了在配置文件中配置一大堆的配置,比如切入点、表达式、通知等等以外,
使用注解的方式更为方便快捷,特别是 Spring boot 出现以后,基本不再使用原先的 beans.xml 等配置文件了,而都推荐注解编程
@Aspect | 切面声明,标注在类、接口(包括注解类型)或枚举上。 |
@Pointcut | 切入点声明,即切入到哪些目标类的目标方法。既可以用 execution 切点表达式, 也可以是 annotation 指定拦截拥有指定注解的方法. value 属性指定切入点表达式,默认为 "",用于被通知注解引用,这样通知注解只需要关联此切入点声明即可,无需再重复写切入点表达式 |
@Before | 前置通知, 在目标方法(切入点)执行之前执行。 value 属性绑定通知的切入点表达式,可以关联切入点声明,也可以直接设置切入点表达式 注意:如果在此回调方法中抛出异常,则目标方法不会再执行,会继续执行后置通知 -> 异常通知。 |
@After | 后置通知, 在目标方法(切入点)执行之后执行 |
@AfterReturning | 返回通知, 在目标方法(切入点)返回结果之后执行. pointcut 属性绑定通知的切入点表达式,优先级高于 value,默认为 "" |
@AfterThrowing | 异常通知, 在方法抛出异常之后执行, 意味着跳过返回通知 pointcut 属性绑定通知的切入点表达式,优先级高于 value,默认为 "" 注意:如果目标方法自己 try-catch 了异常,而没有继续往外抛,则不会进入此回调函数 |
@Around | 环绕通知:目标方法执行前后分别执行一些代码,类似拦截器,可以控制目标方法是否继续执行。 通常用于统计方法耗时,参数校验等等操作。 |
2、上面这些 AOP 注解都是位于 aspectjweaver 依赖中;对于习惯了 Spring 全家桶编程的人来说,并不是需要直接引入 aspectjweaver 依赖,因为 spring-boot-starter-aop 组件默认已经引用了 aspectjweaver 来实现 AOP 功能。换句话说 Spring 的 AOP 功能就是依赖的 aspectjweaver !
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-aop --> <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId><version>2.1.4.RELEASE</version> </dependency>
3.AOP 底层是通过 Spring 提供的的动态代理技术实现的,在运行期间动态生成代理对象,代理对象方法执行时进行增强功能的介入,再去调用目标对象的方法,从而完成功能的增强。主要使用 JDK 动态代理与 Cglib 动态代理;所以如果目标类不是 Spring 组件,则无法拦截,如果是 类名.方法名 方式调用,也无法拦截。
二、@Aspect 快速入门
1、@Aspect 常见用于记录日志、异常集中处理、权限验证、Web 参数校验、事务处理等等2、要想把一个类变成切面类,只需3步:1)在类上使用 @Aspect 注解使之成为切面类2)切面类需要交由 Spring 容器管理,所以类上还需要有 @Service、@Repository、@Controller、@Component 等注解2)在切面类中自定义方法接收通知3、AOP 的含义就不再累述了,下面直接上示例:
/*** 切面类,用于处理日志、参数校验等** @author songwp* @date 2020-04-27*/ @Aspect @Component @Slf4j public class HandleAspect {/*** @Pointcut :切入点声明,即切入到哪些目标方法。value 属性指定切入点表达式,默认为 ""。* 用于被下面的通知注解引用,这样通知注解只需要关联此切入点声明即可,无需再重复写切入点表达式* <p>* 切入点表达式常用格式举例如下:* - * com.songwp.aspect.EmpService.*(..)):表示 com.songwp.aspect.EmpService 类中的任意方法* - * com.songwp.aspect.*.*(..)):表示 com.songwp.aspect 包(不含子包)下任意类中的任意方法* - * com.songwp.aspect..*.*(..)):表示 com.songwp.aspect 包及其子包下任意类中的任意方法* </p>* value 的 execution 可以有多个,使用 || 隔开.*/@Pointcut("execution(public * com.songwp.controller.*.*(..))")public void aopPointCut() {}/*** 前置通知:目标方法执行之前执行以下方法体的内容。* value:绑定通知的切入点表达式。可以关联切入点声明,也可以直接设置切入点表达式* <br/>* * @param joinPoint:提供对连接点处可用状态和有关它的静态信息的反射访问<br/> <p>* * * Object[] getArgs():返回此连接点处(目标方法)的参数,目标方法无参数时,返回空数组* * * Signature getSignature():返回连接点处的签名。* * * Object getTarget():返回目标对象* * * Object getThis():返回当前正在执行的对象* * * StaticPart getStaticPart():返回一个封装此连接点的静态部分的对象。* * * SourceLocation getSourceLocation():返回与连接点对应的源位置* * * String toLongString():返回连接点的扩展字符串表示形式。* * * String toShortString():返回连接点的缩写字符串表示形式。* * * String getKind():返回表示连接点类型的字符串* * * </p>*/@Before("aopPointCut()")public void beforeAdvice() {System.out.println("前置通知执行");}/*** 后置通知:目标方法执行之后执行以下方法体的内容,不管目标方法是否发生异常。* value:绑定通知的切入点表达式。可以关联切入点声明,也可以直接设置切入点表达式*/@After("aopPointCut()")public void afterAdvice() {System.out.println("后置通知执行");}/*** 返回通知:目标方法返回后执行以下代码* value 属性:绑定通知的切入点表达式。可以关联切入点声明,也可以直接设置切入点表达式* pointcut 属性:绑定通知的切入点表达式,优先级高于 value,默认为 ""* returning 属性:通知签名中要将返回值绑定到的参数的名称,默认为 ""** @param joinPoint :提供对连接点处可用状态和有关它的静态信息的反射访问*/@AfterReturning("execution(* com.songwp.service.impl.OperateLogServiceImpl.*(..))")public void logAfterReturning(JoinPoint joinPoint) {System.out.println("返回后通知: " + joinPoint.getSignature().getName());}/*** 异常通知:目标方法发生异常的时候执行以下代码,此时返回通知不会再触发* value 属性:绑定通知的切入点表达式。可以关联切入点声明,也可以直接设置切入点表达式* pointcut 属性:绑定通知的切入点表达式,优先级高于 value,默认为 ""* throwing 属性:与方法中的异常参数名称一致,** @param ex:捕获的异常对象,名称与 throwing 属性值一致*/@AfterThrowing(pointcut = "execution(* com.songwp.service.impl.OperateLogServiceImpl.*(..))", throwing = "ex")public void logAfterThrowing(JoinPoint joinPoint, Throwable ex) {System.out.println("异常后通知: " + joinPoint.getSignature().getName() + ", Exception: " + ex);}/*** 环绕通知* 1、@Around 的 value 属性:绑定通知的切入点表达式。可以关联切入点声明,也可以直接设置切入点表达式* 2、Object ProceedingJoinPoint.proceed(Object[] args) 方法:继续下一个通知或目标方法调用,返回处理结果,如果目标方法发生异常,则 proceed 会抛异常.* 3、假如目标方法是控制层接口,则本方法的异常捕获与否都不会影响目标方法的事务回滚* 4、假如目标方法是控制层接口,本方法 try-catch 了异常后没有继续往外抛,则全局异常处理 @RestControllerAdvice 中不会再触发** @param joinPoint* @return* @throws Throwable*/@Around("execution(* com.songwp.service.impl.OperateLogServiceImpl.*(..))")public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {this.checkRequestParam(joinPoint);System.out.println("环绕通知: " + joinPoint.getSignature().getName());// 继续执行方法Object result = joinPoint.proceed();System.out.println("环绕通知: " + joinPoint.getSignature().getName());return result;}/*** 参数校验,防止 SQL 注入** @param joinPoint*/private void checkRequestParam(ProceedingJoinPoint joinPoint) {Object[] args = joinPoint.getArgs();if (args == null || args.length <= 0) {return;}String params = Arrays.toString(joinPoint.getArgs()).toUpperCase();String[] keywords = {"DELETE ", "UPDATE ", "SELECT ", "INSERT ", "SET ", "SUBSTR(", "COUNT(", "DROP ","TRUNCATE ", "INTO ", "DECLARE ", "EXEC ", "EXECUTE ", " AND ", " OR ", "--"};for (String keyword : keywords) {if (params.contains(keyword)) {log.error("参数存在SQL注入风险,其中包含非法字符 {}.", keyword);throw new RuntimeException("参数存在SQL注入风险:params=" + params);}}} }
三、@Aspect 切面不生效原因
确保切面类被Spring管理:在切面类上添加 @Service、@Repository、@Controller、@Component 等注解 |
检查路径设置:确保切面类被 @ComponentScan 注解扫描到。即有没有被Spring容器管理。可以使用 @PostConstruct注解测试。 |
检查切面表达式:确保切面表达式正确无误,能够匹配到目标方法。 |
特别注意: 比如定义了一个 AOP 切面(@Pointcut)拦截 ServiceA 中的方法 B,当从其他类调用方法 B 时(比如 Controller 层),会正常切入拦截,而从本类其他方法中调用方法 B 时,无法切入拦截,因为此时默认并不是通过代理对象调用的,而是直接通过 this 对象来调的。可以参考@EnableAspectJAutoProxy注解。 |
总结:AOP的高级特性使得开发者能够以声明式的方式处理复杂的应用场景。通过灵活使用切入点表达式和正则表达式,可以在Spring AOP中实现精确的连接点匹配。此外,AOP在性能监控、日志记录、事务管理等场景中的应用,展示了其在提高代码模块化和可维护性方面的强大能力。
相关文章:

Spring基于注解实现 AOP 切面功能
前言 在Spring AOP(Aspect-Oriented Programming)中,动态代理是常用的技术之一,用于在运行时动态地为目标对象生成代理对象, 并拦截其方法调用。Spring AOP 默认使用两种类型的动态代理机制:JDK 动态代理和…...

设计模式 更新ing
设计模式 1、六大原则1.1 单一设计原则 SRP1.2 开闭原则1.3 里氏替换原则1.4 迪米特法则1.5 接口隔离原则1.6 依赖倒置原则 2、工厂模式 1、六大原则 1.1 单一设计原则 SRP 一个类应该只有一个变化的原因 比如一个视频软件,区分不同的用户级别 包括访客࿰…...

Elasticsearch 进阶
核心概念 索引(Index) 一个索引就是一个拥有几分相似特征的文档的集合。比如说,你可以有一个客户数据的索引,另一个产品目录的索引,还有一个订单数据的索引。一个索引由一个名字来标识(必须全部是小写字母),并且当我们要对这个索…...

【AI】Sklearn
长期更新,建议关注、收藏、点赞。 友情链接: AI中的数学_线代微积分概率论最优化 Python numpy_pandas_matplotlib_spicy 建议路线:机器学习->深度学习->强化学习 目录 预处理模型选择分类实例: 二分类比赛 网格搜索实例&…...

通过 JNI 实现 Java 与 Rust 的 Channel 消息传递
做纯粹的自己。“你要搞清楚自己人生的剧本——不是父母的续集,不是子女的前传,更不是朋友的外篇。对待生命你不妨再大胆一点,因为你好歹要失去它。如果这世上真有奇迹,那只是努力的另一个名字”。 一、crossbeam_channel 参考 crossbeam_channel - Rust crossbeam_channel…...

【老白学 Java】对象的起源 Object
对象的起源 Object 文章来源:《Head First Java》修炼感悟。 上一篇文章中,老白学习了抽象类和抽象方法,不禁感慨,原来 Java 还可以这样玩。 同时又有了新的疑问,这些父类从何而来的? 本篇文章老白来聊一聊…...

Ubuntu Linux操作系统
一、 安装和搭建 Thank you for downloading Ubuntu Desktop | Ubuntu (这里我们只提供一个下载地址,详细的下载安装可以参考其他博客) 二、ubuntu的用户使用 2.1 常规用户登陆方式 在系统root用户是无法直接登录的,因为root用户的权限过…...

SpringBoot 打造的新冠密接者跟踪系统:企业复工复产防疫保障利器
摘 要 信息数据从传统到当代,是一直在变革当中,突如其来的互联网让传统的信息管理看到了革命性的曙光,因为传统信息管理从时效性,还是安全性,还是可操作性等各个方面来讲,遇到了互联网时代才发现能补上自古…...

嵌入式Linux(SOC带GPU树莓派)无窗口系统下搭建 OpenGL ES + Qt 开发环境,并绘制旋转金字塔
树莓派无窗口系统下搭建 OpenGL ES Qt 开发环境,并绘制旋转金字塔 1. 安装 OpenGL ES 开发环境 运行以下命令安装所需的 OpenGL ES 开发工具和库: sudo apt install cmake mesa-utils libegl1-mesa-dev libgles2-mesa-dev libdrm-dev libgbm-dev2. 安…...

webGL入门教程_06变换矩阵与绕轴旋转总结
变换矩阵与绕轴旋转总结 目录 1. 变换矩阵简介2. 平移矩阵3. 缩放矩阵4. 旋转矩阵 4.1 绕 Z 轴旋转4.2 绕 X 轴旋转4.3 绕 Y 轴旋转 5. 组合变换矩阵6. 结论 1. 变换矩阵简介 在计算机图形学中,变换矩阵用于在三维空间中对物体进行操作,包括ÿ…...

生成树详解(STP、RSTP、MSTP)
目录 1、STP 1.概述 2.基本概念 3.端口角色及其作用 4.报文结构 5.STP的端口状态 6.三种定时器 7.STP选举步骤 8.配置BPDU的比较原则 9.TCN BPDU 10.临时环路的问题 11.传统STP的不足 拓扑变更处理过程 2、RSTP 1.端口角色 2.端口状态 3.P/A(Propo…...

【QNX+Android虚拟化方案】128 - QNX 侧触摸屏驱动解析
【QNX+Android虚拟化方案】128 - QNX 侧触摸屏驱动解析 一、QNX 侧触摸屏配置基于原生纯净代码,自学总结 纯技术分享,不会也不敢涉项目、不泄密、不传播代码文档!!! 本文禁止转载分享 !!! 汇总链接:《【QNX+Android虚拟化方案】00 - 系列文章链接汇总》 本文链接:《【…...

C#中的集合初始化器
C#中的集合初始化器是一种简洁的语法,允许在声明集合的同时初始化其元素。这种语法特别适用于初始化实现了IEnumerable接口并具有Add方法的集合类型,如List<T>、Dictionary<TKey, TValue>等。 集合初始化器的基本用法 集合初始化器的基本语…...

cartographer建图与定位应用
文章目录 前言一、安装cartographer1.安装环境2.源码编译2.1 下载2.2 编译 二、gazebo仿真2d建图0.准备仿真环境1.编写lua文件2.编写启动文件3.建图保存 三、cartographer定位 move_base导航3.1 编写启动文件3.2 启动launch 总结 前言 本文介绍cartographer在ubuntu18.04下的…...

专业解析 .bashrc 中 ROS 工作空间的加载顺序及其影响 ubuntu 机器人
专业解析 .bashrc 中 ROS 工作空间的加载顺序及其影响 在使用 ROS(Robot Operating System)进行开发时,通常会涉及多个 Catkin 工作空间(Catkin Workspace)。这些工作空间包含不同的 ROS 包和节点,可能相互…...

Apache Doris 现行版本 Docker-Compose 运行教程
特别注意!Doris On Docker 部署方式仅限于开发环境或者功能测试环境,不建议生产环境部署! 如有生产环境或性能测试集群部署诉求,请使用裸机/虚机部署或K8S Operator部署方案! 原文阅读:Apache Doris 现行版…...

Flink四大基石之窗口(Window)使用详解
目录 一、引言 二、为什么需要 Window 三、Window 的控制属性 窗口的长度(大小) 窗口的间隔 四、Flink 窗口应用代码结构 是否分组 Keyed Window --键控窗 Non-Keyed Window 核心操作流程 五、Window 的生命周期 分配阶段 触发计算 六、Wi…...

NGINX配置https双向认证(自签一级证书)
一 生成自签证书 以下是生成自签证书(包括服务端和客户端的证书)的步骤,以下命令执行两次,分别生成客户端和服务端证书和私钥。具体执行可以先建两个目录client和server,分别进入到这两个目录下执行下面的命令。 生成私钥: 首先&…...

Flink双流Join
在离线 Hive 中,我们经常会使用 Join 进行多表关联。那么在实时中我们应该如何实现两条流的 Join 呢?Flink DataStream API 为我们提供了3个算子来实现双流 join,分别是: join coGroup intervalJoin 下面我们分别详细看一下这…...

【数据结构实战篇】用C语言实现你的私有队列
🏝️专栏:【数据结构实战篇】 🌅主页:f狐o狸x 在前面的文章中我们用C语言实现了栈的数据结构,本期内容我们将实现队列的数据结构 一、队列的概念 队列:只允许在一端进行插入数据操作,在另一端…...

基于web的海贼王动漫介绍 html+css静态网页设计6页+设计文档
📂文章目录 一、📔网站题目 二、✍️网站描述 三、📚网站介绍 四、🌐网站演示 五、⚙️网站代码 🧱HTML结构代码 💒CSS样式代码 六、🔧完整源码下载 七、📣更多 一、&#…...

2022 年 9 月青少年软编等考 C 语言三级真题解析
目录 T1. 课程冲突T2. 42 点思路分析T3. 最长下坡思路分析T4. 吃糖果思路分析T5. 放苹果思路分析T1. 课程冲突 此题为 2021 年 9 月三级第一题原题,见 2021 年 9 月青少年软编等考 C 语言三级真题解析中的 T1。 T2. 42 点 42 42 42 是: 组合数学上的第 5 5 5 个卡特兰数字…...

机器学习算法(六)---逻辑回归
常见的十大机器学习算法: 机器学习算法(一)—决策树 机器学习算法(二)—支持向量机SVM 机器学习算法(三)—K近邻 机器学习算法(四)—集成算法 机器学习算法(五…...

计算机科学中的主要协议
1、主要应用层协议: HTTP、FTP、SMTP、POP、IMAP、DNS、TELNET和SSH等 应用层协议的主要功能是支持网络应用,定义了不同应用程序之间的通信规则。它们负责将用户操作转换为网络可以理解的数据格式,并通过传输层进行传输。应用层协议直接与用…...

下载maven 3.6.3并校验文件做md5或SHA512校验
一、下载Apache Maven 3.6.3 Apache Maven 3.6.3 官方下载链接: 二进制压缩包(推荐): ZIP格式: https://archive.apache.org/dist/maven/maven-3/3.6.3/binaries/apache-maven-3.6.3-bin.zipTAR.GZ格式: https://archive.apache.org/dist/…...

【Android】View工作原理
View 是Android在视觉上的呈现在界面上Android提供了一套GUI库,里面有很多控件,但是很多时候我们并不满足于系统提供的控件,因为这样就意味这应用界面的同类化比较严重。那么怎么才能做出与众不同的效果呢?答案是自定义View&#…...

TIE算法具体求解-为什么是泊松方程和傅里叶变换
二维泊松方程的通俗理解 二维泊松方程 是偏微分方程的一种形式,通常用于描述空间中某个标量场(如位相场、电势场)的分布规律。其一般形式为: ∇ 2 ϕ ( x , y ) f ( x , y ) \nabla^2 \phi(x, y) f(x, y) ∇2ϕ(x,y)f(x,y) 其…...

postman中获取随机数、唯一ID、时间日期(包括当前日期增减)截取指定位数的字符等
在Postman中,您可以使用内置的动态变量和编写脚本的方式来获取随机数、唯一ID、时间日期以及截取指定位数的字符。以下是具体的操作方法: 一、postman中获取随机数、唯一ID、时间日期(包括当前日期增减)截取指定位数的字符等 获取…...

【计算机网络】实验3:集线器和交换器的区别及交换器的自学习算法
实验 3:集线器和交换器的区别及交换器的自学习算法 一、 实验目的 加深对集线器和交换器的区别的理解。 了解交换器的自学习算法。 二、 实验环境 • Cisco Packet Tracer 模拟器 三、 实验内容 1、熟悉集线器和交换器的区别 (1) 第一步:构建网络…...

flink学习(14)—— 双流join
概述 Join:内连接 CoGroup:内连接,左连接,右连接 Interval Join:点对面 Join 1、Join 将有相同 Key 并且位于同一窗口中的两条流的元素进行关联。 2、Join 可以支持处理时间(processing time)和事件时…...