Java中的动态代理与Spring AOP编程

第一章:引言

大家好,我是小黑,在Java里,动态代理和Spring AOP(面向切面编程)是两个能让代码更加灵活、更加干净的强大工具。作为一名Java程序员,小黑觉得掌握它们对于写出高质量的代码来说非常重要。动态代理让我们能在运行时创建一个实现了一组给定接口的新类,这个过程完全由Java的反射机制控制。而Spring AOP则让我们能在不修改源代码的情况下,增强方法的功能,比如日志记录、性能统计、安全控制等等。

咱们经常听说,要想做好一件事,最重要的是用对方法。在编程世界里,这句话同样适用。通过动态代理和Spring AOP,咱们可以更加聚焦于业务逻辑的实现,而将那些重复的代码逻辑,比如日志记录、权限检查这些,通过AOP的方式统一处理,大大提高了代码的复用性和可维护性。

第二章:动态代理基础

动态代理,这个听起来有点高深的概念,实际上和咱们日常生活中的代理没什么两样。就像咱们有时候会委托旅行社帮咱们订机票、订酒店一样,程序中的动态代理也是帮咱们完成一些任务,但是更智能一些,因为它是在程序运行时动态创建的,完全由Java的反射机制控制。

Java中实现动态代理的方式主要有两种:一种是基于接口的JDK动态代理,另一种是CGLIB动态代理。JDK动态代理是通过实现被代理类的接口,然后在调用实际方法前后加入自己的逻辑来实现的。而CGLIB动态代理,则是通过继承被代理类,覆盖其方法来实现增强功能。

让咱们通过一个简单的例子来看看JDK动态代理是怎么回事。假设有一个接口和一个实现类,接口定义了一个方法,实现类实现了这个方法。小黑现在用动态代理在这个方法调用前后打印一些信息:

interface Greeting {void sayHello(String name);
}class GreetingImpl implements Greeting {public void sayHello(String name) {System.out.println("你好, " + name);}
}class DynamicProxyHandler implements InvocationHandler {private Object target;public DynamicProxyHandler(Object target) {this.target = target;}public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("方法调用前");Object result = method.invoke(target, args);System.out.println("方法调用后");return result;}public static void main(String[] args) {Greeting greeting = (Greeting) Proxy.newProxyInstance(Greeting.class.getClassLoader(),new Class[]{Greeting.class},new DynamicProxyHandler(new GreetingImpl()));greeting.sayHello("世界");}
}

小黑偷偷告诉你一个买会员便宜的网站: 小黑整的视頻会园优惠站

第三章:深入Spring AOP

咱们谈过动态代理后,接下来进入Spring AOP的世界。AOP(面向切面编程)是一种编程范式,它允许咱们将横切关注点(比如日志、事务管理等)与业务逻辑分离,从而使得业务逻辑更加干净、模块化。Spring AOP就是Spring框架提供的一套AOP实现,它利用了动态代理来实现。

首次接触Spring AOP时,咱们可能会对“切面(Aspect)”、“连接点(JoinPoint)”、“通知(Advice)”等术语感到困惑。别担心,小黑来一一解释。

  • 切面(Aspect):一个关注点的模块化,这个关注点可能会横切多个对象。简单来说,就是把咱们想要实现的功能比如日志记录、性能统计封装起来,称之为一个切面。
  • 连接点(JoinPoint):程序执行过程中的某个特定点,比如方法的调用或异常的抛出。在Spring AOP中,一个连接点总是代表一个方法的执行。
  • 通知(Advice):切面在特定连接点执行的动作。有不同类型的通知,比如“前置通知”在方法执行之前执行,“后置通知”在方法执行之后执行等等。

让咱们来看一个简单的例子,演示如何在Spring中定义一个切面,并在方法执行前后添加日志:

