从新手到高手:Spring AOP的进阶指南

目录

一、AOP简介

        1.1  AOP入门案例

        1.2 AOP 优点

二、核心概念

        2.1 切面(Aspect)

        2.2 切点(PointCut)

       2.3 通知(Advice)

        2.4 织入(Weaving)

三、AOP 原理

   3.1 CGLIB 与 JDK动态代理对比

        3.2 切面优先级

四、总结


一、AOP简介

        AOP(Aspect-Oriented Programming) 面向切面编程,是一种编程范式,通过预编译和运行期间动态代理实现程序功能统一维护的一种技术。下面通过一个简单的案例来了解下AOP如何使用。

        1.1  AOP入门案例

        假如有这么一个需求,需要你来统计方法的执行时长,这也是性能检测的常用手段。如果不使用 AOP 的话,你大概率会这样做。

public void methodA() {long startTime = System.currentTimeMillis();// 业务代码 long endTime = System.currentTimeMillis();// 计算代码执行时长 endTime - startTime   
}

        这样做当然没有错,但是如果有很多方法需要你去统计呢,每个方法内都需要有相同的代码,看起来很麻烦。AOP 出现后在很大程度上缓解了这种臃肿的代码,只需要一个注解就可以搞定。

        首先需要添加依赖:

         <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId></dependency>
// 统计方法执行时长的注解
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ExecutionTimeStatistic {}@Slf4j
@Aspect
@Component
public class ExecutionTimeAspect {@Pointcut("@annotation(com.***.ExecutionTimeStatistic)")public void pointCut() {}@Around("pointCut()")public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {Object obj = null;try {ExecutionTimeStatistic executionTime = getExecutionTime(joinPoint);if (Objects.isNull(executionTime)) {return joinPoint.proceed();}// 获取方法名String methodName = joinPoint.getSignature().getName();// 获取类名String className = joinPoint.getSignature().getDeclaringTypeName();StopWatch stopWatch = new StopWatch("methodName");stopWatch.start();obj = joinPoint.proceed();stopWatch.stop();log.info("执行方法耗时统计, 执行方法为:{}#{}, 耗时:{}", className, methodName, stopWatch.getTotalTimeMillis() + "ms");} catch (Throwable throwable) {log.warn("统计方法执行耗时异常", throwable);throw throwable;}return obj;}private static ExecutionTimeStatistic getExecutionTime(JoinPoint joinPoint) {Signature signature = joinPoint.getSignature();MethodSignature methodSignature = (MethodSignature) signature;Method method = methodSignature.getMethod();if (method != null) {return method.getAnnotation(ExecutionTimeStatistic.class);}return null;}}

        如果需要统计某一个方法的执行时长,只需要在方法上加上 @ExecutionTimeStatistic 注解即可,是不是很简单呢?

        通过这个案例就凸显出 AOP 的几个优点。

        1.2 AOP 优点

        为什么需要 AOP,它有什么优点呢?

  1. 减少代码重复:在传统 OOP 中,横切关注点(如日志记录、性能统计、安全控制等)通常会在多个类中重复出现。AOP 出现后可以将这些关注点模块化为切面,避免重复代码,提高代码可维护性。
  2. 提高模块化:AOP 将模块化的代码抽取出来,业务逻辑更加干净简介。
  3. 易于维护和扩展:修改一个横切关注点只需要再一个地方修改即可,简单便捷。如果有新功能的话,新扩展横切面就好,无需修改原有业务代码。
  4. 提高代码可读性:通过将横切关注点分离出来,主业务逻辑变得更加清晰,提高了代码的可读性。

        总之,AOP通过提供一种新的方式来组织代码,使得程序结构更加清晰、易于维护,并且能够更好地适应变化。

二、核心概念

        下面介绍下 AOP 的核心概念,通过对这些概念的理解有助于设计出更好的切面程序。

        2.1 切面(Aspect)

        切面是 AOP 中的关键概念,它代表了一个横切关注点的模块化实现。一个切面通常包含了多个通知(advice),这些通知会在特定的连接点(join point)上执行。切面可以用来封装那些分散在整个应用程序中的公共行为,如日志记录、事务管理、权限检查等。如下图:

        切面的定义如下:

// 切面定义
@Aspect
@Component
public class ExecutionTimeAspect {}

        2.2 切点(PointCut)

        切入点(Pointcut)定义了通知应该在哪些连接点上执行。它是通过一个表达式来定义的,该表达式指定了哪些连接点满足条件。切入点可以使用Spring AOP的表达式语言来定义。

