我们之前已经研究了spring容器初始的主干线,接下来我们将要开始bean加载这条主线的研究。在bean加载的这条新的主线,我们会看到前面注册到spring容器中的BeanDefinition,是如何用来实例化bean的。
我们看下我们之前写的一段获取bean的代码:
可以看到这是之前我们分析ApplicationContext写的一段代码,其中bean的加载其实就是从getBean方法开始的,我们可以从getBean方法作为入口,开始分析spring 是如何加载bean的。我们先进到ApplicationContext的getBean方法中看下:
我们进入到AbstractApplicationContext的getBean方法中,在这个方法中有两行代码,我们先到assertBeanFactoryActive中看下:
在方法assertBeanFactoryActive中,只不过是判断当前的spring容器,是否被激活了或者是关闭了,如果容器还未被激活或发现容器已经关闭了就会抛出异常。如果spring容器关闭了或者还未初始化,也就没有办法加载bean了。
我们回到刚才的代码:
接下来就会通过getBeanFactory方法获取ApplicationContext中的spring容器初级容器BeanFactory,然后调用BeanFactory中的getBean方法去加载bean。所以,接下来我们到初级容器的getBean方法中看下:
我发现了一个比较关键的方法doGetBean,前面我们也提到过,也就是以do为前缀的方法差不多就是核心方法了,很明显方法doGetBean就是加载bean的核心方法的入口了。
接下来我们到doGetBean方法中看下:
@SuppressWarnings("unchecked")
protected <T> T doGetBean(
String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
throws BeansException {
// 转换并得到bean的最终名称
String beanName = transformedBeanName(name);
Object bean;
// Eagerly check singleton cache for manually registered singletons.
// 根据bean的名称beanName,看下是否在单例缓存中已经注册了当前的bean
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的示例对象
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}
else {
// 如果当前bean对象还没有被实例化,就对当前的bean进行实例化了
// Fail if we're already creating this bean instance:
// We're assumably within a circular reference.
// 如果发现名称为beanName的bean正在创建,并且bean的类型为原型prototype抛出异常
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
// Check if bean definition exists in this factory.
// 获取当前spring容器的父容器
BeanFactory parentBeanFactory = getParentBeanFactory();
// 如果spring容器中还没有注册名称为beanName的BeanDefinition,并且父类容器存在就到spring容器的父容器中加载bean。
if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
// Not found -> check parent.
String nameToLookup = originalBeanName(name);
if (parentBeanFactory instanceof AbstractBeanFactory) {
return ((AbstractBeanFactory) parentBeanFactory).doGetBean(
nameToLookup, requiredType, args, typeCheckOnly);
}
else if (args != null) {
// Delegation to parent with explicit args.
return (T) parentBeanFactory.getBean(nameToLookup, args);
}
else if (requiredType != null) {
// No args -> delegate to standard getBean method.
return parentBeanFactory.getBean(nameToLookup, requiredType);
}
else {
return (T) parentBeanFactory.getBean(nameToLookup);
}
}
if (!typeCheckOnly) {
// 标记beanName 对应的bean已经开始创建了
markBeanAsCreated(beanName);
}
try {
// 将beanName 对应spring容器中的BeanDefinition合并封装成RootBeanDefinition类型的BeanDefinition
RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
// 检查下合并好的RootBeanDefinition是否达到了实例化bean的条件
checkMergedBeanDefinition(mbd, beanName, args);
// Guarantee initialization of beans that the current bean depends on.
// 获取当前bean依赖的那些bean的名称数组
String[] dependsOn = mbd.getDependsOn();
if (dependsOn != null) {
for (String dep : dependsOn) {
// 判断当前bean的名称和自己依赖的那些bean名称存在循环关系
if (isDependent(beanName, dep)) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
}
// 注册依赖的bean
registerDependentBean(dep, beanName);
try {
// 递归调用getBean方法,提前实例化bean依赖的那些bean
getBean(dep);
}
catch (NoSuchBeanDefinitionException ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"'" + beanName + "' depends on missing bean '" + dep + "'", ex);
}
}
}
// Create bean instance.
// 如果当前BeanDefinition为单例类型的,则开始实例化创建单例singleton类型的bean
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, () -> {
try {
// 创建单例single类型的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.
destroySingleton(beanName);
throw ex;
}
});
// 获取bean的示例对象
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
// 如果当前BeanDefinition为原型protoType类型的,则开始实例化原型protoType类型的bean
else if (mbd.isPrototype()) {
// It's a prototype -> create a new instance.
Object prototypeInstance = null;
try {
// 实例化bean之前的处理
beforePrototypeCreation(beanName);
// 创建原型protoType类型的bean
prototypeInstance = createBean(beanName, mbd, args);
}
finally {
// 实例化bean之后的处理
afterPrototypeCreation(beanName);
}
// 获取bean的示例对象
bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
}
else {
// 创建其他作用域类型的bean
String scopeName = mbd.getScope();
if (!StringUtils.hasLength(scopeName)) {
throw new IllegalStateException("No scope name defined for bean ´" + beanName + "'");
}
// 获取作用域对象
Scope scope = this.scopes.get(scopeName);
// 如果没有为BeanDefinition设置scope则抛出异常
if (scope == null) {
throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
}
try {
Object scopedInstance = scope.get(beanName, () -> {
// 实例化bean之前的处理
beforePrototypeCreation(beanName);
try {
// 创建其他作用域类型的bean
return createBean(beanName, mbd, args);
}
finally {
// 实例化bean之后的处理
afterPrototypeCreation(beanName);
}
});
// 获取bean的示例对象
bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
}
catch (IllegalStateException ex) {
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) {
cleanupAfterBeanCreationFailure(beanName);
throw ex;
}
}
// Check if required type matches the type of the actual bean instance.
// 根据参数传进来的bean类型requiredType对实例化后的bean进行检查和转换
if (requiredType != null && !requiredType.isInstance(bean)) {
try {
// 将bean转换为参数指定类型的bean
T convertedBean = getTypeConverter().convertIfNecessary(bean, requiredType);
if (convertedBean == null) {
throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
}
return 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());
}
}
return (T) bean;
}
可以看到,在这个doGetBean方法代码虽然很多,但是不管怎么样,Spring中bean加载的核心逻辑都在doGetBean方法中,接下来我们慢慢来分析这一块代码。现在我们开始看下doGetBean中代码逻辑吧:
首先调用了方法transformedBeanName来处理我们传进来的bean名称,为什么首先要处理bean的名称呢?我们到方法transformedBeanName中看下是如何处理的:
在transformedBeanName中,首先会通过容器的工具类BeanFactoryUtils调用rransformedBeanName方法来处理,我进到方法看下:
在BeanFactoryUtils的方法transformedBeanName中其实就是做了一件事情,如果发现name以一个或多个符号&为前缀,那就剔除掉name中的&,直到name前缀中的符号&都剔除干净了才返回name。那为什要这样做呢?我们在后分析相应的逻辑时候在看,接下来我们回到上一个方法:
已经获取到了剔除前缀符号&的name之后,可以看到,现在又把返回值交给了canonicalName处理,那canonicalName方法又会对name做怎样的处理呢?我们到方法里面看下:
可以看到,在方法canonicalName中,其实就是通过参数传进来的name,到别名缓存alilasMap中获取bean的实际名称,之前我们在BeanDefinition注册到spring容器之后,也看到了别名的注册逻辑。因为在别名缓存aliasMap中,存放了bean的别名stduent1到bean的实际名称student的映射,所以,在方法canonicalName中,就可以通过别名缓存aliasMap,根据别名student1获取bean的实际名称student,这样spring在拿着bean的实际名称student,就可以从spring容器中获取到bean的BeanDefinition了。
我们接着看下一个方法:
spring三级缓存解决循环依赖
接下来会调用方法getSingleton,根据方法getSingleton的名称我们可以知道,接下来应该是要获取beanName对应的单例bean了,我们到方法里面看下:
在方法getSingleton中又调用了它的重载方法getSingleton,并且在原来的参数基础上,又添加了一个新的参数allowEarlyReference,默认值为true。那参数alloEarlyReference有什么特别的作用吗?我们到方法中看下:
可以看到,方法中的代码逻辑比较紧凑,而且我们简单观察下可以发现,方法中用到了很多的成员变量如singletonObjects、earlySingletonObjects和singletonFactories,这些成员变量其实就是各种缓存。我们知道,现在缓存中现在是没有bean的实例的,因为我们现在是第一次调用getBean方法来实例化bean,大多数情况下的bean都是在用到时,才会触发bean的实例化,但是有没有特例呢?我们前面分析ApplicationContext初始化时,如果bean中的属性lazyInit的值设置为了false,就会提取调用getBean方法触发bean的实例化,也就是提前初始化这些非延迟加载的bean,而实例化好的单例对象就会被缓存在singletonObjects中。除了这种情况,前面我们还看到了spring内部的一些组件,如消息MessageSource、事件广播器ApplicationEventMulticaster等,在spring容器ApplicationContext初始化时就会实例化好,其实他们最后也是会注册到缓存singletonObjects中的,因为这些组件是spring功能实现的基础,必须在spring器初始化时就提前实例化好。
在getSinleton中,总共用到了三个缓存,我们看下这三个缓存是什么?
这三个缓存其实就是三个Map,分别用来缓存不同的数据,我们在回到方法getSingleton方法中:
首先会从singletonObjects中,根据bean的名称beanName来获取单例bean,singletonObjects这个缓存主要是用来存放已经完成实例化好的单例bean。为什么我们要强调singletonObjects是用来存放单例bean,并且是完全实例化好的单例bean呢?我们继续往后面看:
如果singletonObject 等于空,也就是singletonObjects中没有缓存名称为beanName的单例,并且方法isSingletonCurrentInCreation返回的是true。就会进入到分支里面,我们到方法isSingletonCurrentlyInCreation中看下,这个是在判断什么?
这个就是判断集合singletonsCurrentlyInCreation中是否存在beanName。我们在后面可以看到当bean开始实例化时,spring就会把bean对应的名称放入到集合singletonsCurrentlyInCreation中,表示这个bean正在实例化,防止bean重复进行实例化。当bean完成实例化完成后就会从集合singletonsCurrentlyInCreation中将bean的名称移除掉。
我们在回到getSingleton方法中继续来看下:
根据我们刚才的分析,如果在缓存singletonObject中找不到beanName对应的单例bean,同时beanName对应的单例正在实例化,接下来就会就会从缓存earlySingletonObjects中获取单例。那earlySingletonObjects又是什么呢?其实缓存earlySingletonObjects简单来说,就是用来存放早期单例的,早期单例就是bean的实例对象还没有全部实例化完成,仅仅只是通过反射创建了一个普通的bean对象出来,bean中的很多属性都还没有来得及赋值。为了满足其他地方的需要,就匆匆放到缓存earlySingletonObjects中了,这块逻辑后面我们也会看的的,所以刚才判断单例缓存中没有现成的单例bean,但是发现了bean正在实例化,我们就在试想能不能从缓存earlySingletonObjects中,提前获取实例化到一半的bean呢?那为什么一bean都还没有完全实例化完成,就要匆匆放到缓存earlySingletonObjects中,暴露给外界使用呢?这个还得说到spring实例化bean过程中可能会出现的一个问题,也就是循环依赖。
我们先通过一个案例来了解一下什么是循环依赖:
我们创建了两个类user1和user2,然后在这两个类中分别都创建了一个成员变量,user1的成员变量依赖user2,而user2的成员变量依赖user1。我们将这两个类配置到xml文件:
User1中的属性user2是依赖User2的,而User2中的属性user1是依赖User1的,所以,当User1实例化,为了给属性user2赋值就要得到实例User2。而User2在实例化时,同时也要为属性user1实例化User1,这样的话就会陷入一个无限循环了,而这就是spring中的循环依赖问题。
Spring是如何解决这个循环依赖的问题呢?Spring是通我们看到过的三级缓存来解决循环依赖的问题。我们继续回到我们之前的地方:
我们知道earlySingletonObjects其实就是用来缓存初步创建好的单例bean,bean中的很多属性都还没有来得及设置。可以看到如果我们从缓存earlySingltetonObject中获取到的singletonObject为空,并且allowEarlyReference为true,也就是允许使用早期单例bean,当然,参数allowEarlyReference默认值,我们前面已经看到了为为true,所以条件成立。
我们再来看下getSingleton方法:
当早期单例缓存earlySingletonObjects中也还找不到单例bean时,接下来就会寻找第三个缓存也就是singletonFactories,singletonFactories就是用来创建早期单例bean的工厂缓存,可以看到我们从singletonFactories中先获取到了bena的对象工厂singletonFactory。对象工厂singletonFactory,我们可以理解为是封装了早期单例bean实例化的方法,通过调用singletonFactory的getObject方法,我们可以获取到早期单例bean,也就是初始化到一半的 bean。Spring会将创建好的早期单例bean,在放到早期单例缓存earlySingletonObjects中,因为早期单例bean现在已经获取到了,这个时候对象工厂ObjectFactory也就没有存在的意义了,就会从工厂缓存singletonFactories中移除掉,最后将得到单例singletonObject直接返回了。
接下来,我们再来详细看下Spring是如何结合这三级缓存来解决循环依赖的。
Spring是怎样结合三级缓存来解决循环依赖的呢?
在Spring容器中,普通bean的实例化过程至少分为三个步骤:1、通过反射创建一个实例bean对象 2、为创建的bean对象填充属性 3、对这个bean进行一些初始化操作
为了方便我们理解下来的内容,我们先对 bean实例化过程有个初步的印象。我们来看下Spring是如何解决setter循环依赖的,我们根据我们之前中的案例User1和User2实例化过程进行分析,我们先看一张 流程图:
在user1在实例化时,首先会创建一个早期的单例bean,这个bean目前既没有设置属性值,也没有进行任何的初始化操作,也就是一个早期的单例bean。接着,因为user1在实例化是,需要依赖User2的实例为属性值,所以User1的实例化是依赖User2的实例。
如果按照上图中的方式,User1的实例在为自己的属性赋值时,就会触发User2的实例化,而User2在实例化到填充属性的阶段时,发现自己同样的也需要依赖User1的实例,此时就会陷入循环依赖的死循环里面了。
那有没有一种方式,既能让user1在实例时获取到User2的实例,同时User2又还没有开始为它的属性赋值,只要User2还没有为属性赋值,就不会出现循环依赖的问题了,User1不就可以先拿着User2的早期实例bean,先完成实例化。
当User2回过头来实例化为自己的属性赋值时,发现自己想要User1的实例,这个时候User1已经实例化完了,这样的话User2进行实例,就可以拿到User1的实例来完成属性值的赋值,循环依赖的问题不就决绝了吗?我们看下下面的流程
首先User2会把刚刚通过反射初步实例化好的早期单例bean,如上图先封装到对象工厂ObjectFactory中,并添加到单例工厂缓存singletonFactories里,这样的话,就相当于Spring把User2初步实例化的单例bean暴露到外界了。
User1此时如果要为属性赋值的话,就可以从工厂缓存中获取到User2的早期单例bean,完成属性赋值,进而完成User1的bean实例化,而User2的bean继续实例化,发现自己为属性赋值时也需要依赖User1的bean,这个时候User1的bean已经实例化好了,所以User2的实例化也可以完成了。
User1是如何从单例工厂缓存SingletonFactories中,获取User2的早期 实例的呢?如图:
User1的实例在实例化时,当User1的实例要为属性赋值时,就会通过工厂缓存singletonFactories,获取到User2的早期单例的对象工厂ObjectFactory,然后通过ObjectFactory的getObject方法获取到User2的早期的单例bean。
因为ObjectFactory存在的意义,就是在我们需要用到到期单例缓存bean时,才会去获取到早期单例bean,此时User2的对象工厂ObjectFactory,就可以从工厂缓存singletonFactories中移除掉了。
而当User1的实例正常实例化完成之后,User2的实例化也会继续实例化,最后把完全实例化好的完整单例bean放到单例缓存singletonObejects中,此时,完整的单例bean就已经得到了,那就没早期单例bean什么事情了,最后会把早期单例bean从早期单例缓存earlySingletonObject中给删除掉。
需要注意的是,以上只是Spring解决Setter注入循环依赖的方法,而构造方法循环依赖的问题Spring是没有办法解决的,原因也很简单的。因为Spring设计的这套三级缓存,就是通过提前暴露bean的早期单例bean,来解决setter注入的循环依赖,但是构造方法的循环依赖问题,是在反射创建bean时就会发生,此时Spring是没有办法提前获取早期单例bean的,因为早期单例bean要经过反射创建才能获取到。所以,对于构造器方法循环依赖,Spring是无法解决的。
我们在来看下前面的参数allowEarlyReference的含义,我们来看下代码:
可以看到,如果allowEarlyReference的值为false,也就是不允许引用早期的单例bean。如果参数为false的话,就无法通过工厂缓存singletonFactories获取到早期的单例bean,setter循环依赖就无法解决了。
- Spring中的bean的作用域默认都是单例的,单例对象是要求全局唯一的,所以单例缓存singletonobjects的作用很简单,就是用来存放Spring容器全局唯一的单例bean。
- Spring的earlySingletonObjects其实就是在bean还没有实例化完成时,临时用来存放早期单例的bean,因为这样的话,当前bean的实例化都需要依赖另外一个bean实例时,早期单例缓存earlySingletonObjects就可以为其他的bean的实例化提供依赖的bean实例了。
- 第三级缓存的作用,主要还是提供一个懒加载的选择,毕竟通过ObjectFactory方法获取到早期的单例bean时,里面会有一些方法逻辑处理,所以,ObjectFactory存放缓存singletonFactories中的一个好处就是,可以让你在用到早期单例缓存时,才会调用ObjectFactor的方法来获取bean。
我们通过一张流程图总结下今天的内容: