从反射到动态代理到aop到spring为什么需要三级缓存

本文详细介绍了Spring框架中的核心思想——IOC和AOP。IOC利用反射技术实现bean的创建,而AOP则通过JDK动态代理机制实现方法的增强。AOP用于将非业务代码如日志记录、权限控制等与业务逻辑分离,其核心包括点切面(@Pointcut)、通知(Advice)等概念。此外,文章还探讨了Spring如何利用三级缓存解决循环依赖问题,确保在处理循环依赖时正确生成原始对象和代理对象。

概要

spring 主要有两大思想,一个是 IOC,一个就是 AOP。对于 IOC 容器中bean 的创建就是使用了 Java 的反射技术;而对于 spring 的核心 AOP 来说,我们知道 AOP 的实现原理之一就是 JDK 的动态代理机制,而 JDK 的动态代理本质就是利用了反射技术来实现的

反射

官方的解释:
java程序在运行状态中,对于任意一个类,都能够在运行时知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。

反射赋予jvm动态编译的能力,我们在程序运行中,可以获得到我们任何想要的类,方法,属性,那么就为我们动态代理的实现提供了基础,因为我们可以在运行中,通过反射,可以创建任意一个类的代理对象

动态代理

利用反射机制在运行时创建代理对象,然后通过代理对象来调用真实对象的方法,可以根据传入不同的类,生成不同的代理对象去执行真实对象的方法

aop

面向切面编程,将业务代码和非业务代码分离,如日志输入,权限控制等进行分离,利用动态代理的方式实现,代理我们的方法执行,我们可以在方法执行前,后对参数和结果进行修改,也可以根据抛出的异常做对应得日志输出,项目中用得aop做得全局异常处理

个人认为是拦截器+动态代理的结合,拦截器拦截到需要aop的对象,在动态代理执行之前可以做一些操作,然后在动态代理方法执行结束之后,也可以在做一些操作。

  • @Pointcut包含两个过滤器:类过滤器,方法过滤器,两者共同作用就可以判断添加增强器的位置。
  • Advice通知是一个增强器,是以拦截器做通知模型,维护一个“围绕”连接点的拦截器链。

三级缓存解决循环依赖

spring利用两级缓存可以解决bean的循环依赖,利用三级缓存解决aop的循环依赖

循环依赖

A依赖B(对象A中有一个属性Class B),B依赖A(对象B中有一个属性Class A)
当我们创建beanA发现没有BeanB,那我们就去创建BeanB,创建BeanB发现没有BeanA
,就去创建BeanA,如此,便开始循环创建

如何解决

三级缓存,三个Map

  1. 一级缓存:用于存放完全初始化好的 bean,从该缓存中取出的 bean 可以直接使用
  2. 二级缓存:提前曝光的单例对象的cache,存放原始的 bean 对象(尚未填充属性),用于解决循环依赖
  3. 三级缓存:单例对象工厂的cache,存放 bean 工厂对象,用于解决循环依赖,可以根据原始bean还是代理bean,生成对应的原始bean对象还是代理bean对象

步骤:
首先,我们开始创建A对象,在三级缓存添加对应的工厂BeanA,然后在依赖注入的时候,发现没有B,然后在三级缓存中添加对应的工厂beanB,然后依赖注入时,通过工厂beanA生产A对象(如果有代理,返回的是代理对象),此时,会将生产的A对象放到二级缓存,三级缓存的工厂beanA会被删除,然后注入到B中,B就可以完成实例化,初始化,然后将B放到了一级缓存中,然后在A在进行依赖注入,初始化的操作,即可完成

为什么只能用三级缓存,不能使用二级缓存

如果没有三级缓存,那么创建过程是这样的:A创建,然后属性注入,发现B没有创建,然后创建B,B可以完成实例化和初始化,初始化之后调用AOP的后置处理器,拿到B的代理对象,然后在对A进行属性注入,A初始化之后,也会调用AOP的后置处理器类,拿到A的代理对象,此时会发现,A中的B是B的代理对象,B中的A不是A的代理对象,而是原始对象
解决方案:引入三级缓存,三级缓存有bean工厂,可以根据原始bean还是代理bean,生成对应的原始bean对象还是代理bean对象,在实例化的时候如果使用了aop,那么拿到的就是代理对象,就会解决上面的问题

