Spring IOC 从单例池中获取Bean

Spring IOC 从单例池中获取Bean

  • 概述
  • getSingleton
  • isSingletonCurrentlyInCreation
  • getObjectForBeanInstance
    • getObjectFromFactoryBean
    • doGetObjectFromFactoryBean
    • isSingletonCurrentlyInCreation
  • 总结

概述

在doGetBean方法当中,处理完bename之后,下一步就是尝试从单例池中获取。如果能获取到,那么就对其进行FactoryBean的处理。当然这个处理对于FactoryBean才有用,对应代码如下:

		// Eagerly check singleton cache for manually registered singletons.
		//如果这里是我们的bean,那么第一次获取肯定是空的,那么这个时候就会走else
		Object sharedInstance = getSingleton(beanName);
		if (sharedInstance != null && args == null) {
			if (logger.isDebugEnabled()) {
				if (isSingletonCurrentlyInCreation(beanName)) {
					logger.debug("Returning eagerly cached instance of singleton bean '" + beanName +
							"' that is not fully initialized yet - a consequence of a circular reference");
				}
				else {
					logger.debug("Returning cached instance of singleton bean '" + beanName + "'");
				}
			}
			//2.完成FactoryBean的相关处理,并用来获取FactoryBean的处理结果。
			//缓存中的bean记录的是最原始的Bean状态,我们得到的不一定是最终想要的bean
			//
			bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
		}
  • 这个if选项如果是我们第一次调用这个方法来获取,那么必定通过getSingleton方法返回的对象是空的。那么我们将会进入到else流程中,执行创建Bean的流程。
  • 获取到Bean之后,由于FactoryBean的存储方式和普通Bean是一样的,如果这个Bean是FactoryBean的话,要对器进行处理,得到真正的Bean。

getSingleton

	//传入的第二个参数就是指是否允许延迟加载
	@Nullable
	protected Object getSingleton(String beanName, boolean allowEarlyReference) {
		//第一次调用getSongleton,返回肯定为空
		Object singletonObject = this.singletonObjects.get(beanName);
		if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
			synchronized (this.singletonObjects) {
				//从三级缓存拿,如果是第一次坑定是拿不到的
				singletonObject = this.earlySingletonObjects.get(beanName);
				//如果从三级缓存中拿不到,那么判断是否支持循环依赖,再从二级缓存中拿
				/**
				 * 为什么不直接从二级缓存中拿,而是要put到三级缓存中呢
				 * 性能问题:因为二级缓存是一个工厂,代码相当复杂。从二级缓存中创建出对象的时候进行大量的操作
				 * 为了避免每次引用这个对象都要使用工厂来创建,就需要使用到三级缓存。
				 * 既然有性能消耗,那么为什么还要使用工厂呢?
				 * 解答:因为在前面说过spring的生命周期:
				 * 1.new
				 * 2.注入
				 * 3.执行生命周期方法
				 * 4.AOP代理
				 * 5.放入单例池
				 * 还是循环依赖的场景。如果此时A到了注入的那一步,那么就会进入到B的生命周期。如果这个地方我们不使用工厂
				 * 那么在B中注入的A就是只执行到第二步的一个A的对象。B的A就不是AOP代理过后的对象。为了解决这个问题使用工厂,因为
				 * 它可以使这个A的AOP操作提前到这个工厂中来执行。所以说这个工厂的代码也是极其复杂的。为了避免重复执行这里面
				 * 的代码,这里也用到了三级缓存的。
				 */
				if (singletonObject == null && allowEarlyReference) {
					ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
					if (singletonFactory != null) {
						//从二级缓存中拿到了
						singletonObject = singletonFactory.getObject();
						//将其放到三级缓存中
						this.earlySingletonObjects.put(beanName, singletonObject);
						//将二级缓存中的清除掉
						this.singletonFactories.remove(beanName);
					}
				}
			}
		}
		return singletonObject;

这里面有一些注释是针对解决循环引用的问题来说嘚。

大致流程如下:首先直接到单例池中拿,如果没有,到三级缓存中拿,如果还拿不到,再到二级缓存中拿,拿到了之后最后要放入三级缓存,然后删除二级缓存中的。为什么要去二三级缓存这样的名字不重要。来看看这些单例池和缓存都是什么东西:

