Spring AOP魔法揭秘:代理生命周期的深度之旅

1. Spring AOP概述

Spring AOP(Aspect-Oriented Programming)是Spring框架中一个强大的功能,它允许开发者在不修改业务逻辑代码的情况下,为业务逻辑添加额外的功能,如日志、事务管理、权限验证等。这些额外的功能被称为“切面”,它们会切入到类的指定方法上,在方法执行前后或出现异常时执行特定的代码。


2. Spring AOP的代理实现

Spring AOP的代理主要有两种实现方式:JDK动态代理和CGLIB动态代理。

  1. JDK动态代理:要求被代理的类必须实现一个或多个接口。Spring AOP通过反射机制创建代理对象,代理对象持有目标对象的引用,并在调用目标对象的方法时,动态地插入切面逻辑。
  2. CGLIB动态代理:如果目标类没有实现任何接口,Spring AOP会使用CGLIB库来创建代理对象。CGLIB通过继承目标类来创建代理,因此代理对象会拥有目标类的所有非final方法。

3. Spring AOP代理生命周期深度解析

  1. 准备阶段
    • 扫描和识别:Spring容器在启动时,会扫描所有的Bean定义,查找标记有AOP相关注解(如@Aspect)的Bean以及需要通过AOP增强的Bean。
    • 注册切面:对于被标记为切面的Bean,Spring会解析其中的通知(Advice)和切入点(Pointcut)定义,并将它们注册到相应的切面信息中。
  2. 代理创建阶段
    • 判断代理类型:Spring根据被代理的Bean是否实现了接口,决定使用JDK动态代理还是CGLIB代理。如果Bean实现了接口,则使用JDK动态代理;否则,使用CGLIB代理。
    • 创建代理对象
      • 对于JDK动态代理,Spring通过java.lang.reflect.Proxy类的newProxyInstance方法创建代理对象,该方法需要传入类加载器、接口数组和InvocationHandler接口的实现类。
      • 对于CGLIB代理,Spring使用CGLIB库来创建代理对象。它首先会生成目标类的子类,并在子类中覆盖目标方法,加入代理逻辑。
    • 封装目标对象:在代理对象内部,Spring会持有目标对象的引用,以便在调用目标方法时能够找到真正的执行者。
  3. 代理初始化阶段
    • 依赖注入:如果代理对象需要依赖其他Bean,Spring会负责将这些依赖注入到代理对象中。
    • 通知织入:Spring会根据注册的切面和切入点信息,将通知(Advice)织入到代理对象中。这通常是通过修改代理对象的字节码来实现的,以便在调用目标方法时能够执行通知中的代码。
  4. 代理使用阶段
    • 当其他Bean通过代理对象调用目标方法时,Spring会拦截这个调用,并根据织入的通知执行相应的代码。这可能包括在目标方法执行前、后或出现异常时执行特定的逻辑。
  5. 代理销毁阶段
    • 当Spring容器关闭时,它会销毁所有的Bean,包括代理对象。在销毁之前,Spring会执行一些清理工作,如关闭资源、释放连接等。

4. 源码分析

在Spring的源码中,与AOP代理生命周期相关的关键类包括:

  • AspectJAutoProxyRegistrar:这个类用于在Spring容器中注册AnnotationAwareAspectJAutoProxyCreator Bean,它是实现AOP特性的核心后置处理器。
class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {  @Override  public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {  // 注册AnnotationAwareAspectJAutoProxyCreator Bean  AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);  }  
}  // 在AopConfigUtils中  
public static void registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry) {  // 检查是否已注册  // ...  // 如果没有注册,则注册AnnotationAwareAspectJAutoProxyCreator  RootBeanDefinition beanDefinition = new RootBeanDefinition(AnnotationAwareAspectJAutoProxyCreator.class);  // ... 设置bean定义属性  registry.registerBeanDefinition(AOP_AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);  
}
  • AnnotationAwareAspectJAutoProxyCreator:这个类继承自AbstractAutoProxyCreator,并实现了BeanPostProcessor接口。它负责在Bean的创建过程中创建代理对象,并织入切面逻辑。
public class AnnotationAwareAspectJAutoProxyCreator extends AbstractAutoProxyCreator implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware {  // ... 省略其他代码 ...  @Override  protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {  // 判断是否需要创建代理  if (shouldSkip(beanName, bean, cacheKey)) {  return bean;  }  // 创建代理对象  ProxyFactory proxyFactory = createProxyFactory(bean);  return proxyFactory.getProxy();  }  // ... 省略其他代码,如postProcessAfterInitialization、findEligibleBeans等 ...  
}  // 在ProxyFactory中  
public Object getProxy() {  return createAopProxy().getProxy();  
}  // 在AopProxyFactory中  
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {  // 判断是否实现了接口,从而决定使用JDK动态代理还是CGLIB代理  if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {  Class<?> targetClass = config.getTargetClass();  if (targetClass == null) {  throw new AopConfigException("TargetSource cannot determine target class: " +  "Either an interface or a target is required for proxy creation.");  }  // 创建CGLIB代理  return new ObjenesisCglibAopProxy(config);  }  else {  // 创建JDK动态代理  return new JdkDynamicAopProxy(config);  }  
}

