Spring源码分析 循环依赖 三级缓存

文章目录

  • 本文目的
  • 类图
  • 什么是循环依赖
  • 如何解决
  • Spring三级缓存
    • 三个缓存分别放的是什么?
  • 如果只有二级缓存
  • 代理和三级缓存
    • 二级缓存在其中的作用
  • 源码分析
    • 创建对象之前先从缓存中查找
    • 从一二三级缓存中依次查找对象
    • 对象实例化时放入到三级缓存中
    • 何时放入一级缓存

本文目的

本文主要说明什么是Spring的循环依赖。哪些场景的循环依赖Spring可以解决。如何解决的。同时讲解了Spring的三级缓存。

类图

A
B b
+getB()
+setB()
B
A a
+getA()
+setA()

什么是循环依赖

参考上面的类图,Spring要初始化A类,但A类中有一个类型为B的属性,所以此时要去创建类型B。但是B类中又有一个A类型的属性,所以反过来又需要初始化A,但A此时又没有初始化完成。这就是循环依赖问题。

这个例子中说的是AB型循环依赖,还有ABC型。就是A中有B,B中有C,但C中又有A。

如果不考虑Spring,循环依赖其实并不是问题,因为对象之间相互依赖是很正常的事情。但在Spring中,一个对象并不是简单new出来就可以了,而是会经过一系列的Bean的生命周期。正式因为Beand生命周期的存在,才会出现循环依赖问题。所以建议大家可以先了解下Bean的生命周期。

如何解决

首先并不是所有场景的循环依赖Spring都能解决的。Spring只能解决单例对象且set方法的循环依赖。构造器或者多例对象目前Spring无法解决。这种情况需要程序员自己避免或者向其他办法解决。

接下来正式介绍Spring解决循环依赖的机制,叫做三级缓存。

Spring三级缓存

三级缓存说穿了,其实就是Spring中一个类的三个Map,

  • 一级缓存:singletonObjects;
  • 二级缓存为:earlySingletonObjects;
  • 三级缓存为:singletonFactories;

类名:DefaultSingletonBeanRegistry,源码参考如下:

public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {/*** 一级缓存* 用于保存BeanName和创建bean实例之间的关系** Cache of singleton objects: bean name to bean instance. */private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);/*** 三级缓存* 用于保存BeanName和创建bean的工厂之间的关系** Cache of singleton factories: bean name to ObjectFactory. */private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);/*** 二级缓存* 保存BeanName和创建bean实例之间的关系,与singletonFactories的不同之处在于,当一个单例bean被放到这里之后,那么当* bean还在创建过程中,就可以通过getBean方法获取到,可以方便进行循环依赖的检测** Cache of early singleton objects: bean name to bean instance. */private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);}

三个缓存分别放的是什么?

一级缓存(singletonObjects)中放的是已经初始化完成的bean对象。

二级缓存(earlySingletonObjects)比 singletonObjects 多了一个 early,表示存放的是实例化结束但还没初始化的bean对象。

三级缓存(singletonFactories)中存的是ObjectFactory,表示的是用来创建AOP代理对象的函数式接口。

如果只有二级缓存

如果不考虑AOP对象代理,其实只需要二级缓存足以解决问题。

我们先使用二级缓存来把解决流程演示一遍。

  1. 首先创建A的Bean对象,先依次在一二三级缓存中找。显然第一次肯定是找不到。
  2. 然后开始实例化A,实例化A结束后,会将A对象放入二级缓存中。此时A还没初始化。
  3. 然后进行属性赋值,遇到B对象。
  4. 同样的流程,先依次在一二三级缓存找。肯定也是找不到。
  5. 开始实例化B,实例化B结束,放入二级缓存中。此时B也没初始化。
  6. 然后进行属性赋值,遇到A对象。也是依次在一二三级缓存找
  7. 此时由于A对象已经在二级缓存中存在,所以找到后,完成B的属性赋值
  8. 接着继续B的初始化,然后初始化完成之后,放入一级缓存中。
  9. 接着A的属性赋值和初始化也能结束了。放入一级缓存中。
  10. 下面是用mermaid画的流程图。
