Spring框架XML启动过程详解(三):加载Bean前后过程

在Spring框架中我们是这样获取Bean实例的,本文就这段代码进行一个详细的解析,了解在Spring框架中BeanDefinition是如何被加载为一个可使用的bean实例的。

TestBean bean = (TestBean)beanFactory.getBean("testBean");

1.代码位置

在经过一系列的getBean->doGetBean之后,代码的具体位置如下:org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean,在这个方法中,经过一系列的解析,成功获取到了bean对象,由于代码太长,这里只在必要时贴出关键代码。

2.从缓存中获取

方法的开始部分,通过传入参数name从缓存的bean名称或别名中获取到beanName,然后会先调用getSingleton方法在缓存中寻找bean是否已经创建好,如果创建好的话直接将初始的bean实例进行包装,即调用getObjectForBeanInstance方法,这个方法比较重要,后面会单独讲,另外从缓存中获取bean也是一个比较复杂的过程,涉及到循环依赖的问题,之后会单独开一个文章进行讲解,这里仅简单提一嘴,正常情况下第一次进入时缓存中是没有的,我们已缓存中没有的这种情况为基准进行解析。

3.bean加载的整体过程

如果缓存中不存在已经加载的单例,就需要从头开始bean的加载过程,如下图所示,一共分为以下三步,获取RootBeanDefinition对象,单例的实例化bean以及从bean的实例中获取对象。接下来将详细对这三个步骤进行解析。

 4.获取RootBeanDefinition对象

RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
checkMergedBeanDefinition(mbd, beanName, args);

因为从XML配置文件中读取到的bean信息是存储在GernericBeanDefinition中的,但它不适合进行后续的处理,RootBeanDefinition中用于比它更强大的功能例如缓存、工厂方法、泛型等等,因此需要将GernericBeanDefinition转换为RootBeanDefinition。那为什么不在一开始XML解析时就使用RootBeanDefinition呢?其实GernericBeanDefinition也有它的优点,它自2.5版本之后加入作为通用的bean实现,是ChildBeanDefinition和RootBeanDefinition的替代者,它实现了parentName属性的get和set方法,而RootBeanDefinition却不提供继承相关的操作,因此bean的一般形式是以GenericBeanDefinition存在的,在特定的时机会将它转成RootBeanDefinition。那么既然RootBeanDefinition无法指定父类,那某个bean的父类如何体现呢,这就体现了Spring的思想,它会首先初始化父类的RootBeanDefinition,然后根据子类的GernericBeanDefinition覆盖父类中的相应属性,最终获取子类RootBeanDefinition。

解释完了为什么要转换,我们来看一下实际的代码是如何转换的,其实就是根据beanName从缓存中获取到GenericBeanDefinition,然后在AbstractBeanDefinition有一个构造方法可以将任何类型的BeanDefinition进行转化,而RootBeanDefinition继承了这个构造方法,在转化完成之后还会对其进行一个校验,看是否转化成功。

5.bean的初始化

//1.8新版
sharedInstance = getSingleton(beanName, () -> {
						try {
							return createBean(beanName, mbd, args);
						}
						catch (BeansException ex) {
							destroySingleton(beanName);
							throw ex;
						}
					});

//老版
sharedInstance = getSingleton(beanName, new ObjectFactory<>(Object){
						public Object getObject()throws BeansException{
                            try {
							    return createBean(beanName, mbd, args);
						    }
						    catch (BeansException ex) {
							    destroySingleton(beanName);
							    throw ex;
						    }
					    }
                });

在看源码时可能大家会有疑惑,getSingleton方法中的这个singletonFactory这个变量是从哪里来的,其实这里是用了一个1.8的新特性,上面我为大家展示了新老版本的区别,看老版可能更加明白。这里有两个方法getSingleton和createBean,其中createBean方法是真正的创建的过程,但由于太过于复杂,将其放在下一篇文章中详细介绍,我们这里仅介绍bean的初始化过程。

 在getSingleton方法中主要做了以下几件事:

  1. 检查缓存是否已经加载过
  2. 若没加载则记录beanName的正在加载状态
  3. 加载单例前记录加载状态
  4. 通过入参singletonFactory的getObject方法实例化bean
  5. 加载单例后的处理方法调用
  6. 将结果记录至缓存并删除加载bean过程中记录的各种辅助状态
  7. 返回处理结果

前三步主要涉及到一个缓存的操作,和加载状态的记录,这样做是为了防止出现循环依赖问题,记录的加载状态会在加载完成之后进行删除,即第六步的操作。从上述步骤可以看出重点在第四步,这里就涉及到singletonFactory这个入参了。可以看到它的类型是ObjectFactory,这个接口里面只有一个方法getObject(),在调用getSingleton方法时通过一个匿名内部类将其创建出来,这里采用的是工厂模式。和他有同样作用的还有FactoryBean接口,同样也是通过工厂模式来获取bean,两者的不同在于ObjectFactory生成实例对象可部分或完全自主不受容器管控,而FactoryBean完全自主容器会提供信息。

6.从Bean的实例中获取对象

bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);

这是bean加载的最后一步处理,同时这个方法在getBean中也高频的出现,无论是从缓存中获取还是新创建的bean都需要经过这个方法,因为此时的bean还是最原始的状态,并不一定是我们最终想要的,它可能是一个factoryBean,因此需要通过这个方法就是最后的解析。这个方法主要做了以下几件事:

  1. 对FactoryBean正确性的验证
  2. 对非FactoryBean不做任何处理
  3. 对bean进行转换
  4. 将从Factory中解析bean的工作委托给getObjectFromFactoryBean

可以看到最终要的工作交给了getObjectFromFactoryBean方法,而这个方法主要保证了单例bean的全局唯一,并且调用factoryBean的getObject方法获取到bean对象,在进行后置处理之后返回。

总结

在bean的加载前后做了很多的事情,如果缓存中已经加载过则直接返回,如果没有则需要经过一系列的步骤来获取到bean实例,再经过初始化后并且通过验证才能真正的使用。当然最重要的bean的加载过程还没有开始,下一篇文章中将详细介绍bean是如何加载的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值