### 三级缓存解决循环依赖的原理 Spring框架通过三级缓存机制解决了单例Bean在依赖注入过程中可能出现的循环依赖问题。该机制的核心在于通过三级缓存结构,将Bean的创建过程进行阶段性解耦,从而避免因循环依赖导致的无限递归或初始化失败。 #### 三级缓存的结构与作用 Spring中的三级缓存分别是: 1. **一级缓存(singletonObjects)**:用于存放完全初始化好的Bean,可以直接使用。 2. **二级缓存(earlySingletonObjects)**:用于存放早期暴露的Bean对象,这些Bean尚未完成完整的属性填充和初始化。 3. **三级缓存(singletonFactories)**:用于存放生成早期Bean对象的工厂对象(ObjectFactory),通过该工厂对象可以延迟创建Bean的早期引用。 三级缓存的主要作用是提供一种延迟加载和创建 Bean 的机制[^3]。在 Bean 创建过程中,尤其是在处理循环依赖时,它能够通过 ObjectFactory 来获取早期暴露的 Bean 对象。通过这种方式,Spring 可以在合适的时机创建 Bean,避免了因为直接创建而可能导致的循环依赖问题。例如,在某些情况下,可能需要对 Bean 进行一些额外的处理或增强,通过 ObjectFactory 可以在需要的时候才进行这些操作,而不是在 Bean 实例化时就进行。 #### 解决循环依赖的具体流程 当Spring容器创建Bean时,会经历以下几个关键步骤: 1. **实例化Bean**:通过反射创建一个Bean的实例。 2. **暴露早期引用**:在Bean尚未完成初始化之前,将其早期引用(通过ObjectFactory生成)放入三级缓存中。 3. **依赖注入**:在填充Bean的属性时,如果发现某个依赖的Bean尚未初始化,Spring会尝试从三级缓存中获取该Bean的早期引用。 4. **从三级缓存中获取并升级**:如果目标Bean的早期引用存在于三级缓存中,Spring会通过ObjectFactory生成该Bean的早期对象,并将其移动到二级缓存中,同时从三级缓存中移除。这样可以确保后续对该Bean的访问直接从二级缓存中获取,而不再调用ObjectFactory.getObject()生成新的代理对象[^5]。 在依赖填充阶段,Spring框架使用三级缓存来处理循环依赖。例如,在populateBean方法中,如果某个属性依赖于尚未初始化的Bean,Spring会尝试从缓存中获取其早期引用,从而避免循环依赖导致的初始化失败[^4]。 #### 构造器注入与循环依赖 需要注意的是,Spring三级缓存机制无法解决构造器注入导致的循环依赖问题。这是因为构造器注入发生在Bean实例化阶段,此时Bean的早期引用尚未生成,因此无法通过三级缓存机制进行处理[^2]。 #### AOP代理与三级缓存 在存在AOP代理的情况下,三级缓存机制显得尤为重要。假设只有两级缓存,每次从三级缓存中获取代理对象时,都会生成一个新的代理实例,这与单例模式的要求相违背。因此,Spring通过三级缓存机制,将代理对象的生成延迟到合适的时机,并通过二级缓存确保代理对象的唯一性[^5]。 ### 示例代码 以下是一个简单的Spring Bean定义示例,展示了如何通过setter注入实现循环依赖: ```java @Component public class AService { private BService bService; @Autowired public void setBService(BService bService) { this.bService = bService; } } @Component public class BService { private AService aService; @Autowired public void setAService(AService aService) { this.aService = aService; } } ``` 在这个例子中,`AService`和`BService`通过setter注入形成了循环依赖Spring三级缓存机制可以成功解决这个问题。 ### 总结 Spring框架通过三级缓存机制巧妙地解决了单例Bean的循环依赖问题。这一机制通过延迟加载Bean的初始化过程,使得在依赖注入时能够获取到早期引用,从而避免循环依赖导致的初始化失败。然而,开发者应认识到这是框架层面的补救措施,而非设计推荐。在复杂项目中,应通过代码重构尽量避免循环依赖的出现[^1]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值