Spring实战 | Spring AOP核心功能分析之葵花宝典
国庆中秋特辑系列文章:
国庆中秋特辑(八)Spring Boot项目如何使用JPA
国庆中秋特辑(七)Java软件工程师常见20道编程面试题
国庆中秋特辑(六)大学生常见30道宝藏编程面试题
国庆中秋特辑(五)MySQL如何性能调优?下篇
国庆中秋特辑(四)MySQL如何性能调优?上篇
国庆中秋特辑(三)使用生成对抗网络(GAN)生成具有节日氛围的画作,深度学习框架 TensorFlow 和 Keras 来实现
国庆中秋特辑(二)浪漫祝福方式 使用生成对抗网络(GAN)生成具有节日氛围的画作
国庆中秋特辑(一)浪漫祝福方式 用循环神经网络(RNN)或长短时记忆网络(LSTM)生成祝福诗词
目录
- 一、Spring AOP 简介
- 二、Spring AOP 原理
- 三、Spring AOP 案例分析
- 四、Spring AOP 提供了两种动态代理方式
Spring AOP(Aspect-Oriented Programming,面向切面编程)是 Spring 框架的一个重要模块。
一、Spring AOP 简介
Spring AOP(Aspect-Oriented Programming,面向切面编程)是 Spring 框架的一个重要模块,用于提供声明式的事务管理、日志记录、性能监控等功能。Spring AOP 底层依赖于 AspectJ 实现,可以与 Spring 框架无缝集成,提供一种更加简单、直观的方式来处理企业应用中的常见问题。
二、Spring AOP 原理
- 代理机制
Spring AOP 采用代理机制实现,可以分为 JDK 动态代理和 CGLIB 动态代理。JDK 动态代理是通过实现目标类的接口,生成目标类的代理对象;CGLIB 动态代理是通过继承目标类,生成目标类的子类作为代理对象。 - 通知(Advice)
通知是 Spring AOP 中实现切面功能的核心,可以分为五种类型:Before、After、AfterReturning、AfterThrowing 和 Around。通知的作用是在目标方法执行前、后或者抛出异常时执行特定的逻辑,实现对目标方法的增强。 - 切入点(Pointcut)
切入点是 Spring AOP 中定义的一个表达式,用于指定哪些方法需要被增强。切点表达式可以使用 AspectJ 语言来编写,非常灵活。通过定义切入点,可以精确地控制哪些方法需要被增强。 - 切面(Aspect)
切面是 Spring AOP 中的一种组件,包含切点和通知。切面可以将通用的逻辑(如日志、事务管理等)封装在一起,便于管理和维护。在 Spring AOP 中,可以通过 XML 配置文件或者 Java 代码来定义切面。 - 自动代理
Spring AOP 框架支持自动代理,可以在运行时自动为指定类生成代理对象。自动代理的核心是 Spring AOP 容器,负责管理代理对象、切面和通知。通过自动代理,可以简化开发者的操作,提高开发效率。
三、Spring AOP 案例分析
以下通过一个简单的案例来演示 Spring AOP 的使用。
- 配置文件
首先,创建一个配置文件 applicationContext.xml,用于定义目标类和切面类。
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"><!-- 定义目标类 --> <bean id="target" class="com.example.TargetClass"></bean><!-- 定义切面类 --> <bean id="aspect" class="com.example.AspectClass"></bean><!-- 开启自动代理 --> <aop:config proxy-target-class="true"> <!-- 指定切入点表达式 --> <aop:aspect ref="aspect"> <aop:before pointcut="execution(* com.example.TargetClass.*(..))" method="com.example.AspectClass.beforeAdvice"></aop:before> <aop:after pointcut="execution(* com.example.TargetClass.*(..))" method="com.example.AspectClass.afterAdvice"></aop:after> </aop:aspect> </aop:config>
</beans>
- 目标类(TargetClass)
目标类是一个简单的计算类,包含两个方法:doAdd 和 doSubtract。
package com.example;
public class TargetClass { public int doAdd(int a, int b) { System.out.println("TargetClass doAdd method called"); return a + b; }public int doSubtract(int a, int b) { System.out.println("TargetClass doSubtract method called"); return a - b; }
}
- 切面类(AspectClass)
切面类包含两个通知方法:beforeAdvice 和 afterAdvice,分别用于在目标方法执行前和执行后执行特定逻辑。
package com.example;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.After;
public class AspectClass { @Before("execution(* com.example.TargetClass.*(..))") public void beforeAdvice(JoinPoint joinPoint) { System.out.println("Before advice: " + joinPoint.getSignature().getName()); }@After("execution(* com.example.TargetClass.*(..))") public void afterAdvice(JoinPoint joinPoint) { System.out.println("After advice: " + joinPoint.getSignature().getName()); }
}
- 测试类(TestClass)
测试类用于测试 Spring AOP 的效果。
package com.example;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestClass { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); TargetClass target = (TargetClass) context.getBean("target");int result1 = target.doAdd(2, 3); int result2 = target.doSubtract(5, 2);System.out.println("Result 1: " + result1); System.out.println("Result 2: " + result2); }
}
运行测试类,输出结果如下:
TargetClass doAdd method called
Before advice: doAdd
After advice: doAdd
Result 1: 5
TargetClass doSubtract method called
Before advice: doSubtract
After advice: doSubtract
Result 2: 3
从输出结果可以看出,在目标方法执行前和执行后分别执行了 beforeAdvice 和 afterAdvice 方法,说明 Spring AOP 已经成功实现了对目标方法的增强。
四、Spring AOP 提供了两种动态代理方式
JDK 动态代理和 CGLIB 动态代理。JDK 动态代理是基于接口实现的,而 CGLIB 动态代理是基于类实现的。这两种代理方式在性能上有一定的差别,JDK 动态代理更适合用于接口较多的场景,而 CGLIB 动态代理则更适合用于类较多的场景。
4.1 下面是一个简单的 Spring AOP JDK 动态代理示例,演示了如何使用 Spring AOP 实现日志切面:
- 首先,创建一个切面类(Aspect),包含一个通知(Advice):
package com.example.aspect;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class LoggingAspect {@Pointcut("execution(* com.example.service.*.*(..))") public void serviceMethods() { }@Before("serviceMethods()") public void logBefore(JoinPoint joinPoint) { System.out.println("Before method: " + joinPoint.getSignature().getName()); }@AfterReturning(pointcut = "serviceMethods()", returning = "result") public void logAfterReturning(JoinPoint joinPoint, Object result) { System.out.println("After returning method: " + joinPoint.getSignature().getName()); System.out.println("Result: " + result); }
}
- 接下来,创建一个目标类(Target Class),包含一个需要增强的方法:
package com.example.service;
import org.springframework.stereotype.Service;
@Service
public class TargetService {public String sayHello(String name) { System.out.println("Hello, " + name); return "Hello, " + name; }
}
- 然后,创建一个 Spring 配置类,启用 AOP 支持,并扫描包含切面和目标类的包:
package com.example;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@Configuration
@EnableAspectJAutoProxy
@ComponentScan(basePackages = {"com.example.aspect", "com.example.service"})
public class AppConfig {
}
- 最后,创建一个测试类,使用 Spring AOP 提供的 API 调用目标类的方法:
package com.example;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.stereotype.Component;
@Component
public class Test {public static void main(String[] args) { ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class); TargetService targetService = context.getBean(TargetService.class); String result = targetService.sayHello("World"); System.out.println("Result: " + result); }
}
运行测试类,你将看到目标方法被切面增强的日志输出。这个示例展示了如何使用 Spring AOP JDK 动态代理实现简单的日志切面,以记录目标方法执行的前后状态。这有助于实现代码的重用和提高可维护性。
4.2 下面是一个简单的 Spring AOP CGLIB 动态代理示例,演示了如何使用 Spring AOP 实现日志切面:
- 首先,创建一个切面类(Aspect),包含一个通知(Advice):
package com.example.aspect;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class LoggingAspect {@Pointcut("execution(* com.example.service.*.*(..))") public void serviceMethods() { }@Before("serviceMethods()") public void logBefore(JoinPoint joinPoint) { System.out.println("Before method: " + joinPoint.getSignature().getName()); }@AfterReturning(pointcut = "serviceMethods()", returning = "result") public void logAfterReturning(JoinPoint joinPoint, Object result) { System.out.println("After returning method: " + joinPoint.getSignature().getName()); System.out.println("Result: " + result); }
}
- 接下来,创建一个目标类(Target Class),包含一个需要增强的方法:
package com.example.service;
import org.springframework.stereotype.Service;
@Service
public class TargetService {public String sayHello(String name) { System.out.println("Hello, " + name); return "Hello, " + name; }
}
- 然后,创建一个 Spring 配置类,启用 AOP 支持,并扫描包含切面和目标类的包:
package com.example;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@Configuration
@EnableAspectJAutoProxy
@ComponentScan(basePackages = {"com.example.aspect", "com.example.service"})
public class AppConfig {
}
- 最后,创建一个测试类,使用 Spring AOP 提供的 API 调用目标类的方法:
package com.example;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.stereotype.Component;
@Component
public class Test {public static void main(String[] args) { ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class); TargetService targetService = context.getBean(TargetService.class); String result = targetService.sayHello("World"); System.out.println("Result: " + result); }
}
运行测试类,你将看到目标方法被切面增强的日志输出。这有助于实现代码的重用和提高可维护性。
需要注意的是,CGLIB 动态代理需要 TargetService 类实现 equals() 和 hashCode() 方法,否则会报错。这是因为 CGLIB 需要生成目标类的代理类,而如果 TargetService 类没有实现 equals() 和 hashCode() 方法,那么生成的代理类将无法正确处理目标类的对象。
相关文章:

