天天看点

16--Spring创建Bean的准备工作(一),从缓存中获取单例bean

新版连接 01–Spring源码深度解析目录

在前几个小节中介绍了,Spring的简介,SpringIoC容器初始化,Bean的生命周期,Spring注入的三种方式,BeanFactory和FactoryBean的区别,BeanPostProcessor和BeanFactoryPostProcessor的区别,从今天开始我们就要正式开始分析Spring Bean的加载过程,从头分析Spring的bean是如何从xml配置文件,一步一步的被实例化!

1. 从缓存中获取bean

对于单例bean,Sring在创建其实例后会进行缓存,以供下次使用,所以当我们向容器索要bean的时候,第一步就是尝试从缓存中获取bean的实例,如果没有获取到,再去创建新的bean的实例,那么本小节就先从缓存中获取bean开始讲起。

在12–Spring BeanFactory和FactoryBean的区别小节中,我们已经简介了BeanFactory和FactoryBean的区别,但是没有从源码的角度讲解,那么在本小节我们会从源码分析Spirng是如何获取BeanFactory和FactoryBean的缓存的。

2.Spring中的缓存对象简介

我们知道对于单例bean,Spring在创建bean之后都会缓存bean的实例,以供下次使用,那么这些bean被缓存到哪里了呢?打开

DefaultSingletonBeanRegistry

,可以发现该类中有很多Map和Set的缓存,这些缓存都代表什么呢?看下源码:

/** Cache of singleton objects: bean name to bean instance. */
/** 缓存beanName和bean实例 key-->beanName,value-->beanInstance */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

/** Cache of singleton factories: bean name to ObjectFactory. */
/** 缓存beanName和beanFactory key-->beanName,value-->beanFactory */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

/** Cache of early singleton objects: bean name to bean instance. */
/** 缓存beanName和bean实例 key-->beanName,value-->beanInstance 该缓存主要为了解决bean的循环依赖引用 */
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);

/** Set of registered singletons, containing the bean names in registration order. */
/** 缓存所有注册的单例beanName */
private final Set<String> registeredSingletons = new LinkedHashSet<>(256);
           

以上代码片段摘取了比较重要的缓存定义:

  • singletonObjects:缓存beanName和bean实例 key–>beanName,value–>beanInstance
  • singletonFactories:缓存beanName和beanFactory key–>beanName,value–>beanFactory
  • earlySingletonObjects:缓存beanName和bean实例 key–>beanName,value–>beanInstance

    该缓存主要为了解决bean的循环依赖引用

  • registeredSingletons:缓存所有注册的单例beanName

其他的对象暂时先不做介绍了,因为以上这些对象,我们将会在本小节中用到,大家先有所了解。

3.创建测试
  • 新建bean,Animal接口,Dog,Cat,DogFactoryBean实现类,其中DogFactoryBean还实现了FactoryBean接口,我们将Cat作为一个普通bean,将Dog作为DogFactoryBean
package com.lyc.cn.day07;

public interface Animal {
	void sayHello();
}

           
package com.lyc.cn.day07;

/**
 * @author: LiYanChao
 * @create: 2018-09-07 16:36
 */
public class Cat implements Animal {

	private String name;

	@Override
	public void sayHello() {
		System.out.println("大家好,我是一只名叫" + getName() + "的猫,我是一个普通的bean");
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}
}

           
package com.lyc.cn.day07;

/**
 * @author: LiYanChao
 * @create: 2018-09-07 16:36
 */
public class Dog implements Animal {
	private String name;

	@Override
	public void sayHello() {
		System.out.println("大家好,我是一只名叫" + getName() + "的狗,我是一个通过FactoryBean的getObject()方法创建的bean");
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}
}

           
package com.lyc.cn.day07;

import org.springframework.beans.factory.FactoryBean;

/**
 * @author: LiYanChao
 * @create: 2018-09-07 16:41
 */
public class DogFactoryBean implements FactoryBean<Dog>, Animal {

	private String name;