        切点定义如下:

@Pointcut("@annotation(com.***.ExecutionTimeStatistic)")
public void pointCut() {}

       2.3 通知(Advice)

        通知是在切面中的一个动作,它定义了在特定的连接点上要执行的行为,Spring AOP 支持多种类型的通知。

通知类型描述
@Before用于定义前置通知,相当于BeforeAdvice,在连接点之前执行的通知
@AfterReturning用于定义后置通知,在连接点成功执行之后通知
@Around用于定义环绕通知,在连接点之前和之后执行的通知,可以控制连接点是否执行
@AfterThrowing用于定义异常通知来处理程序中未处理的异常,在连接点抛出异常之后执行的通知。程序正常执行时不会通知
@After用于定义最终通知,无论连接点是否最终执行都会通知

        总结来说,Spring AOP中注解的执行顺序是  @Before-> @Around(开始部分) -> 目标方法执行 -> @Around(结束部分) -> @AfterReturning(如果成功)或 @AfterThrowing(如果异常) -> @After。

        2.4 织入(Weaving)

        织入指的是将切面(Aspect)和通知(Advice)插入到应用程序的目标对象的方法执行的流程中。简单来说,就是将切面中的横切关注点与应用程序的主要业务逻辑进行整合。

        织入可以再不同的时机发生,主要有三种类型:

  • 编译时织入:在编译阶段,AOP 代理会直接修改源代码或字节码,将切面的代码织入到目标类方法中。这种方式的优点是在编译期就完成了织入,避免了运行时的额外开销,但是需要专门的工具来支持。
  • 加载时织入:在类加载到 JVM 时织入,使用 Agent  技术可以再类加载时修改字节码。
  • 运行时织入:在应用程序运行时动态的创建代理对象,并将切面织入到代理对象中,Spring 就是采用的这种方式,缺点是在运行期会有一定的开销。

        下面来看下 Spring AOP 的实现原理。

三、AOP 原理

        上面讲了织入的方式,Spring AOP 默认的织入方式是运行时织入的,那它是如何实现的呢?

        AOP 的原理是基于动态代理技术实现的,Spring AOP 默认使用动态代理来实现切面(Aspect)中的通知(Advice)在目标对象的方法调用前后插入的功能。

        动态代理是运行时动态创建代理对象的一种技术,Spring 支持两种动态代理技术。

        JDK 动态代理:当目标对象实现了至少一个接口时,Spring 使用 JDK 动态代理技术来创建代理对象。JDK 动态代理是通过 java.lang.reflect.Proxy 类和 InvocationHandler 接口实现的。

        CGLIB 动态代理:当目标对象没有实现任何接口时,Spring 使用 CGLIB(Code Generation Library)来创建代理,CGLIB 是一个代码生成库,它可以动态生成类的子类。

        Spring AOP 会根据目标对象是否实现了接口来决定使用哪种类型的代理:如果目标对象实现了接口,则使用 JDK 动态代理;如果目标对象没有实现接口,则使用 CGLIB 代理。

        不过在编程中可以指定使用哪种代理方式,在 SpringBoot 启动类上添加 @EnableAspectJAutoProxy 注解并设置 proxyTargetClass 属性来控制代理类型:

@EnableAspectJAutoProxy(proxyTargetClass = true) // 强制使用 CGLIB 代理

   3.1 CGLIB 与 JDK动态代理对比

            CGLIB 和 JDK 动态代理是两种常见的动态代理技术,它们各有特点和适用场景。

  1. 适用场景:JDK动态代理适用于实现了接口的的对象,不需要依赖第三方库,比较简单。CGLIB 适用于任何类,包括没有实现接口的类和final类,需要依赖 CGLIB 库。
  2. 性能对比
    1. 初始化时 JDK 动态代理不需要生成额外的字节码,因此性能较好。
    2. 方法运行时,每次方法调用时,JDK 动态代理都需要通过 InvocationHandler 接口来处理,这可能会引入一定的性能开销。CGLIB 代理通过生成子类的方式实现了方法拦截,因此在方法调用时性能较高。
    3. 内存开销:由于 JDK 动态代理不需要生成额外的字节码,因此内存消耗相对较低。CGLIB 代理生成子类时会增加内存消耗,特别是在大规模应用中,可能会导致内存增加。

        总结,对于少量方法调用时,JDK 动态代理在性能上可能优于 CGLIB 代理。对于大量方法调用时,CGLIB 代理在性能上通常优于 JDK 动态代理。

        3.2 切面优先级