B
A
三级缓存
A
B
B创建完成
初始化B
在二级缓存中找到A
去一、二、三级缓存中查找a
属性赋值A
放入二级缓存
实例化B
没有找到B
在一、二、三级缓存中查找B
A创建完成
初始化A
属性赋值B
放入二级缓存
实例化A
没有找到A
在一、二、三级缓存中查找A
三级缓存
二级缓存
一级缓存

代理和三级缓存

既然二级缓存就能解决循环依赖,那为什么考虑了AOP代理之后就必须使用三级缓存了呢?首先要了解的一个知识就是Spring的AOP代理对象的产生是在填充属性后进入到初始化阶段才进行的,是通过后置处理器BeanPostProcessor来实现。如果用二级缓存来解决,那么就要在属性填充的时候,就要将代理对象生成好,放入二级缓存了。那这样就与Spring的Bean生命周期相悖了。所以这种方式不好,于是就引入了三级缓存以及ObjectFactory对象。

引入了三级缓存之后的流程。

  1. 首先创建A的Bean对象,先依次在一二三级缓存中找。显然第一次肯定是找不到。
  2. 然后开始实例化A,实例化A结束后,那此时会将A放入三级缓存而不是二级缓存
  3. 放入三级缓存中的A是一个函数式接口ObjectFactory对象。此时并没有调用接口方法
  4. 然后进行属性赋值,遇到B对象。
  5. 同样的流程,先依次在一二三级缓存找。肯定也是找不到。
  6. 开始实例化B,实例化B结束,同样将ObjectFactory放入三级缓存中。
  7. 然后进行属性赋值,遇到A对象。也是依次在一二三级缓存找。
  8. 此时可以在三级缓存中找到A的ObjectFactory对象,找到后会调用ObjectFactory.getObject()方法,
  9. 将生成的代理对象放到二级缓存中。同时删除三级缓存中的对象。
  10. 接着继续B的初始化,然后初始化完成之后,放入一级缓存中。
  11. 接着A的属性赋值和初始化也能结束了。放入一级缓存中。
B
A
三级缓存
ObjectFactory(A)
ObjectFactory(B)
B创建完成
初始化B
通过ObjectFactory的getObject()
得到对象然后移入二级缓存
在三级缓存中找到ObjectFactory(A)
去一、二、三级缓存中查找a
属性赋值A
放入三级缓存
实例化B
没有找到B
在一、二、三级缓存中查找B
A创建完成
初始化A
属性赋值B
放入三级缓存
实例化A
没有找到A
在一、二、三级缓存中查找A
三级缓存
二级缓存
一级缓存

二级缓存在其中的作用

假设AB相互依赖,AC相互依赖。那么B实例化后,就该进行C的实例化,这时C就可以从二级缓存来获取A的实例引用了,就不需要再从三级缓存获取工厂让其生产实例。

即假设只有AB相互依赖,其他对象不依赖AB时,这里二级缓存是没用的,一级和三级缓存起作用。

源码分析

接下来我们可以从Spring源码中印证上述想法的正确性。

创建对象之前先从缓存中查找