/**单例池 */
	private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
/** 三级缓存,这里的bean还只是一个对象,还没成为真正的bean beanname-->实例 */
	private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
/** beanname到能创建这个bean对象的工厂的映射 为什么是工厂之后再说*/
	private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

三级缓存是解决循环依赖的关键。如果是bean才刚刚开始创建,那么肯定获取不到。

  • allowEarlyReference:是否支持循环依赖,如果要关闭循环依赖,可以通过修改这个变量

isSingletonCurrentlyInCreation

	public boolean isSingletonCurrentlyInCreation(String beanName) {
		return this.singletonsCurrentlyInCreation.contains(beanName);
	}
/** 正在创建的bean的名字的集合 */
	private final Set<String> singletonsCurrentlyInCreation =
			Collections.newSetFromMap(new ConcurrentHashMap<>(16));

spring在解决循环依赖的时候,会将没有完成生命周期的bean放入到这个map中,然后通过isSingletonCurrentlyInCreation来判断是否是在创建之中。这个也是解决循环依赖的关键代码。

getObjectForBeanInstance

到这里bean就已经获取了,根据上面的代码,如果获取到了要调用getObjectForBeanInstance方法,进行FactoryBean的处理。这个方法的主要逻辑就是将一个获取到的Bean实例传入,如果它不是FactoryBean,那么就会返回它自己,如果是,那么就返回FactoryBean创建的实例。

为了方便理解,先给出这个方法的时序图:

Spring IOC 从单例池中获取Bean_第1张图片

protected Object getObjectForBeanInstance(
			Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) {

		// Don't let calling code try to dereference the factory if the bean isn't a factory.
		//1.若为工厂引用(name的开头是&)
		if (BeanFactoryUtils.isFactoryDereference(name)) {

			//如果name以&开头,//如果是nullBean直接返回
			//			if (beanInstance instanceof NullBean) {
			//				return beanInstance;
			//			}却又不是FactoryBean直接抛出异常。
			if (!(beanInstance instanceof FactoryBean)) {
				throw new BeanIsNotAFactoryException(beanName, beanInstance.getClass());
			}
		}

		// Now we have the bean instance, which may be a normal bean or a FactoryBean.
		// If it's a FactoryBean, we use it to create a bean instance, unless the
		// caller actually wants a reference to the factory.
		// 到这里我们就有了一个 Bean 实例,当然该实例可能是会是是一个正常的 bean 又或者是一个 FactoryBean
		// 如果是 FactoryBean,我我们则创建该 Bean,除非调用者需要的正是一个FactoryBean
		//这里在干嘛啊??????这个地方难道不是矛盾的吗
		if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name)) {
			return beanInstance;
		}

		Object object = null;
		if (mbd == null) {
			//3.若传入的BeanDefinition为null,就从缓存中加载Bean对象。
			//getCachedObjectForFactoryBean从一个map当中,通过beanName获取FactoryBean
			//这个缓存中保存了从factory中可以获取到的bean
			//这里的object获取到了就直接是最后需要的bean了
			object = getCachedObjectForFactoryBean(beanName);
		}
		// 若 object 依然为空,则可以确认,beanInstance 一定是 FactoryBean 。从而,使用 FactoryBean 获得 Bean 对象
		//因为缓存池中取不到
		if (object == null) {
			// 进行类型转换
			FactoryBean<?> factory = (FactoryBean<?>) beanInstance;
			// Caches object obtained from FactoryBean if it is a singleton.
			//containsBeanDefinition方法检查beanName是否在beanDefinitionMap 中,这个map中
			//保存所有已经加载的类。
			//这个if是肯定会被满足的
			if (mbd == null && containsBeanDefinition(beanName)) {
				// 将存储 XML 配置文件的 GenericBeanDefinition 转换为 RootBeanDefinition,
				// 如果指定 BeanName 是子 Bean 的话同时会合并父类的相关属性
				mbd = getMergedLocalBeanDefinition(beanName);
			}
			//判断是否是用户定义的,而不是应用程序本身定义的。
			//mbd.isSynthetic()判断是否为用户定义的
			//但是这里传入的mbd就null直接为false
			//下面传入的!synthetic就为true
			boolean synthetic = (mbd != null && mbd.isSynthetic());
			// 核心处理方法,使用 FactoryBean 获得 Bean 对象
			object = getObjectFromFactoryBean(factory, beanName, !synthetic);
		}
		return object;
	}