        有时在一个方法上可能会使用多个切面,那执行时这些切面的顺序如何确定呢?可以通过 @Order 注解来制定切面的顺序来执行。

四、总结

        Spring AOP 提供了一种优雅的方式来处理横切关注点,如日志记录、性能监控、安全性检查、事务管理和数据校验等。通过使用AOP,可以将这些关注点从业务逻辑中分离出来,提高代码的可维护性和可读性。在实际项目中,根据具体需求选择合适的AOP实现方式,可以显著提高开发效率和系统的可靠性。

往期经典推荐

从手动到智能: 动态线程池讲解_动态线程池技术-CSDN博客

一文掌握Java动态代理的奥秘与应用场景_java动态代理使用场景-CSDN博客

Sentinel与Nacos强强联合,构建微服务稳定性基石的重要实践_sentinel nacos-CSDN博客

TiDB高手进阶:揭秘自增ID热点现象与高级调优技巧_tidb 自增id-CSDN博客

SpringBoot项目并发处理大揭秘,你知道它到底能应对多少请求洪峰?_一个springboot能支持多少并发-CSDN博客

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/451878.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

在各大媒体报纸上刊登自己的文章用什么投稿方法发表快?

在职场中,信息宣传是每个单位的重要工作,而每个月的考核投稿任务更是让我深感压力。作为一名普通员工,我常常面临着如何在各大媒体上顺利发表文章的问题。起初,我选择了传统的邮箱投稿方式,然而这条路却让我陷入了无尽的焦虑和挫败之中。 刚开始投稿时,我满怀激情,认真撰写每一…

[论文笔记]HERMES 3 TECHNICAL REPORT

引言 今天带来论文HERMES 3 TECHNICAL REPORT&#xff0c;这篇论文提出了一个强大的工具调用模型&#xff0c;包含了训练方案介绍。同时提出了一个函数调用标准。 为了简单&#xff0c;下文中以翻译的口吻记录&#xff0c;比如替换"作者"为"我们"。 聊天模…

数据库事务

为了保证一致性 1.ACID 事务具有四个基本特性&#xff0c;也就是通常所说的 ACID 特性&#xff0c;即原子性&#xff08;Atomicity&#xff09;、一致性&#xff08;Consistency&#xff09;、隔离性&#xff08;Isolation&#xff09;和持久性&#xff08;Durability&#x…

算法: 模拟题目练习

文章目录 模拟替换所有的问号提莫攻击Z 字形变换外观数列数青蛙 总结 模拟 替换所有的问号 按照题目的要求写代码即可~ public String modifyString(String ss) {int n ss.length();if (n 1) {return "a";}char[] s ss.toCharArray();for (int i 0; i < n; i…

使用Python和Proxy302代理IP高效采集Bing图片

目录 项目背景一、项目准备环境配置 二、爬虫设计与实现爬虫设计思路目标网站分析数据获取流程 代码实现1. 初始化爬虫类&#xff08;BingImageSpider&#xff09;2. 创建存储文件夹3. 获取图像链接4. 下载图片5. 使用Proxy302代理IP6. 主运行函数 运行截图 三、总结 项目背景 …

SpringMVC一个拦截器和文件上传下载的完整程序代码示例以及IDEA2024部署报错 找不到此 Web 模块的 out\artifacts\..问题

一、SpringMVC一个拦截器和文件上传下载的完整程序代码示例 本文章是一个 SpringMVC拦 截器和文件上传下载的完整程序代码示例&#xff0c;使用的开发工具是 IntelliJ IDEA 2024.1.6 (Ultimate Edition)&#xff0c; 开发环境是 OpenJDK-21 java version 21.0.2。Tomcatt版本为…

【C++篇】类与对象的秘密(上)

目录 引言 一、类的定义 1.1类定义的基本格式 1.2 成员命名规范 1.3 class与struct的区别 1.4 访问限定符 1.5 类的作用域 二、实例化 2.1 类的实例化 2.2 对象的大小与内存对齐 三、this 指针 3.1 this指针的基本用法 3.2 为什么需要this指针&#xff1f; 3.3 t…

基于SSM+微信小程序的房屋租赁管理系统(房屋2)

&#x1f449;文末查看项目功能视频演示获取源码sql脚本视频导入教程视频 1、项目介绍 基于SSM微信小程序的房屋租赁管理系统实现了有管理员、中介和用户。 1、管理员功能有&#xff0c;个人中心&#xff0c;用户管理&#xff0c;中介管理&#xff0c;房屋信息管理&#xff…

Java基础-IO基础

IO是指input/output&#xff0c;即输入和输出。输入和输出是以内存为中心的&#xff1a; input 从外部往内存输入数据&#xff0c;比如硬盘中的数据写入内存等。 output 从内存往外输出数据&#xff0c;比如内存数据写入硬盘等。 File File类表示一个文件或者一个目录。使用F…

【服务器虚拟化是什么?】

&#x1f3a5;博主&#xff1a;程序员不想YY啊 &#x1f4ab;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f917;点赞&#x1f388;收藏⭐再看&#x1f4ab;养成习惯 ✨希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出…

父子元素中只有子元素设置margin-bottom的问题

问题代码如下所示 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>Title</title><style>.div1 {background-color: red;width: 80px;height: 80px;border: 1px solid orange;}.div2 {bac…

【飞腾加固服务器】全国产化解决方案:飞腾FT2000+/64核,赋能关键任务保驾护航

在信息安全和自主可控的时代背景下&#xff0c;国产化设备的需求与日俱增&#xff0c;尤其是在国防、航空航天、能源和其他关键行业。高可靠性和极端环境设计的国产加固服务器&#xff0c;搭载强大的飞腾FT2000/64核处理器&#xff0c;全面满足国产自主可控的严苛要求。 性能强…

光伏电站设计之辐照度效果(threejs实现)

类似 solaredge里面的日照度效果 1、由经纬度和屋顶朝向获取&#xff08;参考pvlib&#xff09;当前地区的辐照度值&#xff0c; 2、根据辐照度值插值获取对应辐照度的颜色。 3、计算片段着色器里面计算每个顶点的遮挡率和紫色混合 4、计算鼠标移动中的投射屋顶位置辐照度&…

Ansible自动化运维管理工具

一、Ansible 1.1、自动化运维管理工具有哪些&#xff1f; 工具架构语言使用情况Ansible无clientpython 协议用ssh95%puppetC/Sruby 协议用http基本不用chefC/Sruby 协议用http基本不用saltstackC/Spython 协议用ssh5% 1.2、Ansible简介 Ansible是一个基于Py…

网易翻译工具解析!这几大翻译器值得一试!

翻译工具的出现&#xff0c;使得跨语言沟通变得更加便捷。本文将为您推荐几款优秀的翻译工具&#xff0c;包括福昕在线翻译、福昕翻译客户端、海鲸AI翻译和网易有道翻译&#xff0c;帮助您在学习、工作和生活中轻松应对语言挑战。 福昕在线翻译 直达链接&#xff08;复制到浏…

c4d渲染和3d渲染有什么区别?c4d和3dmax哪个容易学?

在现代设计和创意产业中&#xff0c;3D渲染技术是不可或缺的一部分。它能够帮助设计师和艺术家将他们的创意转化为逼真的视觉效果&#xff0c;从而更好地展示和传达他们的想法。在众多3D渲染软件中&#xff0c;C4D渲染和3D Max是两款备受关注的软件。 本文将探讨C4D渲染和3D渲…

深度学习领域,你心目中 idea 最惊艳的论文是哪篇?

深度学习发展至今&#xff0c;共经历了三次浪潮&#xff0c;20 世纪40年代到60年代深度学习的雏形出现在控制论(cybernetics)中&#xff0c;20 世纪 80 年代 到 90 年代深度学习表现为 联结主义(connectionism)&#xff0c;直到 2006 年&#xff0c;才真正以深度学习之名复兴。…

Android中的内容提供者

目录 1.创建内容提供者 1--手动创建一个Android应用程序 2--创建自定义的内容提供者 2.访问其他应用程序 1. 解析URI 2. 查询数据 3. 遍历查询结果 3)案例:读取手机通信录 1.声明权限 2.activity_main.xml文件内容 3.my_phone_list.xml文件内容 4.定义PhoneInfo实体 5.定义MyPh…

现代大数据架构Kappa

现代大数据架构中的Kappa架构是一种处理大数据的架构&#xff0c;它作为Lambda架构的替代方案出现&#xff0c;旨在简化数据处理流程。以下是对Kappa架构的详细介绍&#xff1a; 一、核心思想 Kappa架构的核心思想是简化数据处理流程&#xff0c;通过使用单一的流处理层来同时…

就是这个样的粗爆,手搓一个计算器:热量计算器

作为程序员&#xff0c;没有合适的工具&#xff0c;就得手搓一个&#xff0c;PC端&#xff0c;移动端均可适用。废话不多说&#xff0c;直接上代码。 HTML: <div class"calculator"> <label for"weight">体重 (kg):</label> <inpu…