// 定义一个切面
@Aspect
@Component
public class LogAspect {// 定义前置通知@Before("execution(* com.example.service.*.*(..))")public void beforeAdvice(JoinPoint joinPoint) {System.out.println("方法执行前:调用" + joinPoint.getSignature().getName() + "方法");}// 定义后置通知@After("execution(* com.example.service.*.*(..))")public void afterAdvice(JoinPoint joinPoint) {System.out.println("方法执行后:调用" + joinPoint.getSignature().getName() + "方法");}
}

在这个例子中,@Aspect标注的类LogAspect定义了一个切面。@Before@After注解定义了前置和后置通知,execution(* com.example.service.*.*(..))是一个切点表达式,表示com.example.service包下所有类的所有方法都是连接点,即在这些方法执行前后,执行相应的通知。

通过这种方式,咱们可以很容易地为业务逻辑添加额外的行为,而不需要修改业务逻辑本身。这不仅使得代码更加模块化,而且提高了代码的复用性和可维护性。

Spring AOP背后的工作原理是动态代理。对于实现了接口的Bean,Spring默认使用JDK动态代理。对于没有实现接口的Bean,则使用CGLIB来创建代理。这一切对开发者来说都是透明的,Spring框架自动处理了这些底层细节。

通过深入了解Spring AOP,咱们可以更好地利用这一强大的编程范式,编写出更加简洁、高效的代码。

第四章:Spring AOP实现机制

继续深入Spring AOP的世界,这一章节咱们聚焦于Spring AOP的实现机制,包括如何在Spring框架中配置和使用AOP,以及它是如何工作的。理解了这些,咱们就能更加灵活地在项目中利用AOP来解决问题了。

在Spring中配置AOP

Spring AOP的配置非常灵活,可以通过XML配置文件,也可以通过注解的方式来实现。由于Spring框架推荐使用注解方式,因为它更简洁、直观,所以小黑这里也主要介绍基于注解的配置方法。

为了启用Spring AOP,咱们需要在配置类上添加@EnableAspectJAutoProxy注解。这个注解会告诉Spring框架,自动代理那些标注了@Aspect注解的类。

@Configuration
@EnableAspectJAutoProxy
public class AppConfig {
}

定义了切面后,咱们就可以在切面类中使用@Aspect注解来标注这个类是一个切面,然后通过@Before@After@Around等注解来定义不同类型的通知。

Spring AOP使用的动态代理技术

正如之前提到的,Spring AOP在底层使用了动态代理技术。具体来说,如果目标对象实现了接口,Spring AOP会默认使用JDK动态代理。如果目标对象没有实现接口,则会使用CGLIB库来创建代理。

JDK动态代理只能代理接口,不支持类。而CGLIB可以在运行时动态生成一个被代理类的子类,通过方法重写的方式来实现代理,因此它不需要接口也能实现代理功能。

使用AspectJ注解实现AOP

AspectJ是一个面向切面的框架,它扩展了Java语言。Spring AOP支持使用AspectJ的注解来定义切面和通知,这使得AOP的实现更加直观和强大。

以下是使用AspectJ注解定义切面和通知的一个简单例子:

@Aspect
@Component
public class LoggingAspect {// 定义一个前置通知@Before("execution(* com.example.service.*.*(..))")public void logBefore(JoinPoint joinPoint) {System.out.println("即将执行方法: " + joinPoint.getSignature().getName());}// 定义一个后置通知@AfterReturning(pointcut = "execution(* com.example.service.*.*(..))", returning = "result")public void logAfterReturning(JoinPoint joinPoint, Object result) {System.out.println("方法执行完成: " + joinPoint.getSignature().getName() + ", 返回值: " + result);}
}

在这个例子中,@Before注解定义了一个前置通知,它会在匹配的方法执行之前执行。@AfterReturning注解定义了一个后置通知,它会在匹配的方法成功执行之后执行,并且可以访问到方法的返回值。

通过这样的方式,咱们可以非常方便地在方法执行的不同阶段织入自己的逻辑,而不需要改动原有的业务代码。这对于实现日志记录、性能监控、事务管理等横切关注点非常有用。