AnnotationAwareAspectJAutoProxyCreator的源码中,可以看到它是如何扫描切面、注册通知和切入点、创建代理对象以及织入通知的。

  • ProxyFactory:这个类是一个用于创建AOP代理的工厂类,它提供了创建JDK动态代理和CGLIB代理的接口。
public class ProxyFactory {  private AdvisedSupport advisedSupport; // 假设这是一个包含代理配置的类  public ProxyFactory(AdvisedSupport advisedSupport) {  this.advisedSupport = advisedSupport;  }  public Object getProxy() {  // 判断是否应该使用CGLIB代理或JDK动态代理  boolean proxyTargetClass = advisedSupport.isProxyTargetClass();  boolean hasInterfaces = !Proxy.isProxyClass(advisedSupport.getTargetClass()) &&  advisedSupport.getTargetClass() != null && advisedSupport.getTargetClass().getInterfaces().length > 0;  // 如果没有指定使用CGLIB并且目标类有接口,则使用JDK动态代理  if (!proxyTargetClass && hasInterfaces) {  return new JdkDynamicAopProxy(advisedSupport).getProxy();  }  // 否则,使用CGLIB代理  return new CglibAopProxy(advisedSupport).getProxy();  }  // 这里为了简化,没有展示JdkDynamicAopProxy和CglibAopProxy的实现  // 但在Spring中,这些类会负责创建实际的代理对象  // 假设的AdvisedSupport类(为了演示)  static class AdvisedSupport {  private Class<?> targetClass;  private boolean proxyTargetClass;  // 省略getter和setter方法  public Class<?> getTargetClass() {  return targetClass;  }  public void setTargetClass(Class<?> targetClass) {  this.targetClass = targetClass;  }  public boolean isProxyTargetClass() {  return proxyTargetClass;  }  public void setProxyTargetClass(boolean proxyTargetClass) {  this.proxyTargetClass = proxyTargetClass;  }  }  
}

通过查看ProxyFactory的源码,可以了解代理对象的具体创建过程。


5. 总结

Spring AOP通过动态代理技术为业务逻辑添加了额外的功能,其代理生命周期包括准备、创建、初始化、使用和销毁五个阶段。在源码层面,Spring通过一系列关键的类(如AspectJAutoProxyRegistrarAnnotationAwareAspectJAutoProxyCreatorProxyFactory)来实现AOP代理的创建和管理。通过深入理解这些类和源码实现,我们可以更好地掌握Spring AOP的原理和使用方法,从而更加灵活地应对各种复杂的业务场景。


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

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

相关文章

win_os_linux不能用于文件名的保留字符

windows 在 Windows 文件系统中&#xff0c;以下字符是保留字符&#xff0c;不能用于文件名或目录名&#xff1a; < (小于号)> (大于号): (冒号)" (双引号)/ (斜杠)\ (反斜杠)| (竖线)? (问号)* (星号) 此外&#xff0c;文件名不能以空格或句点 (.) 结尾&#x…

Creo模型按一定的比例放大或缩小(实际尺寸)

原来&#xff0c;距离是100mm 缩放操作 放大3倍&#xff0c;距离变为300mm

Web开发者在页面滚动时提供简单而优雅的动画效果的插件库

1、动画插件库scrollrevealjs 官网连接: https://scrollrevealjs.org/api/view-offset.html <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8" /><meta name"viewport" content"widthdevice-width…

一文掌握JavaScript面向对象的知识点

文章导读&#xff1a;AI 辅助学习前端&#xff0c;包含入门、进阶、高级部分前端系列内容&#xff0c;当前是 JavaScript的部分&#xff0c;瑶琴会持续更新&#xff0c;适合零基础的朋友&#xff0c;已有前端工作经验的可以不看&#xff0c;也可以当作基础知识回顾。 这篇文章…

使用QtCreator C++编写串口调试助手

100编程书屋_孔夫子旧书网 1.首先看一下我设计的界面&#xff08;我这里比较简单&#xff0c;大家可根据自己的需求进行设计&#xff09; &#xff08;界面设计的过程中&#xff0c;每一个控件的名称最好进行修改&#xff0c;便于后续控件太多不好区分&#xff0c;给控件命名的…

6.1 Go 数组

&#x1f49d;&#x1f49d;&#x1f49d;欢迎莅临我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:「stormsha的主页」…

深入分析 Android Activity (十一)

文章目录 深入分析 Android Activity (十一)1. Activity 的内存管理和优化1.1 内存泄漏的常见原因1.2 避免内存泄漏的方法1.3 内存泄漏检测工具 2. Activity 的配置变更处理2.1 处理配置变更2.2 保存和恢复状态2.3 使用 ViewModel 3. Activity 的测试3.1 单元测试3.2 UI 测试 4…

质量工具系列之Dependency-Track

项目开发中依赖了很多第三方开源工具&#xff0c;对于其版本&#xff0c;漏洞等因为时间或者是数量太多而无法关注到&#xff0c;Dependency-Track解决这些问题。 Dependency-Track 是一个开源组件分析平台&#xff0c;是开放网络应用安全项目&#xff08;OWASP&#xff09;的一…