AbstractBeanFactory.doGetBean()方法。第15行,先调用getSingleton方法去缓存中找。

	/*** 得到一个Bean实例的实例,**/protected <T> T doGetBean(String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)throws BeansException {/** 提取对应的beanName,这里需要转换的原因在于,当bean对象实现FactoryBean接口之后就会变成&beanName,同时如果存在别名,也需要把别名进行转换 */String beanName = transformedBeanName(name);Object bean;// Eagerly check singleton cache for manually registered singletons./** 提前检查单例缓存中是否有手动注册的单例对象,跟循环依赖有关联 */Object sharedInstance = getSingleton(beanName);// 如果bean的单例对象找到了,且没有创建bean实例时要使用的参数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 + "'");}}// 返回对象的实例,当你实现了FactoryBean接口的对象,需要获取具体的对象的时候就需要此方法来进行获取了bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);}else {// Fail if we're already creating this bean instance:// We're assumably within a circular reference.// 当对象都是单例的时候会尝试解决循环依赖的问题,但是原型模式下如果存在循环依赖的情况,那么直接抛出异常if (isPrototypeCurrentlyInCreation(beanName)) {throw new BeanCurrentlyInCreationException(beanName);}// Check if bean definition exists in this factory.// 如果bean定义不存在,就检查父工厂是否有BeanFactory parentBeanFactory = getParentBeanFactory();// 如果beanDefinitionMap中也就是在所有已经加载的类中不包含beanName,那么就尝试从父容器中获取if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {// Not found -> check parent.// 获取name对应的规范名称【全类名】,如果name前面有'&',则会返回'&'+规范名称【全类名】String nameToLookup = originalBeanName(name);// 如果父工厂是AbstractBeanFactory的实例if (parentBeanFactory instanceof AbstractBeanFactory) {// 调用父工厂的doGetBean方法,就是该方法。【递归】return ((AbstractBeanFactory) parentBeanFactory).doGetBean(nameToLookup, requiredType, args, typeCheckOnly);}else if (args != null) {// Delegation to parent with explicit args.// 如果有创建bean实例时要使用的参数// Delegation to parent with explicit args. 使用显示参数委派给父工厂// 使用父工厂获取该bean对象,通bean全类名和创建bean实例时要使用的参数return (T) parentBeanFactory.getBean(nameToLookup, args);}else if (requiredType != null) {// No args -> delegate to standard getBean method.// 没有创建bean实例时要使用的参数 -> 委托给标准的getBean方法。// 使用父工厂获取该bean对象,通bean全类名和所需的bean类型return parentBeanFactory.getBean(nameToLookup, requiredType);}else {// 使用父工厂获取bean,通过bean全类名return (T) parentBeanFactory.getBean(nameToLookup);}}// 如果不是做类型检查,那么表示要创建bean,此处在集合中做一个记录if (!typeCheckOnly) {// 为beanName标记为已经创建(或将要创建)markBeanAsCreated(beanName);}try {// 此处做了BeanDefinition对象的转换,当我们从xml文件中加载beandefinition对象的时候,封装的对象是GenericBeanDefinition,// 此处要做类型转换,如果是子类bean的话,会合并父类的相关属性RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);// 检查mbd的合法性,不合格会引发验证异常checkMergedBeanDefinition(mbd, beanName, args);// Guarantee initialization of beans that the current bean depends on.// 如果存在依赖的bean的话,那么则优先实例化依赖的beanString[] dependsOn = mbd.getDependsOn();if (dependsOn != null) {// 如果存在依赖,则需要递归实例化依赖的beanfor (String dep : dependsOn) {// 如果beanName已注册依赖于dependentBeanName的关系if (isDependent(beanName, dep)) {throw new BeanCreationException(mbd.getResourceDescription(), beanName,"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");}// 注册各个bean的依赖关系,方便进行销毁registerDependentBean(dep, beanName);try {// 递归优先实例化被依赖的BeangetBean(dep);}// 捕捉为找到BeanDefinition异常:'beanName'依赖于缺少的bean'dep'catch (NoSuchBeanDefinitionException ex) {throw new BeanCreationException(mbd.getResourceDescription(), beanName,"'" + beanName + "' depends on missing bean '" + dep + "'", ex);}}}// Create bean instance.// 创建bean的实例对象if (mbd.isSingleton()) {// 返回以beanName的(原始)单例对象,如果尚未注册,则使用singletonFactory创建并注册一个对象:sharedInstance = getSingleton(beanName, () -> {try {// 为给定的合并后BeanDefinition(和参数)创建一个bean实例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.// 显示地从单例缓存中删除实例:它可能是由创建过程急切地放在那里,以允许循环引用解析。还要删除// 接收到该Bean临时引用的任何Bean// 销毁给定的bean。如果找到相应的一次性Bean实例,则委托给destoryBeandestroySingleton(beanName);// 重新抛出exthrow ex;}});// 从beanInstance中获取公开的Bean对象,主要处理beanInstance是FactoryBean对象的情况,如果不是// FactoryBean会直接返回beanInstance实例bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);}// 原型模式的bean对象创建else if (mbd.isPrototype()) {// It's a prototype -> create a new instance.// 它是一个原型 -> 创建一个新实例// 定义prototype实例Object prototypeInstance = null;try {// 创建Prototype对象前的准备工作,默认实现将beanName添加到prototypesCurrentlyInCreation中beforePrototypeCreation(beanName);// 为mbd(和参数)创建一个bean实例prototypeInstance = createBean(beanName, mbd, args);}finally {// 创建完prototype实例后的回调,默认是将beanName从prototypesCurrentlyInCreation移除afterPrototypeCreation(beanName);}// 从beanInstance中获取公开的Bean对象,主要处理beanInstance是FactoryBean对象的情况,如果不是// FactoryBean会直接返回beanInstance实例bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);}else {// 指定的scope上实例化beanString scopeName = mbd.getScope();if (!StringUtils.hasLength(scopeName)) {throw new IllegalStateException("No scope name defined for bean ´" + beanName + "'");}// 从scopes中获取scopeName对于的Scope对象Scope scope = this.scopes.get(scopeName);// 如果scope为nullif (scope == null) {// 抛出非法状态异常:没有名为'scopeName'的scope注册throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");}try {// 从scope中获取beanName对应的实例对象Object scopedInstance = scope.get(beanName, () -> {// 创建Prototype对象前的准备工作,默认实现 将beanName添加到prototypesCurrentlyInCreation中beforePrototypeCreation(beanName);try {// 为mbd(和参数)创建一个bean实例return createBean(beanName, mbd, args);}finally {// 创建完prototype实例后的回调,默认是将beanName从prototypesCurrentlyInCreation移除afterPrototypeCreation(beanName);}});// 从beanInstance中获取公开的Bean对象,主要处理beanInstance是FactoryBean对象的情况,如果不是// FactoryBean会直接返回beanInstance实例bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);}catch (IllegalStateException ex) {// 捕捉非法状态异常// 抛出Bean创建异常:作用域 'scopeName' 对于当前线程是不活动的;如果您打算从单个实例引用它,请考虑为此// beanDefinition一个作用域代理throw new BeanCreationException(beanName,"Scope '" + scopeName + "' is not active for the current thread; consider " +"defining a scoped proxy for this bean if you intend to refer to it from a singleton",ex);}}}catch (BeansException ex) {// 捕捉获取Bean对象抛出的Bean异常// 在Bean创建失败后,对缓存的元数据执行适当的清理cleanupAfterBeanCreationFailure(beanName);// 重新抛出exthrow ex;}}// Check if required type matches the type of the actual bean instance.// 检查requiredType是否与实际Bean实例的类型匹配// 如果requiredType不为null&&bean不是requiredType的实例if (requiredType != null && !requiredType.isInstance(bean)) {try {// 获取此BeanFactory使用的类型转换器,将bean转换为requiredTypeT convertedBean = getTypeConverter().convertIfNecessary(bean, requiredType);// 如果convertedBean为nullif (convertedBean == null) {// 抛出Bean不是必要类型的异常throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());}// 返回convertedBeanreturn convertedBean;}catch (TypeMismatchException ex) {if (logger.isTraceEnabled()) {logger.trace("Failed to convert bean '" + name + "' to required type '" +ClassUtils.getQualifiedName(requiredType) + "'", ex);}throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());}}// 将bean返回出去return (T) bean;}

从一二三级缓存中依次查找对象

DefaultSingletonBeanRegistry.getSingleton()方法。从三级缓存中找到对象之后还会调用ObjectFactory的getObject()方法得到单例对象。放到二级缓存中,同时从三级缓存中移除。

protected Object getSingleton(String beanName, boolean allowEarlyReference) {// Quick check for existing instance without full singleton lock// 从单例对象缓存(一级缓存)中获取beanName对应的单例对象Object singletonObject = this.singletonObjects.get(beanName);// 如果单例对象缓存中没有,并且该beanName对应的单例bean正在创建中if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {// 从早期单例对象缓存(二级缓存)中获取单例对象(之所称成为早期单例对象,是因为earlySingletonObjects里的对象的都是通过提前曝光的ObjectFactory创建出来的,还未进行属性填充等操作)singletonObject = this.earlySingletonObjects.get(beanName);// 如果在早期单例对象缓存中也没有,并且允许创建早期单例对象引用if (singletonObject == null && allowEarlyReference) {// 如果为空,则锁定全局变量并进行处理synchronized (this.singletonObjects) {// Consistent creation of early reference within full singleton locksingletonObject = this.singletonObjects.get(beanName);if (singletonObject == null) {singletonObject = this.earlySingletonObjects.get(beanName);if (singletonObject == null) {// 当某些方法需要提前初始化的时候则会调用addSingletonFactory方法将对应的ObjectFactory初始化策略存储在singletonFactoriesObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);if (singletonFactory != null) {// 如果存在单例对象工厂,则通过工厂创建一个单例对象singletonObject = singletonFactory.getObject();// 记录在缓存中,二级缓存和三级缓存的对象不能同时存在this.earlySingletonObjects.put(beanName, singletonObject);// 从三级缓存中移除this.singletonFactories.remove(beanName);}}}}}}return singletonObject;}

对象实例化时放入到三级缓存中

AbstractAutowireCapableBeanFactory.doCreateBean()方法.对于符合循环依赖条件的bean,会往三级缓存中放入一个lambda表达式返回的ObjectFactory对象

// ... 省略非关键代码
// 判断当前bean是否需要提前曝光:单例&允许循环依赖&当前bean正在创建中,检测循环依赖	
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");}// 为避免后期循环依赖,可以在bean初始化完成前将创建实例的ObjectFactory加入工厂addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));synchronized (this.singletonObjects) {if (!this.singletonObjects.containsKey(beanName)) {//实例化后的对象先添加到三级缓存中,三级缓存对应beanName的是一个lambda表达式(能够触发创建代理对象的机制)this.singletonFactories.put(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));this.registeredSingletons.add(beanName);}}}// Initialize the bean instance.
// 初始化bean实例
Object exposedObject = bean;
try {// 对bean的属性进行填充,将各个属性值注入,其中,可能存在依赖于其他bean的属性,则会递归初始化依赖的beanpopulateBean(beanName, mbd, instanceWrapper);// 执行初始化逻辑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);}
}

何时放入一级缓存

AbstractBeanFactory的doGetBean()方法中。运行createBean方法后,会调用getSingleton方法。

// 创建bean的实例对象
if (mbd.isSingleton()) {// 返回以beanName的(原始)单例对象,如果尚未注册,则使用singletonFactory创建并注册一个对象:sharedInstance = getSingleton(beanName, () -> {try {// 为给定的合并后BeanDefinition(和参数)创建一个bean实例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.// 显示地从单例缓存中删除实例:它可能是由创建过程急切地放在那里,以允许循环引用解析。还要删除// 接收到该Bean临时引用的任何Bean// 销毁给定的bean。如果找到相应的一次性Bean实例,则委托给destoryBeandestroySingleton(beanName);// 重新抛出exthrow ex;}});// 从beanInstance中获取公开的Bean对象,主要处理beanInstance是FactoryBean对象的情况,如果不是// FactoryBean会直接返回beanInstance实例bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}

这里的getSingleton方法和上面的不一样,如果从一级缓存没有找到,那么会调用addSingleton()方法将其加入到一级缓存中。

	public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {// 如果beanName为null,抛出异常Assert.notNull(beanName, "Bean name must not be null");// 使用单例对象的高速缓存Map作为锁,保证线程同步synchronized (this.singletonObjects) {// 从单例对象的高速缓存Map中获取beanName对应的单例对象Object singletonObject = this.singletonObjects.get(beanName);// 如果单例对象获取不到if (singletonObject == null) {// 如果当前在destorySingletons中if (this.singletonsCurrentlyInDestruction) {throw new BeanCreationNotAllowedException(beanName,"Singleton bean creation not allowed while singletons of this factory are in destruction " +"(Do not request a bean from a BeanFactory in a destroy method implementation!)");}// 如果当前日志级别时调试if (logger.isDebugEnabled()) {logger.debug("Creating shared instance of singleton bean '" + beanName + "'");}// 创建单例之前的回调,默认实现将单例注册为当前正在创建中beforeSingletonCreation(beanName);// 表示生成了新的单例对象的标记,默认为false,表示没有生成新的单例对象boolean newSingleton = false;// 有抑制异常记录标记,没有时为true,否则为falseboolean recordSuppressedExceptions = (this.suppressedExceptions == null);// 如果没有抑制异常记录if (recordSuppressedExceptions) {// 对抑制的异常列表进行实例化(LinkedHashSet)this.suppressedExceptions = new LinkedHashSet<>();}try {// 从单例工厂中获取对象singletonObject = singletonFactory.getObject();// 生成了新的单例对象的标记为true,表示生成了新的单例对象newSingleton = true;}catch (IllegalStateException ex) {// Has the singleton object implicitly appeared in the meantime ->// if yes, proceed with it since the exception indicates that state.// 同时,单例对象是否隐式出现 -> 如果是,请继续操作,因为异常表明该状态// 尝试从单例对象的高速缓存Map中获取beanName的单例对象singletonObject = this.singletonObjects.get(beanName);// 如果获取失败,抛出异常if (singletonObject == null) {throw ex;}}// 捕捉Bean创建异常catch (BeanCreationException ex) {// 如果没有抑制异常记录if (recordSuppressedExceptions) {// 遍历抑制的异常列表for (Exception suppressedException : this.suppressedExceptions) {// 将抑制的异常对象添加到 bean创建异常 中,这样做的,就是相当于 '因XXX异常导致了Bean创建异常‘ 的说法ex.addRelatedCause(suppressedException);}}// 抛出异常throw ex;}finally {// 如果没有抑制异常记录if (recordSuppressedExceptions) {// 将抑制的异常列表置为null,因为suppressedExceptions是对应单个bean的异常记录,置为null// 可防止异常信息的混乱this.suppressedExceptions = null;}// 创建单例后的回调,默认实现将单例标记为不在创建中afterSingletonCreation(beanName);}// 生成了新的单例对象if (newSingleton) {// 将beanName和singletonObject的映射关系添加到该工厂的单例缓存中:addSingleton(beanName, singletonObject);}}// 返回该单例对象return singletonObject;}}/*** 将beanName和singletonObject的映射关系添加到该工厂的单例缓存中** Add the given singleton object to the singleton cache of this factory.* <p>To be called for eager registration of singletons.* @param beanName the name of the bean* @param singletonObject the singleton object*/protected void addSingleton(String beanName, Object singletonObject) {synchronized (this.singletonObjects) {// 将映射关系添加到单例对象的高速缓存(一级缓存)中this.singletonObjects.put(beanName, singletonObject);// 移除beanName在单例工厂缓存中的数据(三级缓存)this.singletonFactories.remove(beanName);// 移除beanName在早期单例对象的高速缓存的数据(二级缓存)this.earlySingletonObjects.remove(beanName);// 将beanName添加到已注册的单例集中this.registeredSingletons.add(beanName);}}

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

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

相关文章

腾讯云cvm云硬盘扩容

过去一直记得腾讯云的系统盘扩容,关于系统盘的扩容直接点资源调整-云硬盘扩容 系统盘扩容后就可以直接使用的&#xff1f; 但是现在操作了发现vda 200G 但是现在vda1不能自动扩容了&#xff1f; 腾讯云cvm云硬盘扩容 先看一眼官方文档吧&#xff1a;在线扩展系统盘分区及文…

LeetCode每日一题:1993. 树上的操作(2023.9.23 C++)

目录 1993. 树上的操作 题目描述&#xff1a; 实现代码与解析&#xff1a; 模拟 dfs 原理思路&#xff1a; 1993. 树上的操作 题目描述&#xff1a; 给你一棵 n 个节点的树&#xff0c;编号从 0 到 n - 1 &#xff0c;以父节点数组 parent 的形式给出&#xff0c;其中 p…

Android开发MVP架构记录

Android开发MVP架构记录 安卓的MVP&#xff08;Model-View-Presenter&#xff09;架构是一种常见的软件设计模式&#xff0c;用于帮助开发者组织和分离应用程序的不同组成部分。MVP架构的目标是将应用程序的业务逻辑&#xff08;Presenter&#xff09;、用户界面&#xff08;V…

Mybatis自动映射Java对象 与 MySQL8后的JSON数据

文章目录 Mybatis自动映射Java对象 与 MySQL8后的JSON数据1.转化成为正常Json类型1.1 JsonTypeHander1.2 ListJsonTypeHandler 负责List<T> 类型1.3 实体类1.4 mapper1.5 测试类 2. 存储为携带类型的Json Mybatis自动映射Java对象 与 MySQL8后的JSON数据 1.转化成为正常…

【密码学补充知识】

&#x1f511;密码学&#x1f512;概述 &#x1f4d5; 1.基本概念 明文 &#xff1a; 要交换的信息 密文 &#xff1a; 明文经过一组规则变换成看似没有意义的随机消息。 加密 &#xff1a; 明文经过一组规则变换成密文的过程 解密 &#xff1a; 密文恢复出明文的过程 加…

MT1184矩形相交 题解【超详细】

目录 题目 样例 题目解析 代码 图解 矩形相交 题目 输入2个矩形的左上角和右下角两个点的坐标值(x&#xff0c;y)&#xff0c;判断2个矩形是否相交&#xff0c;输出YES或者NO。矩形的边应与x&#xff0c;y轴相平行。假定输入坐标能顺利构成矩形&#xff0c;不考虑无效矩形…

macOS使用官方安装包安装python

新手程序员可能想知道如何在 Mac 上正确安装 Python&#xff0c;这里介绍在 macOS 上安装 Python 的方法。 操作步骤 1.从 Python 官方网站 (python.org) 下载最新的 Python 版本. 单击 macOS 链接并选择最新的 Python 版本。 2.下载完成后&#xff0c;双击包开始安装Python…

二、ubuntu主机端tftp及nfs服务开发环境安装

一.主机端tftp服务环境安装及配置 检查是否已经安装tftp server $dpkg -s tftpd-hpa#如果提示未安装服务&#xff0c;则执行下面安装指令$sudo apt-get install tftpd-hpa tftp-hpa#tftpd-hpa服务端 tftp-hpa客户端创建tftp启动目录&#xff0c;用于存放内核与设备树文件&a…

利用 spring test 实现自动启动spring 容器进行 JPA接口测试

自动启动context JPA接口测试 import com.alibaba.druid.pool.DruidDataSource; import org.junit.BeforeClass; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cont…

第6讲:v-for使用

目录 1.循环遍历 2.v-for遍历整形变量&#xff08;99乘法表&#xff09; 3.v-for遍历普通数组 4.v-for遍历数组对象 1.循环遍历 v-for指令基于一个数组渲染一个列表&#xff0c;它和JavaScript的遍历语法相似&#xff1a; v-for”item in list” list 是一个数组&#xff0c; i…

[C++ 网络协议] Windows中的线程同步

目录 1. 用户模式(User mode)和内核模式(Kernal mode) 2. 用户模式的同步(CRITICAL_SECTION) 3. 内核模式同步 3.1 互斥量 3.2 信号量 3.3 事件对象 4. 实现Windows平台的多线程服务器端 1. 用户模式(User mode)和内核模式(Kernal mode) Windows操作系统的运行方式是“…

windows平台 git bash使用

打开所在需要git管理的目录,鼠标右键open Git BASH here 这样就直接进来,不需要windows dos窗口下麻烦的切路径&#xff0c;windows和linux 路径方向不一致 (\ /) 然后git init 建立本地仓库,接下来就是git相关的操作了. 图形化界面查看 打开所在需要git管理的目录,鼠标右键…

kubernetes问题(一)-探究Pod被驱逐的原因及解决方法

1 k8s evicted是什么 k8s evicted是Kubernetes中的一个组件&#xff0c;主要用于处理Pod驱逐的情况。在Kubernetes中&#xff0c;当Node节点资源不够用时&#xff0c;为了保证整个集群的运行稳定&#xff0c;会按照一定的优先级和策略将其中的Pod驱逐出去。这时就需要一个组件…

软考高级架构师下篇-17安全架构设计理论与实践

目录 1. 引言信息安全面临的威胁2. 安全体系架构的范围3.典型安全模型4.信息安全整体架构设计5.数据库系统安全设计6.系统架构脆弱性分析7.安全架构设计实践8. 前文回顾1. 引言 随着科技的发展,信息系统的安全受到诸多方面的威胁,设计信息系统安全架构需要从各个方面考虑,这…

如何通过优化Read-Retry机制降低SSD读延迟?

近日,小编发现发表于2021论文中,有关于优化Read-Retry机制降低SSD读延迟的研究,小编这里给大家分享一下这篇论文的核心的思路,感兴趣的同学可以,可以在【存储随笔】VX公号后台回复“Optimizing Read-Retry”获取下载链接。 本文中主要基于Charge Trap NAND架构分析。NAND基…

MySQL5.7开启通用日志功能

起因&#xff1a; 因项目数据库占用异常&#xff0c;查询数据库有哪些IP地址连接使用&#xff08;Windows环境下&#xff09;。 操作步骤&#xff1a; 1、修改MySQL服务的my.ini 文件 # 开启通用查询日志 general_log 1 log_output …

buuctf-[WUSTCTF2020]朴实无华

打开环境就这么一句话 先打开index.php,但是没有什么 查看了下网络 看到gzip和php 我试了试www.zip 还有index.phps&#xff0c;也是一样的&#xff0c;都没找到文件 于是我想到用御剑扫&#xff0c;但是我好像线程太长了&#xff0c;一个没扫到&#xff0c;我就想到用dirsea…

人工智能AI 全栈体系(六)

第一章 神经网络是如何实现的 这些年神经网络的发展越来越复杂&#xff0c;应用领域越来越广&#xff0c;性能也越来越好&#xff0c;但是训练方法还是依靠 BP 算法。也有一些对 BP 算法的改进算法&#xff0c;但是大体思路基本是一样的&#xff0c;只是对 BP 算法个别地方的一…

Jmeter配置性能监控插件

一、版本不兼容时&#xff0c;有报错 1、当jmeter版本比较高时&#xff0c;只需要从官网安装jmeter-plugins-manager-1.10.jar一个包 2、当jmeter版本较低时&#xff0c;安装JMeterPlugins-Extras-1.4.0.zip、JMeterPlugins-Standard-1.4.0.zip内两个jar包 3、服务器上传文件…

FPGA到底是什么?

首先只是凭自己浅略的了解&#xff0c;FPGA好像也是涉及到了开发板&#xff0c;单片机之类的东西&#xff0c;和嵌入式十分相似&#xff0c;但是比嵌入式更高级的东西。 肯定有很多小伙伴如我一样&#xff0c;只是听说过FPGA&#xff0c;听别人说的传呼其神&#xff0c;那么它到…