	@Override
	public Dog getObject() throws Exception {
		Dog dog = new Dog();
		dog.setName(this.name);
		return dog;
	}

	@Override
	public Class<?> getObjectType() {
		return Dog.class;
	}

	@Override
	public boolean isSingleton() {
		return true;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	@Override
	public void sayHello() {
		System.out.println("大家好,我是一只名叫" + getName() + "的狗,我是FactoryBean的实例");
	}
}

           
  • 新建day07.xml配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	   xmlns:p="http://www.springframework.org/schema/p"
	   xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">

<!--定义FactoryBean-->
<bean id="dog" class="com.lyc.cn.day07.DogFactoryBean" p:name="壮壮"/>

<!--定义普通bean-->
<bean id="cat" class="com.lyc.cn.day07.Cat" p:name="美美"/>

</beans>
           
  • 新建MyTest测试用例
package com.lyc.cn.day07;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.ClassPathResource;

/**
 * @author: LiYanChao
 * @create: 2018-09-07 16:43
 */
public class MyTest {
	private XmlBeanFactory xmlBeanFactory;

	@Before
	public void initXmlBeanFactory() {
		System.out.println("========测试方法开始=======\n");
		xmlBeanFactory = new XmlBeanFactory(new ClassPathResource("day07.xml"));
	}

	@After
	public void after() {
		System.out.println("\n========测试方法结束=======");
	}

	@Test
	public void test() throws Exception {
		// ① 获取两次dog,以将其缓存
		Animal dog = xmlBeanFactory.getBean("dog", Dog.class);
		dog = xmlBeanFactory.getBean("dog", Dog.class);
		dog.sayHello();

		// ② 获取两次&dog,以将其缓存
		DogFactoryBean dogFactoryBean = xmlBeanFactory.getBean("&dog", DogFactoryBean.class);
		dogFactoryBean = xmlBeanFactory.getBean("&dog", DogFactoryBean.class);
		dogFactoryBean.sayHello();

		// ③ 获取两次cat,以将其缓存
		Animal cat = xmlBeanFactory.getBean("cat", Cat.class);
		xmlBeanFactory.getBean("cat", Cat.class);
		cat.sayHello();

	}
}

           
  • 执行测试
========测试方法开始=======

大家好,我是一只名叫壮壮的狗,我是一个通过FactoryBean的getObject()方法创建的bean
大家好,我是一只名叫壮壮的狗,我是FactoryBean的实例
大家好,我是一只名叫美美的猫,我是一个普通的bean

========测试方法结束=======
           
2.beanName转换

接下来我们来分析从缓存中获取bean的过程,打开测试用例,debug跟踪代码到doGetBean()方法,transformedBeanName(name)方法就是对beanName的转换

protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,	@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {

	//转换bean的名称,去掉&前缀,且如果bean有别名的话,优先使用别名
	final String beanName = transformedBeanName(name);
	Object bean;

	// 从缓存中获取bean
	Object sharedInstance = getSingleton(beanName);
	if (sharedInstance != null && args == null) {
		//省略源码中的日志打印信息
		//获取给定beanName的实例对象(对于FactoryBean,可以是bean实例本身,也可以是它创建的对象。)
		bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
	}
}
           

转换的过程分为两步:去掉FactoryBean引用前缀,即&符;确定原始名称,将别名解析为规范名称。

  • 去掉FACTORY_BEAN_PREFIX前缀,打开BeanFactoryUtils.transformedBeanName(name)方法
/**
 * 返回bean的真实名称,去掉FactoryBean引用前缀
 * Return the actual bean name, stripping out the factory dereference
 * prefix (if any, also stripping repeated factory prefixes if found).
 * @param name the name of the bean
 * @return the transformed name
 * @see BeanFactory#FACTORY_BEAN_PREFIX
 */
public static String transformedBeanName(String name) {
	Assert.notNull(name, "'name' must not be null");
	String beanName = name;
	//beanName前缀为&,循环截取直至所有&被去掉
	while (beanName.startsWith(BeanFactory.FACTORY_BEAN_PREFIX)) {
		beanName = beanName.substring(BeanFactory.FACTORY_BEAN_PREFIX.length());
	}
	return beanName;
}
           