理解Spring AOP的实现机制,对于高效利用这一技术解决实际编程问题非常关键。希望通过本章的介绍,咱们能对Spring AOP有了更深入的理解。

第五章:动态代理与Spring AOP的高级话题

咱们已经掌握了基础的概念和实现方式。现在,让咱们进一步探索一些高级话题,包括性能考量、最佳实践以及如何解决一些常见的问题。

动态代理和Spring AOP的性能考量

在使用动态代理和Spring AOP时,性能是一个不可忽视的话题。虽然动态代理和AOP为咱们提供了极大的便利和灵活性,但是它们也引入了一定的性能开销。比如,动态代理的方法调用比直接调用慢,因为它需要通过反射机制来实现;Spring AOP的通知执行也会增加执行时间。

为了最小化性能开销,咱们可以采取一些措施:

  • 尽量减少通知的复杂度:在通知中尽量避免执行复杂的逻辑。
  • 合理选择通知类型:例如,如果不需要方法返回后处理,就不要使用@AfterReturning通知。
  • 使用编译时织入:相比于运行时织入,编译时织入(如AspectJ的编译时织入)可以减少运行时的性能开销。
动态代理和Spring AOP的最佳实践

要充分发挥动态代理和Spring AOP的威力,遵循一些最佳实践是非常有帮助的:

  • 切面应该尽量轻量:切面执行的逻辑应该简单快速,避免在切面中执行耗时操作。
  • 合理定义切点表达式:避免使用过于宽泛的切点表达式,这样可以减少不必要的切面逻辑执行,提高系统性能。
  • 明智地选择切面的应用场景:并不是所有的功能都适合通过切面来实现。对于核心业务逻辑,直接实现可能更加清晰和直接。
解决在AOP中遇到的常见问题

在实际应用中,咱们可能会遇到一些问题,比如切面不生效、通知执行顺序不符合预期等。这些问题通常都有解决方案:

  • 切面不生效:检查是否在Spring配置中启用了AOP(通过@EnableAspectJAutoProxy注解),以及切面类是否被正确扫描并注册为Bean。
  • 通知执行顺序问题:可以通过实现org.springframework.core.Ordered接口或使用@Order注解来指定切面的执行顺序。
  • 循环依赖:如果切面和目标Bean之间存在循环依赖,可能会导致问题。这时候,检查并重构代码结构,解决循环依赖问题是关键。

通过上述内容,咱们对动态代理和Spring AOP的高级话题有了进一步的理解。这些知识不仅能帮助咱们解决实际开发中的问题,还能让咱们更加高效地利用这两项技术来设计和实现软件。

第六章:实战案例:构建一个简单的Spring AOP应用

项目需求分析

在很多应用中,监控方法的执行时间是一个常见需求,它帮助开发者了解应用的性能状况。使用Spring AOP,咱们可以轻松实现这一功能,而无需修改现有业务逻辑代码。目标是创建一个切面,它能够在任意方法执行前后记录时间,计算出方法的执行耗时。

逐步构建Spring AOP项目

首先,确保咱们的项目已经包含了Spring Boot的起步依赖,以及AOP的依赖:

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId>
</dependency>

接下来,定义咱们的日志切面MethodExecutionTimeAspect

@Aspect
@Component
public class MethodExecutionTimeAspect {private static final Logger logger = LoggerFactory.getLogger(MethodExecutionTimeAspect.class);// 定义切点为所有Service层的方法@Pointcut("within(@org.springframework.stereotype.Service *)")public void monitor() {}// 在方法执行前后记录时间@Around("monitor()")public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {long startTime = System.currentTimeMillis();Object proceed = joinPoint.proceed();long executionTime = System.currentTimeMillis() - startTime;logger.info(joinPoint.getSignature() + " executed in " + executionTime + "ms");return proceed;}
}

在这个切面中,咱们定义了一个切点monitor,它匹配所有标记有@Service注解的类中的方法。使用@Around注解定义了一个环绕通知,它在目标方法执行前后执行,计算并记录方法的执行时间。