开头进行一堆判断,如果它是一个factorybean的话,最后会调用getObjectFromFactoryBean方法来执行从,factorybean中加载bean的逻辑。

  • getCachedObjectForFactoryBean的作用是从缓存中获取bean。由于一个factoryBean只能创建一个bean,所以可以将每个factoryBean对应的bean缓存起来,既保证了单例性,又免于复杂操作。
  • synthetic是当前应用是否是由系统自己加载的,这里传入的mbd为null,所以这个变量必定为null
  • 这个方法执行的很多都是检测逻辑。
    • 第一种,当该实例对象为非 FactoryBean 类型直接返回给定的 Bean 实例对象 beanInstance
    • 第二种,当该实例对象为FactoryBean 类型,从 FactoryBean ( beanInstance ) 中,获取 Bean 实例对象。

getObjectFromFactoryBean

protected Object getObjectFromFactoryBean(FactoryBean<?> factory, String beanName, boolean shouldPostProcess) {
		//1.为单例模式且在缓存中存在
		//containsSingleton直接到单例池中查看有没有
		if (factory.isSingleton() && containsSingleton(beanName)) {
			//互斥锁加锁
			synchronized (getSingletonMutex()) {
				//1.2从缓存中获取指定的factoryBean
				Object object = this.factoryBeanObjectCache.get(beanName);
				//如果为空,就从FactoryBean中获取对象
				//如果是第一次获取应该是获取不到的
				if (object == null) {
					//调用doGetObjectFromFactoryBean方法来获取bean
					object = doGetObjectFromFactoryBean(factory, beanName);
					// Only post-process and store if not put there already during getObject() call above
					// (e.g. because of circular reference processing triggered by custom getBean calls)
					Object alreadyThere = this.factoryBeanObjectCache.get(beanName);
					if (alreadyThere != null) {
						object = alreadyThere;
					}
					else {
						//1.3需要后续处理。shouldPostProcess为true,所以这里的代码会执行
						if (shouldPostProcess) {
							//若bean处于创建中,就返回非处理对象,而不是存储它
							if (isSingletonCurrentlyInCreation(beanName)) {
								// Temporarily return non-post-processed object, not storing it yet..
								return object;
							}
							//单例Bean的前置处理
							//会往singletonsCurrentlyInCreation中加入beanName
							beforeSingletonCreation(beanName);
							try {
								//对从FactoryBean获取的对象进行后处理
								//生成的对象将暴露给bean引用
								//这个方法的实现在这里啥也没干,需要自己扩展
								object = postProcessObjectFromFactoryBean(object, beanName);
							}
							catch (Throwable ex) {
								throw new BeanCreationException(beanName,
										"Post-processing of FactoryBean's singleton object failed", ex);
							}
							finally {
								//单例Bean的后置处理
								//就是beforeSingletonCreation的反操作
								afterSingletonCreation(beanName);
							}
						}
						//1.4添加到factoryBeanObject中进行缓存。
						//在这里加进去的
						if (containsSingleton(beanName)) {
							this.factoryBeanObjectCache.put(beanName, object);
						}
					}
				}
				return object;
			}
		}
		//到这里说明单例池中是没有这个beanName的
		else {
			//为空就从FactoryBean中加载对象
			Object object = doGetObjectFromFactoryBean(factory, beanName);
			//需要后续处理
			if (shouldPostProcess) {
				try {
					// 对从 FactoryBean 获取的对象进行后处理
					// 生成的对象将暴露给 bean 引用
					object = postProcessObjectFromFactoryBean(object, beanName);
				}
				catch (Throwable ex) {
					throw new BeanCreationException(beanName, "Post-processing of FactoryBean's object failed", ex);
				}
			}
			return object;
		}
	}

  • 该方法进来就会判断这个FactoryBean是否在单例池中包含着。那么什么情况不会包含着呢,那就是解决循环依赖的时候,该FactoryBean会处在创建中,而没有被单例池包括(不知道对不对,我是这样理解的)

  • 对单例池加锁,可以在Spring的IOC容器里看到很多对单例池加锁的代码,IOC容器要保证单例池全局唯一,所以对单例池加互斥锁。

  • 如果单例池中包含了,首先还是先检查缓存,缓存中检查不到,就调用doGetObjectFromFactoryBean方法,在这个方法中直接调用factory的getObject()方法来获取bean,这里就不展开讲了,然后分别会调用

    beforeSingletonCreation(beanName);postProcessObjectFromFactoryBean(object, beanName);以及afterSingletonCreation(beanName);具体作用看注释。

  • 如果单例池中获取不到,就不需要对单例池进行加锁。执行的逻辑也是差不多的。

  • 最后如果这个FactoryBean已经进入了单例池,那么会调用this.factoryBeanObjectCache.put(beanName, object);将bean放入到缓存,方便下次获取。