深度解析:优质前端社区工作的多维度探索

深度解析&#xff1a;优质前端社区工作的多维度探索 在数字时代的浪潮中&#xff0c;前端社区工作已然成为推动技术发展与创新的重要力量。一个优质的前端社区工作不仅能够汇聚智慧、促进技术交流&#xff0c;还能够为社区成员提供成长的空间和平台。然而&#xff0c;究竟什么…

web自动化-数据驱动与失败用例截图、失败重新运行

因为只有失败的用例需要截图&#xff0c;那么问题就是&#xff1a; 什么时候用例会失败&#xff1f; 数据驱动测试 我们前面覆盖到的用例都是正常的用例&#xff0c;如果要测试异常的用例呢&#xff1f; 我们来写一下登录的异常 场景&#xff1a;【login_page】 # 用户输入框…

【Qt】Qt中的信号槽

一、信号和槽概述 信号槽是Qt矿建引以为豪的机制之一。 所谓信号槽&#xff0c;实际上就是观察者模式&#xff08;发布——订阅模式&#xff09;。当某个事件发生之后&#xff0c;比如&#xff0c;按钮检测到自己被点击了一下&#xff0c;它就会发出一个信号。这种发出的信号是…

LAMP集群分布式实验报告

前景&#xff1a; 1.技术成熟度和稳定性&#xff1a; LAMP架构&#xff08;Linux、Apache、MySQL、PHP&#xff09;自1998年提出以来&#xff0c;经过长时间的发展和完善&#xff0c;已经成为非常成熟和稳定的Web开发平台。其中&#xff0c;Linux操作系统因其高度的灵活性和稳…

Linux 主机一键安全整改策略

为防止linux主机被恶意攻击&#xff0c;和受到攻击后能更快定位到源头&#xff0c;需要对linux主机做一些参数配置。 比如禁用root的远程登录、用户多次密码验证失败后被锁、禁止系统账号交互式登录等等。 下面是linux主机安全整改的一些简单介绍&#xff0c;最后会通过脚本一…

【XR806开发板试用】基础篇,从零开始搭建一个LCD彩屏时钟(ST7735S驱动)

本文从搭建环境开始&#xff0c;step by step教大家使用XR806实现驱动SPI屏幕&#xff08;ST7735S驱动&#xff09;&#xff0c;并连接WiFi实现ntp对时&#xff0c;最终实现把时间显示到屏幕上。 #1. 搭建开发环境 1. 安装编译环境所需的依赖包 基于ubuntu 20.04&#xff0c;按…

UI自动化测试最佳设计模式POM

当使用Selenium进行UI自动化测试时&#xff0c;Page Object Model&#xff08;POM&#xff09;是一种最佳实践的设计模式。POM的核心思想是通过将页面封装成对象&#xff0c;使得测试代码更加清晰、可维护和可重用。 POM的主要组成部分包括页面对象类、元素定位方式和操作方法…

MybatisPlus @TableField之SqlCondition源码解析

应用对象&#xff1a;模型&#xff0c;作用&#xff1a;调用IService接口&#xff0c;使用查询包装器实现灵活的条件查询。 TableField(conditionSqlCondition.LIKE) 注解使用逻辑 com.baomidou.mybatisplus.extension.service.IService.page(分页对象&#xff0c;查询包装器…

“区块链技术在网络安全领域的革新应用与挑战“

区块链技术&#xff0c;以其去中心化、不可篡改和透明度高的特性&#xff0c;在网络安全领域展现出了巨大的革新潜力&#xff0c;同时也带来了一系列新的挑战。以下是区块链技术在网络安全领域的应用及其面临的主要挑战的深入分析。 革新应用 1. 数据保护与隐私增强&#xff…

PHP身份证识别接口、线上平台如何实现身份证实名认证功能?

线上平台实现身份证实名认证的功能&#xff0c;需要结合身份证识别接口来完成。首先&#xff0c;用户通过上传身份证图片或者拍照的方式实现证件信息的提取&#xff0c;身份证实名认证接口通过对提取到的证件信息进行核验&#xff0c;以此来实现线上用户身份的实名认证&#xf…

LabVIEW车轮动平衡检测系统

LabVIEW车轮动平衡检测系统 随着汽车行业的快速发展&#xff0c;车轮动平衡问题对乘坐舒适性、操控稳定性及安全性的影响日益凸显&#xff0c;成为了提高汽车性能的一个关键环节。传统的检测系统因精度低、成本高、操作复杂等问题&#xff0c;难以满足现代汽车行业的需求。开发…

行车安全:UWB模块的智能化在车辆安全系统中的作用

随着交通车辆数量的不断增加和道路交通拥堵的加剧&#xff0c;车辆安全问题日益引起人们的关注。在这种背景下&#xff0c;超宽带&#xff08;UWB&#xff09;技术作为一种新兴的定位技术&#xff0c;正逐渐应用于车辆安全系统中&#xff0c;为提高车辆行车安全性提供了新的解决…