为了展示这个切面的效果,咱们可以创建一个简单的服务类:

@Service
public class SampleService {public void execute() {// 模拟业务逻辑执行时间try {Thread.sleep(1000);} catch (InterruptedException e) {Thread.currentThread().interrupt();}}
}

最后,在Spring Boot的主类或任意配置类中,确保启用了AOP:

@SpringBootApplication
@EnableAspectJAutoProxy
public class Application {public static void main(String[] args) {SpringApplication.run(Application.class, args);}
}
测试和调试AOP功能

构建完毕后,咱们可以通过编写单元测试或直接运行应用来测试AOP功能。每当SampleServiceexecute方法被调用时,咱们的切面应该能够记录并打印出方法的执行时间。

通过这个简单的实战案例,咱们不仅加深了对Spring AOP的理解,也掌握了如何在实际项目中应用AOP来解决具体问题。希望这个案例能够激发出咱们更多关于使用AOP优化项目的想法。

第七章:总结

经过前面几章的学习和探索,咱们一起深入了解了Java中的动态代理和Spring AOP编程。从基本概念到高级应用,再到实战案例,小黑希望这些内容能够帮助咱们更好地掌握这两项强大的技术。现在,让咱们在本章做一个总结回顾,巩固咱们所学的知识。

动态代理与Spring AOP核心要点回顾
  • 动态代理:动态代理是一种强大的Java机制,它允许在运行时动态创建代理对象,用于在实际对象前后插入自定义的操作。Java支持两种动态代理机制:基于接口的JDK动态代理和基于类的CGLIB代理。
  • Spring AOP:面向切面编程(AOP)是一种编程范式,它允许咱们将横切关注点(如日志、事务管理等)与业务逻辑分离。Spring AOP提供了一套易于使用的AOP实现,使得在应用中实现横切关注点变得简单而高效。
  • 实战案例:通过构建一个简单的Spring AOP应用,记录方法的执行时间,咱们实践了如何在项目中利用AOP解决具体问题,增强了对Spring AOP应用场景和实现方式的理解。
学习路径建议

掌握动态代理和Spring AOP是一个持续深入的过程,小黑建议咱们在未来的学习和实践中:

