新版连接 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的过程:
- 解析beanName,包含了对beanName的规范和对FactoryBean的&符引用处理,如果有&会去掉。
- 尝试从singletonObjects缓存中获取bean,如果获取到直接返回并进一步处理。
- 如果未获取到,则会去判断该bean是否正在被创建(这里又涉及到了Bean的循环依赖,会在接下来的章节中详细介绍)并尝试从earlySingletonObjects获取提前曝光的bean。
- 如果未能获取到提前曝光的bean,尝试从BeanFactory缓存中获取指定beanName的BeanFactory。
- 如果可以获取到的话,则通过getObject()获取bean实例并缓存。
- 对普通bean和FactoryBean的处理(假设已经从缓存中获取到了bean)
- 判断bean是否包含了对FactoryBean的前缀引用(&)。
- 如果有,判断bean是否NullBean实例,如果是,则直接返回。
- 判断bean是否FactoryBean实例,如果不是,则抛出异常。
- 通过if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name))判断,如果bean是FactoryBean的实例且beanName包含&符,则返回FactoryBean实例本身;如果bean不是FactoryBean的实例,则直接返回。
对于Spring缓存中获取bean就分析到这里了,希望大家可以多提意见。。。