一看就懂,Spring IOC 三级缓存如何解决循环依赖

2024-05-29 06:08

本文主要是介绍一看就懂,Spring IOC 三级缓存如何解决循环依赖,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

历时一周的时间,前后经历4/5次更改,浓缩出本文~

目录

 Bean 创建时机

Spring 创建 bean 三步曲

循环依赖

关键代码


 Bean 创建时机

AbstractApplicationContext.javapublic void refresh() { finishBeanFactoryInitialization(beanFactory);  }protected finishBeanFactoryInitialization(beanFactory) {// Instantiate all remaining (non-lazy-init) singletons.beanFactory.preInstantiateSingletons}    

     

Spring 创建 bean 三步曲

1.  反射调用无参构造创建 bean:   Constructor.newInstance()

2.  为 bean 属性赋值: populateBean()

3.  初始化 bean:initializeBean()

循环依赖

关于循环依赖的问题已经在上图中说明,为了看的更清楚,整理如下流程图:

解释下上图, A依赖B, B依赖A, 

1. spring 获取 a,a 不存在缓存,开始创建

2. a 创建后放入三级缓存

3. 赋值 a 的属性,发现依赖了 b

4. 获取 b, b 不存在缓存,开始创建

5. b 创建后放入三级缓存

6. 赋值 b 的属性, 发现依赖了 a

7. 从三级缓存获取 a (的代理,如果需要的话), 并放入二级缓存(2中操作的)

8. 注入到 b 

9. 初始化 b (生成代理对象)

10. b 放入一级缓存(b 的创建全部完成)

11. 把 b 注入 a

12. 接着 3, 初始化 a,并没有生成代理!

13. 从二级缓存获取 a 的代理对象(7中操作的)

14. 将 a 的代理对象放入一级缓存(b 的创建全部完成)

关键代码

获取 bean

	protected <T> T doGetBean(String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)throws BeansException {String beanName = transformedBeanName(name);Object beanInstance;// 从缓存中获取,一级->二级->三级, b 初始化时获取 a,从三级缓存获取并移入二级Object sharedInstance = getSingleton(beanName);// 缓存中有if (sharedInstance != null && args == null) {if (logger.isTraceEnabled()) {if (isSingletonCurrentlyInCreation(beanName)) {logger.trace("Returning eagerly cached instance of singleton bean '" + beanName +"' that is not fully initialized yet - a consequence of a circular reference");}else {logger.trace("Returning cached instance of singleton bean '" + beanName + "'");}}// 直接返回 bean,或者 factoryBean.getObject()beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, null);}else {//缓存中没有,再次尝试从缓存获取,没有则调用 createBean 返回的最终 bean(如果用了aop,是增强的bean) 并放入一级缓存中。if (mbd.isSingleton()) {sharedInstance = getSingleton(beanName, () -> {try {return createBean(beanName, mbd, args);}catch (BeansException ex) {// Explicitly remove instance from singleton cache: It might have been put there// eagerly by the creation process, to allow for circular reference resolution.// Also remove any beans that received a temporary reference to the bean.destroySingleton(beanName);throw ex;}});beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);}else if (mbd.isPrototype()) {}else {}catch (IllegalStateException ex) {throw new ScopeNotActiveException(beanName, scopeName, ex);}}}catch (BeansException ex) {beanCreation.tag("exception", ex.getClass().toString());beanCreation.tag("message", String.valueOf(ex.getMessage()));cleanupAfterBeanCreationFailure(beanName);throw ex;}finally {beanCreation.end();}}return adaptBeanInstance(name, beanInstance, requiredType);}
AbstractBeanFactory.java

创建 bean 并初始化过程:

	protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)throws BeanCreationException {// Instantiate the bean.BeanWrapper instanceWrapper = null;if (mbd.isSingleton()) {instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);}if (instanceWrapper == null) {// 1. 反射创建 beaninstanceWrapper = createBeanInstance(beanName, mbd, args);}Object bean = instanceWrapper.getWrappedInstance();// Eagerly cache singletons to be able to resolve circular references// even when triggered by lifecycle interfaces like BeanFactoryAware.boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&isSingletonCurrentlyInCreation(beanName));if (earlySingletonExposure) {if (logger.isTraceEnabled()) {logger.trace("Eagerly caching bean '" + beanName +"' to allow for resolving potential circular references");}// 2. a、b 创建后都会被放入三级缓存:beanName-> singletonFactory// b 初始化时,会从三级缓存的 singletonFactory.getObject() 拿到 a 的代理对象addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));}// 3. 初始化(resolveDependency, 生成代理对象)Object exposedObject = bean;try {populateBean(beanName, mbd, instanceWrapper);// b 在这里会生成代理对象exposedObject = initializeBean(beanName, exposedObject, mbd);}catch (Throwable ex) {if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {throw (BeanCreationException) ex;}else {throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);}}if (earlySingletonExposure) {Object earlySingletonReference = getSingleton(beanName, false);if (earlySingletonReference != null) {if (exposedObject == bean) {exposedObject = earlySingletonReference;}else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {String[] dependentBeans = getDependentBeans(beanName);Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);for (String dependentBean : dependentBeans) {if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {actualDependentBeans.add(dependentBean);}}if (!actualDependentBeans.isEmpty()) {throw new BeanCurrentlyInCreationException(beanName,"Bean with name '" + beanName + "' has been injected into other beans [" +StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +"] in its raw version as part of a circular reference, but has eventually been " +"wrapped. This means that said other beans do not use the final version of the " +"bean. This is often the result of over-eager type matching - consider using " +"'getBeanNamesForType' with the 'allowEagerInit' flag turned off, for example.");}}}}// Register bean as disposable.try {registerDisposableBeanIfNecessary(beanName, bean, mbd);}catch (BeanDefinitionValidationException ex) {throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);}return exposedObject;}
AbstractAutowireCapableBeanFactory.java

7 中获取 a 时会从三级缓存中获取,singletonFactory.getObject 时会生成代理:

	protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {Object exposedObject = bean;if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {for (SmartInstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().smartInstantiationAware) {// 重要!!如果使用了aop, InfrastructureAdvisorAutoProxyCreator 会返回代理对象exposedObject = bp.getEarlyBeanReference(exposedObject, beanName);}}return exposedObject;}

AbstractAutowireCapableBeanFactory.java

总结

1. 一级缓存 存放已经 ready 的 bean,供其他 bean 使用

private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

2. 二级缓存,存放循环依赖中首先被创建的 bean(被增强后的单例 bean)

private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);

3. 三级缓存, 存放对象名和对象工厂

Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

二级缓存的意义就在于:如果使用了 aop , 缓存了被增强对象,而不是通过三级缓存每次都创建个增强对象。


如果觉得还不错的话,关注、分享、在看(关注不失联~), 原创不易,且看且珍惜~

这篇关于一看就懂,Spring IOC 三级缓存如何解决循环依赖的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1012856

相关文章

JVM 的类初始化机制

前言 当你在 Java 程序中new对象时,有没有考虑过 JVM 是如何把静态的字节码(byte code)转化为运行时对象的呢,这个问题看似简单,但清楚的同学相信也不会太多,这篇文章首先介绍 JVM 类初始化的机制,然后给出几个易出错的实例来分析,帮助大家更好理解这个知识点。 JVM 将字节码转化为运行时对象分为三个阶段,分别是:loading 、Linking、initialization

Spring Security 基于表达式的权限控制

前言 spring security 3.0已经可以使用spring el表达式来控制授权,允许在表达式中使用复杂的布尔逻辑来控制访问的权限。 常见的表达式 Spring Security可用表达式对象的基类是SecurityExpressionRoot。 表达式描述hasRole([role])用户拥有制定的角色时返回true (Spring security默认会带有ROLE_前缀),去

浅析Spring Security认证过程

类图 为了方便理解Spring Security认证流程,特意画了如下的类图,包含相关的核心认证类 概述 核心验证器 AuthenticationManager 该对象提供了认证方法的入口,接收一个Authentiaton对象作为参数; public interface AuthenticationManager {Authentication authenticate(Authenti

Spring Security--Architecture Overview

1 核心组件 这一节主要介绍一些在Spring Security中常见且核心的Java类,它们之间的依赖,构建起了整个框架。想要理解整个架构,最起码得对这些类眼熟。 1.1 SecurityContextHolder SecurityContextHolder用于存储安全上下文(security context)的信息。当前操作的用户是谁,该用户是否已经被认证,他拥有哪些角色权限…这些都被保

Spring Security基于数据库验证流程详解

Spring Security 校验流程图 相关解释说明(认真看哦) AbstractAuthenticationProcessingFilter 抽象类 /*** 调用 #requiresAuthentication(HttpServletRequest, HttpServletResponse) 决定是否需要进行验证操作。* 如果需要验证,则会调用 #attemptAuthentica

Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

Java架构师知识体认识

源码分析 常用设计模式 Proxy代理模式Factory工厂模式Singleton单例模式Delegate委派模式Strategy策略模式Prototype原型模式Template模板模式 Spring5 beans 接口实例化代理Bean操作 Context Ioc容器设计原理及高级特性Aop设计原理Factorybean与Beanfactory Transaction 声明式事物

Java进阶13讲__第12讲_1/2

多线程、线程池 1.  线程概念 1.1  什么是线程 1.2  线程的好处 2.   创建线程的三种方式 注意事项 2.1  继承Thread类 2.1.1 认识  2.1.2  编码实现  package cn.hdc.oop10.Thread;import org.slf4j.Logger;import org.slf4j.LoggerFactory

每天认识几个maven依赖(ActiveMQ+activemq-jaxb+activesoap+activespace+adarwin)

八、ActiveMQ 1、是什么? ActiveMQ 是一个开源的消息中间件(Message Broker),由 Apache 软件基金会开发和维护。它实现了 Java 消息服务(Java Message Service, JMS)规范,并支持多种消息传递协议,包括 AMQP、MQTT 和 OpenWire 等。 2、有什么用? 可靠性:ActiveMQ 提供了消息持久性和事务支持,确保消

好题——hdu2522(小数问题:求1/n的第一个循环节)

好喜欢这题,第一次做小数问题,一开始真心没思路,然后参考了网上的一些资料。 知识点***********************************无限不循环小数即无理数,不能写作两整数之比*****************************(一开始没想到,小学没学好) 此题1/n肯定是一个有限循环小数,了解这些后就能做此题了。 按照除法的机制,用一个函数表示出来就可以了,代码如下