Spring实战 | Spring AOP核心功能分析之葵花宝典
国庆中秋特辑系列文章: 国庆中秋特辑(八)Spring Boot项目如何使用JPA 国庆中秋特辑(七)Java软件工程师常见20道编程面试题 国庆中秋特辑(六)大学生常见30道宝藏编程面试题 国庆中秋特辑&…...
linux之/etc/skel目录
/etc/skel目录是在使用useradd添加用户时,一个需要用到的目录,该目录用来存放新建用户时需要拷贝到新建用户家目录下的文件。即:当我们新建新用户时,这个目录下的所有文件会自动被复制到新建用户的家目录下,默认情况下…...
文件介绍---C语言编程
0 Preface/Foreword 1 C文件概述 文件(File)是程序设计中的一个重要的概念。所谓“文件”一般指存储在外部介质上数据的集合。一批数据是以文件的形式存放在外部介质(如磁盘)上。操作系统是以文件为单位对数据进行管理,…...
软考 系统架构设计师系列知识点之特定领域软件体系结构DSSA(6)
接前一篇文章:软考 系统架构设计师系列知识点之特定领域软件体系结构DSSA(5) 所属章节: 第7章. 系统架构设计基础知识 第5节. 特定领域软件体系结构 相关试题 3. 特定领域软件架构(Domain Specific Software Archite…...

TensorFlow入门(二十三、退化学习率)
学习率 学习率,控制着模型的学习进度。模型训练过程中,如果学习率的值设置得比较大,训练速度会提升,但训练结果的精度不够,损失值容易爆炸;如果学习率的值设置得比较小,精度得到了提升,但训练过程会耗费太多的时间,收敛速度慢,同时也容易出现过拟合的情况。 退化学习率 退化学…...

登录中获取验证码的节流
一. 验证码框 <el-input placeholder"请输入验证码" prefix-icon"el-icon-lock" v-model"ruleForm.code"><el-button slot"suffix" :disabled"disabled" type"text" size"mini" click"ch…...
spring boot 实现Minio分片上传
应用场景 分片上传,就是将所要上传的文件,按照一定的大小,将整个文件分隔成多个数据块(我们称之为Part)来进行分别上传,上传完之后再由服务端对所有上传的文件进行汇总整合成原始的文件。 分片上传的场景…...

2023年09月 C/C++(六级)真题解析#中国电子学会#全国青少年软件编程等级考试
C/C编程(1~8级)全部真题・点这里 Python编程(1~6级)全部真题・点这里 第1题:生日相同 在一个有180人的大班级中,存在两个人生日相同的概率非常大,现给出每个学生的名字,出生月日。试…...
docker-compose 部署示例
文章目录 docker-compose文件格式docker-compose 下载 docker-compose文件格式 这个软件的实际很小,只是根据配置文件产生一些docker命令来执行可以。 配置文件本身是yml的格式,如下 version: 3.5services:# Etherpad: real-time collaborative docume…...

新版WordPress插件短视频去水印小程序源码
最新版去水印小程序源码,本版本全开源,是WordPress插件 上传到Wordpress 安装插件 启动之后 绑定自己的小程序id wordpress可以在宝塔一键部署 也可以用我的这个 搭建前我们需要一下东西: 第一个:一台服务器(国内外都可…...

如何提高MES系统的落地成功率?
导 读 ( 文/ 2768 ) 制造执行系统(MES)在现代制造业中扮演着至关重要的角色,但实施MES系统并取得成功并非易事。为了帮助企业提高MES系统的落地成功率,本文将介绍关键的方法和策略。通过深入了解业务需求、有效的团队合作、全面的…...

private key ssh连接服务器
这里用到的软件是PuTTY。 https://www.chiark.greenend.org.uk/~sgtatham/putty/latest.html 保存本地rsa文件后,打开软件PuTTYgen,点击Load导入文件,输入Key passphrase即密码,保存至本地。 随后在PuTTY配置ssh的用户名 来Cred…...
PDF-Word-图片等的互相转换
轻闪PDF客户端 - 功能强大的一站式PDF工具 | PDF编辑、转换、阅读 上面页面支持PDF转换成各类别:鼠标停留在PDF工具,点击转换类型即可在线转换 Word-PDF:word文档打开word文件中,点击文件->另存为->另存为的位置->保存…...

【VR开发】【Unity】0-课程简介和概述
【说明】 这是我录制的一套VR基础开发课程的文字版本,更加便于快速参考。 应大家在后台所提的需求,从今天开始,我计划带给大家一套完整达40课时的VR开发基础课程。 在开始学习前需要注意如下几点: 本教程基于Unity2022.2.1f1版…...
Java面试题-Java核心基础-第三天(基本数据类型)
目录 一、Java的基本数据类型了解吗? 二、基本类型和包装类型的区别 三、包装类型的缓存机制了解吗? 四、自动拆箱和自动装箱了解吗? 五、为什么浮点数在运算的时候会有精度损失的风险? 六、如何解决浮点数在运算时存在的精度…...
Bean容器里的单例是根据什么识别它是同一个类呢?(比如容器里创建了A类,再去用这个A类的时候,Bean容器怎么知道这个就是A类?)
Spring容器中的Bean的识别主要依赖于Bean的名称(ID)和类型。以下是详细解释: Bean的名称(ID):每个Bean在Spring容器中都必须有一个唯一的名称(ID)。这个名称通常在配置文件中、Java…...
简述 happens - before 八大规则
“Happens-Before” 是Java内存模型中的概念,用于描述多线程程序中操作之间的顺序和可见性关系。它定义了一组规则,哪些操作具有可预测的行为。以下是"Happens-Before" 的八大规则: 程序顺序规则(Program Order Rule&am…...
windows flask 多进程高并发
最近在做的一个项目,需要将十几个python函数封装程flask服务供外界调用,每个函数之间没有什么关系,相互独立。虽然感觉不是很难,但因为用的windows系统,遇到的坑比较多,在此一一总结一下。 flask偶尔出现卡…...

【设计模式】十、组合模式
文章目录 案例组合模式基本介绍类图代码 组合模式在 JDK 集合的源码分析组合模式的注意事项和细节 案例 编写程序展示一个学校院系结构:需求是这样,要在一个页面中展示出学校的院系组成,一个学校有多个学院,一个学院有多个系。如…...
React知识点系列(8)-每天10个小知识
目录 1. 在 React 中,什么是受控组件和非受控组件?请解释一下它们之间的区别和适用场景。2. 如何使用 React 的 useReducer Hook 来管理组件状态?请描述一下 useReducer 的工作原理和适用场景。工作原理:适用场景: 3. …...
uniapp 对接腾讯云IM群组成员管理(增删改查)
UniApp 实战:腾讯云IM群组成员管理(增删改查) 一、前言 在社交类App开发中,群组成员管理是核心功能之一。本文将基于UniApp框架,结合腾讯云IM SDK,详细讲解如何实现群组成员的增删改查全流程。 权限校验…...
SkyWalking 10.2.0 SWCK 配置过程
SkyWalking 10.2.0 & SWCK 配置过程 skywalking oap-server & ui 使用Docker安装在K8S集群以外,K8S集群中的微服务使用initContainer按命名空间将skywalking-java-agent注入到业务容器中。 SWCK有整套的解决方案,全安装在K8S群集中。 具体可参…...

8k长序列建模,蛋白质语言模型Prot42仅利用目标蛋白序列即可生成高亲和力结合剂
蛋白质结合剂(如抗体、抑制肽)在疾病诊断、成像分析及靶向药物递送等关键场景中发挥着不可替代的作用。传统上,高特异性蛋白质结合剂的开发高度依赖噬菌体展示、定向进化等实验技术,但这类方法普遍面临资源消耗巨大、研发周期冗长…...

如何在看板中体现优先级变化
在看板中有效体现优先级变化的关键措施包括:采用颜色或标签标识优先级、设置任务排序规则、使用独立的优先级列或泳道、结合自动化规则同步优先级变化、建立定期的优先级审查流程。其中,设置任务排序规则尤其重要,因为它让看板视觉上直观地体…...
Python爬虫(二):爬虫完整流程
爬虫完整流程详解(7大核心步骤实战技巧) 一、爬虫完整工作流程 以下是爬虫开发的完整流程,我将结合具体技术点和实战经验展开说明: 1. 目标分析与前期准备 网站技术分析: 使用浏览器开发者工具(F12&…...
如何为服务器生成TLS证书
TLS(Transport Layer Security)证书是确保网络通信安全的重要手段,它通过加密技术保护传输的数据不被窃听和篡改。在服务器上配置TLS证书,可以使用户通过HTTPS协议安全地访问您的网站。本文将详细介绍如何在服务器上生成一个TLS证…...
Neo4j 集群管理:原理、技术与最佳实践深度解析
Neo4j 的集群技术是其企业级高可用性、可扩展性和容错能力的核心。通过深入分析官方文档,本文将系统阐述其集群管理的核心原理、关键技术、实用技巧和行业最佳实践。 Neo4j 的 Causal Clustering 架构提供了一个强大而灵活的基石,用于构建高可用、可扩展且一致的图数据库服务…...
汇编常见指令
汇编常见指令 一、数据传送指令 指令功能示例说明MOV数据传送MOV EAX, 10将立即数 10 送入 EAXMOV [EBX], EAX将 EAX 值存入 EBX 指向的内存LEA加载有效地址LEA EAX, [EBX4]将 EBX4 的地址存入 EAX(不访问内存)XCHG交换数据XCHG EAX, EBX交换 EAX 和 EB…...
OpenPrompt 和直接对提示词的嵌入向量进行训练有什么区别
OpenPrompt 和直接对提示词的嵌入向量进行训练有什么区别 直接训练提示词嵌入向量的核心区别 您提到的代码: prompt_embedding = initial_embedding.clone().requires_grad_(True) optimizer = torch.optim.Adam([prompt_embedding...
【C++从零实现Json-Rpc框架】第六弹 —— 服务端模块划分
一、项目背景回顾 前五弹完成了Json-Rpc协议解析、请求处理、客户端调用等基础模块搭建。 本弹重点聚焦于服务端的模块划分与架构设计,提升代码结构的可维护性与扩展性。 二、服务端模块设计目标 高内聚低耦合:各模块职责清晰,便于独立开发…...