  • 确定原始名称,将别名解析为规范名称。打开canonicalName(BeanFactoryUtils.transformedBeanName(name))方法
/**
 * 确定原始名称,将别名解析为规范名称。
 * Determine the raw name, resolving aliases to canonical names.
 * @param name the user-specified name 用户指定的beanName
 * @return the transformed name 转换后的beanName
 */
public String canonicalName(String name) {
	String canonicalName = name;
	// Handle aliasing...
	String resolvedName;
	do {
		//从别名缓存Map中获取对应beanName
		resolvedName = this.aliasMap.get(canonicalName);
		if (resolvedName != null) {
			canonicalName = resolvedName;
		}
	}
	while (resolvedName != null);
	return canonicalName;
}
           

以上代码还是比较简单的,对于&符前缀的理解,大家可以参考12–Spring BeanFactory和FactoryBean的区别

3.从缓存中获取Bean,打开getSingleton(beanName)方法并进入其子函数中
/**
 * 返回在给定名称下注册的(原始)单例对象。
 * 检查已经实例化的单例,并允许对当前创建的单例的早期引用(解决循环引用)。
 * @param beanName 要查找的bean的名称
 * @param allowEarlyReference 是否应该创建早期引用
 * @return 返回已经注册单例bean,如果未获取到则返回null
 */
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
	// 从缓存中获取bean
	Object singletonObject = this.singletonObjects.get(beanName);
	// 未能获取到bean,但是允许对当前创建的单例的早期引用(解决循环引用)
	// isSingletonCurrentlyInCreation-->判断指定的单例bean是否当前正在创建(Spring只解决单例bean的循环依赖问题)
	if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
		synchronized (this.singletonObjects) {
			//从earlySingletonObjects获取提前曝光的bean
			singletonObject = this.earlySingletonObjects.get(beanName);
			//未能获取到提前曝光的bean且当前的bean允许被创建早期依赖
			if (singletonObject == null && allowEarlyReference) {
				//从缓存中获取BeanFactory
				ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
				if (singletonFactory != null) {
					//通过getObject()方法获取bean,注意:通过此方法获取的bean不是被缓存的
					singletonObject = singletonFactory.getObject();
					//将获取到的singletonObject缓存至earlySingletonObjects
					this.earlySingletonObjects.put(beanName, singletonObject);
					//从singletonFactories移除bean
					this.singletonFactories.remove(beanName);
				}
			}
		}
	}
	return singletonObject;
}
           

这一部分代码又涉及到了Spring中的bean循环依赖问题,在接下来的章节中会对此问题进行详细的讲解,这里大家可以不必理会,只需要记住从singletonObjects(缓存beanName和bean实例 key–>beanName,value–>beanInstance)获取缓存的bean即可。

4.获取给定beanName的实例对象

进入到

getObjectForBeanInstance(sharedInstance, name, beanName, null);

方法中,查看代码

/**
 * 获取给定bean实例的对象,对于FactoryBean,可以是bean实例本身,也可以是它创建的对象。
 * @param beanInstance  共享bean实例
 * @param name 可能包含工厂取消引用前缀的名称,通过getBean()方法时传递的原始beanName
 * @param beanName 规范bean名称
 * @param mbd 合并bean定义
 * @return the object to expose for the bean
 */