doGetObjectFromFactoryBean

	private Object doGetObjectFromFactoryBean(final FactoryBean<?> factory, final String beanName)
			throws BeanCreationException {

		Object object;
		try {
			//需要权限验证
			if (System.getSecurityManager() != null) {
				AccessControlContext acc = getAccessControlContext();
				try {
					//从FactoryBean中获取Bean对象
					object = AccessController.doPrivileged((PrivilegedExceptionAction<Object>) factory::getObject, acc);
				}
				catch (PrivilegedActionException pae) {
					throw pae.getException();
				}
			}
			else {
				//从FactoryBean中获取Bean对象
				object = factory.getObject();
			}
		}
		catch (FactoryBeanNotInitializedException ex) {
			throw new BeanCurrentlyInCreationException(beanName, ex.toString());
		}
		catch (Throwable ex) {
			throw new BeanCreationException(beanName, "FactoryBean threw exception on object creation", ex);
		}

		// Do not accept a null value for a FactoryBean that's not fully
		// initialized yet: Many FactoryBeans just return null then.
		if (object == null) {
			if (isSingletonCurrentlyInCreation(beanName)) {
				throw new BeanCurrentlyInCreationException(
						beanName, "FactoryBean which is currently in creation returned null from getObject");
			}
			object = new NullBean();
		}
		return object;
	}

主要逻辑就是object = factory.getObject();如果通过object = factory.getObject();都没有获取到,那么会构造一个NullBean返回。

isSingletonCurrentlyInCreation

这个方法是用于检测bean创建状态的,这个方法之前讲过。

	public boolean isSingletonCurrentlyInCreation(String beanName) {
		return this.singletonsCurrentlyInCreation.contains(beanName);
	}

再回来看beforeSingletonCreation(beanName);postProcessObjectFromFactoryBean(object, beanName);以及afterSingletonCreation(beanName);三个函数,在getObjectFromFactoryBean方法中被调用。主要作用如下(在注释中也有说明)

  • beforeSingletonCreation(beanName):将beanname添加到singletonsCurrentlyInCreation这个map中

  • afterSingletonCreation(beanName):将beanname从singletonsCurrentlyInCreation中移除

  • postProcessObjectFromFactoryBean(object, beanName): ,对从 FactoryBean 处获取的 Bean 实例对象进行后置处理。其默认实现是直接返回 object 对象,不做任何处理。

    // DefaultSingletonBeanRegistry.java
    
    protected Object postProcessObjectFromFactoryBean(Object object, String beanName) throws BeansException {
    	return object;
    }
    

    这个方法在这里没有做什么。但是子类可以重写,比如 AbstractAutowireCapableBeanFactory

    // AbstractAutowireCapableBeanFactory.java
    
    protected Object postProcessObjectFromFactoryBean(Object object, String beanName) {
    	return applyBeanPostProcessorsAfterInitialization(object, beanName);
    }
    

    applyBeanPostProcessorsAfterInitialization方法的作用是将当前bean传入到后置处理器中,让后置处理器对其进行处理。之后我们会详细讲解这个方法。

总结

到这里,主要学习了两个知识点,一是spring如何从单例池或者缓存中获取实例。二是获取的实例如果是一个FactoryBean,如何根据这个FactoryBean获取到真正的Bean实例。中间涉及到了很多的细节问题。

你可能感兴趣的:(spring)