  • 继续深化理解:通过高级教程、专业书籍,加深对动态代理和Spring AOP更深层次原理的理解。
  • 实战演练:理论知识的学习需要通过实践来巩固。尝试在自己的项目中应用动态代理和Spring AOP,解决实际问题。
  • 参与社区交流:加入Java和Spring相关的社区,参与讨论,分享经验,可以让咱们更快地解决遇到的问题,也能了解到更多的最佳实践和新技术趋势。
结语

通过动态代理和Spring AOP,咱们可以编写出更加模块化、可维护和可重用的代码,提高开发效率和代码质量。希望通过本系列文章的学习,咱们能够更加自信地在Java开发中使用这些强大的工具,写出更加优秀的代码。

小黑在这里祝愿每一位跟随这一系列文章学习的朋友,都能在程序员这条路上越走越远,遇到的问题越来越少,收获的快乐越来越多。记住,学习之路上永远不会孤单,因为咱们都在这条路上,一起前进。


更多推荐

详解SpringCloud之远程方法调用神器Fegin

掌握Java Future模式及其灵活应用

小黑整的视頻会园优惠站

使用Apache Commons Chain实现命令模式

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

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

相关文章

通过GitHub探索Python爬虫技术

1.检索爬取内容案例。 2.找到最近更新的。(最新一般都可以直接运行) 3.选择适合自己的项目&#xff0c;目前测试下面画红圈的是可行的。 4.方便大家查看就把代码粘贴出来了。 #图中画圈一代码 import requests import os import rewhile True:music_id input("请输入歌曲…

鸿蒙Harmony应用开发—ArkTS声明式开发(通用属性:位置设置)

设置组件的对齐方式、布局方向和显示位置。 说明&#xff1a; 从API Version 7开始支持。后续版本如有新增内容&#xff0c;则采用上角标单独标记该内容的起始版本。 align align(value: Alignment) 设置容器元素绘制区域内的子元素的对齐方式。 卡片能力&#xff1a; 从API…

【系统分析师】-系统配置与性能评价

1、性能指标 主频&#xff1a;又称时钟频率&#xff0c;1GHZ表示1秒有1G个时钟周期 1s10^9ns 主频外频 * 倍频 时钟周期 主频的倒数指令周期&#xff1a;取出并执行一条指令的时间 总线周期&#xff1a;一个访存储器或IO操作所用时间平均执行周期数&#xff1a;CPI表示…

如何解决幻兽帕鲁/Palworld服务器联机游戏时的丢包问题?

如何解决幻兽帕鲁/Palworld服务器联机游戏时的丢包问题&#xff1f; 等待服务器维护&#xff1a;首先&#xff0c;确保网络连接稳定&#xff0c;然后查看游戏官方或社区论坛&#xff0c;了解是否有服务器维护的消息。这是解决丢包问题的一种直接且有效的方法。 更新显卡驱动&a…

Siemens-NXUG二次开发-获取prt中体与类型、实体面与类型、实体边与类型、边上点的Tag标识[Python UF][20240302]

Siemens-NXUG二次开发-获取prt中体与类型、实体面与类型、实体边与类型、边上点的Tag标识[Python UF][20240302] 1.python uf函数1.1 NXOpen.UF.Obj.CycleObjsInPart1.2 NXOpen.UF.Obj.AskTypeAndSubtype1.3 NXOpen.UF.Modeling.AskBodyFaces1.4 NXOpen.UF.Modeling.AskFaceEdg…

RISC-V特权架构 - 机器模式下的异常处理

RISC-V特权架构 - 机器模式下的异常处理 1 进入异常1.1 从mtvec 定义的PC 地址开始执行1.2 更新CSR 寄存器mcause1.3 更新CSR 寄存器mepc1.4 更新CSR 寄存器mtval1.5 更新CSR 寄存器mstatus 2 退出异常2.1 从mepc 定义的PC 地址开始执行2.2 更新CSR 寄存器mstatus 3 异常服务程…

Android Tombstone 分析

1.什么是tombstone Tombstone是指在分布式系统中用于标记数据已被删除的记录&#xff0c;通常包含删除操作的时间戳和相关信息。 当一个动态库&#xff08;native程序&#xff09;开始执行时&#xff0c;系统会注册一些连接到 debuggerd 的signal handlers。当系统发生崩溃时…

Spark Shuffle Tracking 原理分析

Shuffle Tracking Shuffle Tracking 是 Spark 在没有 ESS(External Shuffle Service)情况&#xff0c;并且开启 Dynamic Allocation 的重要功能。如在 K8S 上运行 spark 没有 ESS。本文档所有的前提都是基于以上条件的。 如果开启了 ESS&#xff0c;那么 Executor 计算完后&a…

MySQL 表的基本操作,结合项目的表自动初始化来讲

有了数据库以后&#xff0c;我们就可以在数据库中对表进行增删改查了&#xff0c;这也就意味着&#xff0c;一名真正的 CRUD Boy 即将到来&#xff08;&#x1f601;&#xff09;。 查表 查看当前数据库中所有的表&#xff0c;使用 show tables; 命令 由于当前数据库中还没有…

基于Python3的数据结构与算法 - 09 希尔排序

一、引入 希尔排序是一种分组插入排序的算法。 二、排序思路 首先取一个整数d1 n/2&#xff0c;将元素分为d1个组&#xff0c;每组相邻量取元素距离为d1&#xff0c;在各组内直接进行插入排序&#xff1b;取第二个整数d2 d1/2&#xff0c; 重复上述分组排序过程&#xff0…

CSS 自测题 -- 用 flex 布局绘制骰子(一、二、三【含斜三点】、四、五、六点)

一点 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8" /><meta name"viewport" content"widthdevice-width, initial-scale1.0" /><title>css flex布局-画骰子</title><sty…

Unity 切换场景

场景切换前必须要将场景拖动到Build中 同步加载场景 using System.Collections; using System.Collections.Generic; //using UnityEditor.SearchService; using UnityEngine; // 场景管理 需要导入该类 using UnityEngine.SceneManagement;public class c3 : MonoBehaviour {…

redis五大基础类型【重点】

之前写过一点小知识&#xff1a;https://blog.csdn.net/qq_45927881/article/details/134959181?spm1001.2014.3001.5501 参考链接 https://xiaolincoding.com/redis/data_struct/command.html#%E4%BB%8B%E7%BB%8D 目录 1. string&#xff08;字符串&#xff09;2. Hash&#…

MySql安全加固:配置不同用户不同账号禁止使用旧密码禁止MySql进程管理员权限

MySql安全加固&#xff1a;配置不同用户不同账号&禁止使用旧密码&禁止MySql进程管理员权限 1.1 检查是否配置不同用户不同账号1.2 检查是否禁止使用旧密码1.3 禁止MySql进程管理员权限 &#x1f496;The Begin&#x1f496;点点关注&#xff0c;收藏不迷路&#x1f496…

【c++】通讯录管理系统

1.系统功能介绍及展示 2.创建项目 3.菜单实现 4.退出功能实现 5.添加联系人—结构体设计 6.添加联系人—功能实现 7.显示联系人 8.删除练习人—检测联系人是否存在 9.删除联系人—功能实现 10.查找联系人 11.修改联系人 12.清空通讯录 #include <iostream> #include <…

什么是VR虚拟社区|VR元宇宙平台|VR主题馆加盟

VR虚拟社区是指一种基于虚拟现实技术构建的在线社交平台或环境&#xff0c;用户可以在其中创建虚拟化的个人形象&#xff08;也称为avatars&#xff09;并与其他用户进行交流、互动和合作。在VR虚拟社区中&#xff0c;用户可以选择不同的虚拟场景和环境&#xff0c;如虚拟公园、…

fly-barrage 前端弹幕库(3):滚动弹幕的设计与实现

项目官网地址&#xff1a;https://fly-barrage.netlify.app/&#xff1b; &#x1f451;&#x1f40b;&#x1f389;如果感觉项目还不错的话&#xff0c;还请点下 star &#x1f31f;&#x1f31f;&#x1f31f;。 Gitee&#xff1a;https://gitee.com/fei_fei27/fly-barrage&a…

显示器开机正常,插入HDMI线却不显示画面,换了HDMI线还是不行?

环境&#xff1a; 惠普/P24VG4 DELL笔记本 问题描述&#xff1a; 显示器开机正常&#xff0c;插入HDMI线却不显示画面&#xff0c;换了HDMI线还是不行&#xff0c;是不是显示器坏了&#xff1f; 解决方案&#xff1a; 1.前往显示器设置菜单里面查看input 2.把输入源默认设…

二百二十五、海豚调度器——用DolphinScheduler调度执行Flume数据采集任务

一、目的 数仓的数据源是Kafka&#xff0c;因此离线数仓需要用Flume采集Kafka中的数据到HDFS中 在实际项目中&#xff0c;不可能一直在Xshell中启动Flume任务&#xff0c;一是项目的Flume任务很多&#xff0c;二是一旦Xshell页面关闭Flume任务就会停止&#xff0c;这样非常不…

案例研究|DataEase助力众陶联应对产业链数据可视化挑战

佛山众陶联供应链服务有限公司&#xff08;以下简称为“众陶联”&#xff09;成立于2016年&#xff0c;是由34家陶瓷企业共同创办的建陶行业工业互联网平台&#xff0c;股东产值占整个行业的22.5%。众陶联以数据赋能为核心&#xff0c;积极探索新的交易和服务模式&#xff0c;构…