protected Object getObjectForBeanInstance(Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) {
	// 判断bean是否factoryBean
	// Don't let calling code try to dereference the factory if the bean isn't a factory.
	if (BeanFactoryUtils.isFactoryDereference(name)) {
		// 当前bean是factoryBean,且beanInstance是NullBean的实例,则返回beanInstance
		if (beanInstance instanceof NullBean) {
			return beanInstance;
		}
		// 当前bean是factoryBean,但是不是FactoryBean的实例,则抛出异常
		// 因BeanFactoryUtils.isFactoryDereference(name)-->只是从bean名称上进行了判断,我们通过getBean("&myBean")可以人为将一个非factoryBean当做factoryBean
		// 所以这里必须要判断beanInstance是否为FactoryBean的实例
		if (!(beanInstance instanceof FactoryBean)) {
			throw new BeanIsNotAFactoryException(transformedBeanName(name), beanInstance.getClass());
		}
	}

	/**
	 * 现在我们有了bean实例,它可能是一个普通的bean,也可能是一个FactoryBean。
	 * 如果它是FactoryBean,我们使用它创建一个bean实例,除非调用者实际上需要工厂的引用。
	 * 下面这句话稍微有些绕,首先判断beanInstance是FactoryBean的实例,然后又加了一个非的条件,将判断结果反置
	 * 再加一个或条件,判断该bean的name是否有&引用,这样一来就可以判断是返回bean的实例还是返回FactoryBean对象
	 * 例1:我们通过getBean("&myBean"),假设myBean实现了BeanFactory接口,那么myBean肯定是FactoryBean的实例
	 * 此时将第一个判断条件置否,再去判断bean的name是否包含了&符,如果是的话,那么就返回FactoryBean对象本身
	 *
	 * 例2:我们通过getBean("myBean"),假设myBean是一个普通的bean,那么它肯定不是FactoryBean的实例,
	 * 那么该bean跟FactoryBean无任何关系,直接返回其实例即可
	 */
	if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name)) {
		return beanInstance;
	}

	Object object = null;
	// 如果beanDefinition为null,则尝试从缓存中获取给定的FactoryBean公开的对象
	if (mbd == null) {
		object = getCachedObjectForFactoryBean(beanName);
	}
	// 未能从缓存中获得FactoryBean公开的对象,则说明该bean是一个新创建的bean
	// 下面的代码与缓存获取无关了,大家可以先不看。
	if (object == null) {
		// Return bean instance from factory.
		FactoryBean<?> factory = (FactoryBean<?>) beanInstance;
		// rootBeanDefinition为null,但是在beanDefinitionMap中缓存了对应的beanName
		// Caches object obtained from FactoryBean if it is a singleton.
		if (mbd == null && containsBeanDefinition(beanName)) {
			//合并beanDefinition(包括父类bean)
			mbd = getMergedLocalBeanDefinition(beanName);
		}
		// 如果beanDefinition不为null,则要判断该beanDefinition对象是否通过合成获得,
		// 如果不是,则说明该beanDefinition不由有程序本身定义的
		boolean synthetic = (mbd != null && mbd.isSynthetic());
		// 从给定的FactoryBean中获取指定的beanName对象
		object = getObjectFromFactoryBean(factory, beanName, !synthetic);
	}
	return object;
}
           
  • 从缓存中获取bean的过程:
    1. 解析beanName,包含了对beanName的规范和对FactoryBean的&符引用处理,如果有&会去掉。
    2. 尝试从singletonObjects缓存中获取bean,如果获取到直接返回并进一步处理。
    3. 如果未获取到,则会去判断该bean是否正在被创建(这里又涉及到了Bean的循环依赖,会在接下来的章节中详细介绍)并尝试从earlySingletonObjects获取提前曝光的bean。
    4. 如果未能获取到提前曝光的bean,尝试从BeanFactory缓存中获取指定beanName的BeanFactory。
    5. 如果可以获取到的话,则通过getObject()获取bean实例并缓存。
  • 对普通bean和FactoryBean的处理(假设已经从缓存中获取到了bean)
    1. 判断bean是否包含了对FactoryBean的前缀引用(&)。
    2. 如果有,判断bean是否NullBean实例,如果是,则直接返回。
    3. 判断bean是否FactoryBean实例,如果不是,则抛出异常。
    4. 通过if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name))判断,如果bean是FactoryBean的实例且beanName包含&符,则返回FactoryBean实例本身;如果bean不是FactoryBean的实例,则直接返回。

对于Spring缓存中获取bean就分析到这里了,希望大家可以多提意见。。。

继续阅读