Bean加载的总体流程
以这行代码为起点,探究这背后到底经历了什么?
1 | MyTestBean bean=(MyTestBean)bf.getBean("MyTestBean"); |
- 调用
getBean("MyTestBean")背后实际是调用了doGetBean(name,null,null,false)doGetBean方法。这个方法干的事情有些多,但总结起来就这们几步:
- 通过传入的的name参数,提取beanName属性.这个地方有可能会有疑问,传入的name不就是bean的Name吗。其实不然,因为spring还允许有别名。
- 尝试使用直接从缓存获取,或者sinletionFactories中的ObjectFactory中获取,具体的也就是在
Object sharedInstance = getSingleton(beanName);代码中。这里的缓存其实就是指DefaultSingletonBeanRegistry类中的singletonObjects:Map。这个缓存就是一个ConcurrentHashMap其中键为BeanName,值就是对应的Bean。
具体的代码实现如下:
1 | /** |
回到刚才的doGetBean方法,通过getSingleton方法的一番努力,如果拿到了bean,现在我们可以返回实例了。
这一步是由bean = getObjectForBeanInstance(sharedInstance, name, beanName, null)来实现
但值得注意的是,有些存在beanFactory的时候,spring并不是返回的实例本身,而是返回指定方法返回的实例。这个地方有点绕,其实很好理解,因为spring创建bean本身就有两种创建方法(大分类),一种是利用构造器,一种是利用工厂方法(又可分为静态工厂和实例工厂)。如果是利用工厂方法,我们当然不能直接返回工厂对象咯,而是应该返回该工厂对象中getObejct()方法返回的对象。
- 如果没有拿到bean,那
doGetBean还要经过一番努力。如果已经加载的类中不包括beanName(已经加载的类存在beanDefinitionMap中,值得注意的BeanDefintion对应Bean的关系,就像Class对应Object的关系一样)。那么就尝试从parentBeanFactory中获取BeanFactory,如果没找到就递归到BeanFactory中去找。 - 如果不仅仅做类型检测而是需要创建bean,就将该beanName记录为正在创建的状态。这个一步由这段代码实现:
1 | if (!typeCheckOnly) { |
- 进行属性的准备,如果是子bean的话,需要合并父类的相关属性,这个也很好理解,如果一个类有继承的父类的时候,它本身包含的属性是不完整的,它还有一部分属性在它的父类中体现。这一步主要由这段代码实现:
1 | final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName); |
- 如果存在依赖,这需要递归实例化依赖的bean。这里涉及一个不太常见的用法
depend-on。这一步主要由这段代码实现:
1 | String[] dependsOn = mbd.getDependsOn(); |
- 创建了bean之后,就可以实例化mbd本身了。这个时候,又分为了两种情况,一种是单例的创建,一种是原型的创建。
- 类型的转换
创建Bean
createBean函数负责创建bean。这个函数在doGetBean方法中调用过。现在进入这个方法内部,查看其具体实现。
首先这个方法的声明是这样的:
1 | protected Object createBean(String beanName, RootBeanDefinition mbd, Object[] args) |
它主要干了这些事情:
- 调用
Class<?> resolvedClass = resolveBeanClass(mbd, beanName);,根据设置的class属性和className来解析Class。这个方法的内部没有什么特别的。并且利用该方法得到的resolvedClass来初始化mbdToUse:RootBeanDefinition - 验证准备覆盖的方法
mbdToUse.prepareMethodOverrides();.说人话,就是对override属性进行标记及验证。 - 通过调用
Object bean = resolveBeforeInstantiation(beanName, mbdToUse);给bean后置处理器一个通过代理类替换目标类的机会。这个地方需要注意。 - 调用
Object beanInstance = doCreateBean(beanName, mbdToUse, args);来进行Bean的创建
循环依赖
什么是循环依赖?
循环依赖就是循环引用。就是两个或多个bean相互之间持有对方。比如beanA中引用了beanB,而beanB中引用了beanA。那么这两个bean之间就形成了循环依赖。**值得注意的是循环依赖不等于循环调用。**循环调用是方法调用之间成环。循环调用是无解的。
spring是如何解决循环依赖的?
要回答回答这个问题,首先要清楚,spring中bean有哪几种循环依赖,哪些循环依赖是可以解决的。
循环依赖的分类
- 构造器循环依赖
这种依赖的成因是因为构造器注入形成的循环依赖。这种依赖是无解的。因此,我们不太建议使用构造器注入。至于为什么无法解决构造器循环依赖,原因其实非常的简单。举例:假如beanA在调用构造器的时候,发现需要beanB作为参数,然后去调用beanB的构造器,发现又需要beanA作为参数,这种情况根本没得搞,死循环了。 - setter循环依赖
表示通过setter注入方式构成的循环依赖。setter循环依赖在单例作用域下是可以解决的。spring通过提前暴露完成实例化的bean来解决setter循环依赖。举个栗子:beanA实例化后,进行属性赋值的时候,发现它需要通过setter注入beanB,然后他就去实例化beanB,beanB实例化之后,在beanB属性赋值的时候,发现需要注入beanA,这个时候因为beanA已经提前暴露了(它现在还没完整的创建),beanB发现了beanA,然后直接把beanA注入即可,这个时候beanB的属性赋值阶段,已经完成了,这个时候我们再看beanA,这个时候直接把beanB注入beanA即可了,这样循环依赖就解决了。
在解决循环依赖的过程中,很关键的两点是:哪些bean正在创建需要透明;bean实例化之后,就可暴露出来,供其它bean注入。
- prototype范围的依赖处理
prototype范围的循环依赖也是无解的,因为spring不会缓存prototype范围的对象,因此无法实现提前暴露。
常规的bean的创建
总体的流程
所谓的常规的,就是指在createBean中调用了Object bean = resolveBeforeInstantiation(beanName, mbdToUse);后,并没有创建代理,就调用doCreateBean。该方法进行的就是常规的bean的创建。
这个类主要做了这些事情:
- 如果是单例则首先清除缓存
1 | if (mbd.isSingleton()) { |
- 根据bean所指定的策略创建实例,将beanDefinition转换为BeanWrapper,这个方法的内部实现其实比较复杂。如果存在工厂方法,就使用工厂方法进行初始化;如果有多个构造器,则根据参数匹配构造器来初始化;如果没有工厂方法,也不存在带参数的构造器,则使用默认构造方法进行实例化。
1 | instanceWrapper = createBeanInstance(beanName, mbd, args); |
- 判断是否需要提前曝光,根据这些条件来进行判断:单例&允许循环依赖&当前bean正在创建过程中。当需要提前曝光时,在bean创建完成之前将创建实例的ObjectFactory加入工厂
1 | boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && |
- 属性填充,也就是在这个地方,就可能出现循环依赖,如果存在依赖其它bean的属性,则会递归初始化bean
1 | populateBean(beanName, mbd, instanceWrapper); |
- 调用初始化方法
1 | exposedObject = initializeBean(beanName, exposedObject, mbd); |
- 检测依赖
1 | removeSingletonIfCreatedForTypeCheckOnly(dependentBean)for (String dependentBean : dependentBeans) { |
removeSingletonIfCreatedForTypeCheckOnly(dependentBean)方法所做的事情就是去一个alreadyCreated:Map中看,该bean所依赖的bean,是否已经被创建了,如果被创建了,就将该bean从三级缓存和registeredSingletons:map中删除,并返回true。如果所依赖的bean已经被创建,就将该bean加入actualDependentBeans:Set.完成对所有的依赖的bean的检测之后,如果actualDependentBeans:Set不为空,就抛出循环依赖异常(这个地方值得注意的是,如果一个bean创建成功了,那么它所依赖的bean也一定是创建了的,如果它所依赖的bean没有被创建,这就说明存在循环依赖)
- 根据scope注册bean
所谓的注册就是将bean加入一个disposableBeans:map中去。便于在销毁时使用。
1 | registerDisposableBeanIfNecessary(beanName, bean, mbd); |
- 完成创建并返回
完成bean实例化的createBeanInstance方法
instanceWrapper = createBeanInstance(beanName, mbd, args);这个方法主要完成了bean的实例化。这也是bean生命周期的第一步。
这个方法主要做以下几件事情:
- 拿到beanName对应的class
1 | Class<?> beanClass = resolveBeanClass(mbd, beanName); |
像class这些信息在spring初始化的使用就已经拿到了。这个地方只是取出来就可以了。
2. 如果有相应的工厂方法,就使用工厂方法创建bean
1 | if (mbd.getFactoryMethodName() != null) { |
spring创建bean无非就是两种方案,一个是利用工厂方法,一个是利用构造器。如果没有对应的工厂方法,那就只好使用构造器来实例化。
3. 没有对应的工厂方法,就使用构造器来实例化。先锁定构造方法
1 | synchronized (mbd.constructorArgumentLock) { |
因为一个类可能有多个构造方法,所以这个地方需要根据参数来确定调用哪个构造函数或工厂方法。因为这个操作是比较费时的所以spring采用了缓存的机制。
- 经过解析,调用相应的构造方法进行初始化
1 | //如果已经解析过勒,则使用解析好的构造函数方法,而无需再次锁定 |
- 如果需要根据参数来解析构造函数,就执行以下几步
1 | // Candidate constructors for autowiring? |
带参数的实例化,其实非常的复杂,因为它充满了不确定性。
容器功能的拓展
ApplicationContext对BeanFactory进行了拓展。ApplicationContext包含了BeanFactory的所有功能。
从这行代码,为起点来看它背后的东西:
1 | ApplicationContext bf=new ClassPathXmlApplicationContext("beanFactory.xml"); |
- 首先它调用了一个有参构造器
1 | public ClassPathXmlApplicationContext(String configLocation) throws BeansException { |
它又调用
1 | public ClassPathXmlApplicationContext( |
- 设置了配置文件的路径,之后就可以进行配置文件的解析和各种功能的实现了。这些东西都是在
refresh方法中实现了。
完整的代码如下:
1 | public void refresh() throws BeansException, IllegalStateException { |
它主要做了这些事情:
- 初始化前的准备工作。例如对系统属性或者环境变量进行准备和验证
- 初始化beanFactory并进行xml文件的读取。因为ApplicationContext包含了BeanFactory所提供的一切特征,在这一步就是利用BeanFactroy中的xml文件读取的相关功能。也就是从这一步开始ApplicationContext已经包含了一个BeanFactory了。这段代码可以佐证:
1 | protected ConfigurableListableBeanFactory obtainFreshBeanFactory() { |
- 对BeanFactory进行各种功能填充,常见的
@Autowired就是在这一步增加支持的。 - 子类覆盖方法做额外的处理。通过这个拓展点,可以方便程序员来拓展spring的功能
- 调用各种beanFactoty处理器
- 注册拦截bean创建的bean处理器,这一步并没有调用,真正调用的是在getBean的时候
- 为上下文初始化message源,对不同语言的消息题进行国际化处理。
- 初始化应用消息广播器。
- 留给子类来初始化其它的bean
- 在所有的注册的bean中查找 listener bean,注册到消息广播器中。
- 初始化涉嫌的单实例
- 完成刷新过程
重要步骤细节分析
1. 环境准备
环境准备调用的是protected void prepareRefresh()方法,该方法的内部实现,有行关键的代码:
1 | //留给子类覆盖的,这个方法内部是空的 |
其实这两行代码基本没做什么实际的事情,包括第二行代码,因为实际上因为没有什么需要验证的属性,所以第二行代码也没有做什么实际的事情。这两行的代码的意义在于,提供了拓展点。通过重写initPropertySources()来实现个性化的属性处理,之后再利用validateRequiredProperties对属性进行验证。
2. 加载BeanFactory
获取BeanFactory,这个工作由这行代码实现:
1 | ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); |
也正是从这个地方开始,ApplicationContext有了BeanFactory的全部功能。obtainFreshBeanFactory()方法中调用了refreshBeanFactory()方法,由这个方法来完成了BeanFactory的初始化工作。
这个方法完成的首要工作就是创建beanFactory:DefaultListableBeanFactory,这个对象非常的重要,它整个IOC容器的关键。之后是指定序列化ID,定制BeanFactory,加载BeanDefinition,最后使用全局变量记录BeanFactory实例。
3. 加载beanDefinition
加载beanDefeinition的第一步就是初始化用于读取配置文件的beanDefinitionReader:XmlBeanDefinitionReader.创建成功之后,对beanDefinitionReader做环境变量的设置之后,就可以使用loadBeanDefinitions(beanDefinitionReader)读取并解析spring配置文件,来加载BeanDefinitinon。
4. 功能拓展
spring在prepareBeanFactory(ConfigurableListableBeanFactory beanFactory),方法中完成了ApplicationContext的功能的拓展。
主要是添加了以下几个方面的拓展:
- 增加对spEL语言的支持
- 证件对属性编辑器的支持
- 增加对一些内置类的信息注入。
- 设置了依赖功能可能忽略的接口
- 注册了一些固定依赖的属性
- 增加了AspectJ的支持
- 将相关环境变量及属性注册以单例模式注册
5. BeanFactory的后处理
BeanFactory作为spring中容器功能的基础,用于存放所有已经加载的bean,为了保证程序的高可拓展性,Spring针对BeanFactory做了大量的拓展。PostProcessor等都是在这个地方实现的。
后处理主要做了这些事情:
- 激活BeanFactoryPostProcessor
invokeBeanFactoryPostProcessors(beanFactory);调用了所有的后置处理器,其中首先调用所有的硬编码配置的后置处理器,其后调用了配置注册的后置处理器。在执行的时候,它会按照配置中order属性来按一定的顺序来执行那些后置处理器。spring通过三个list来记录这些后置处理器。它们分别是: registryPostProcessors记录通过硬编码方式注册的BeanDefinitionRegistryPostProcessor类型的处理器regularPostProcessors记录通过硬编码方式注册的BeanFactoryPostProcessor类型的处理器registryPostProcessorBeans记录通过配置方式注册的BeanDefinitonRegistryPostProcessor类型的处理器。
6. 注册BeanPostProcessor
实现了对BeanFactoryPostProcessor的调用之后,就要进行BeanPostProcessor的注册了。spring中许多的功能都是通过后处理器的方式来拓展的。BeanPostProcessor的处理与BeanFactoryPostProcessor的处理几位相似。在注册之前需要根据 PriorityOrdered进行排序,根据Ordered进行排序或者无序。
7. 初始化消息资源
初始化消息资源是为了进行国际化处理。在确定特定类型的本地化信息的时候,,需要两个条件,一个是“语言条件”,另一个是“国家/地区的类型”。spring定义了国际化信息的MessageSource接口。
8. 初始化ApplicationEventMulticaster
首先来展示一下Spring事件监听器的简单用法:
1.定义监听事件
1 | public classs TestEvent extends ApplicationEvent{ |
- 定义监听器
1 | public class TestListener implements ApplicationListener{ |
- 编写配置文件
1 | <bean id="testListener" class="com.test.event.TestListener"/> |
- 测试
1 | public class Test{ |
控制台输出:
1 | msg |
具体的操作由initApplicationEventMulticaster();来实现,它无非就处理了两种情况一种是:如果用户自定义了事件广播器,那么使用用户自定义的事件广播器。第二种情况:如果用户没有自定义事件广播器,那么使用默认的ApllicationEventMulticaster。
这个地方运用了设计模式的观察者模式.
当spring事件产生的时候会默认使用SimpleApplicationEventMulticaster的multicastEvent来广播事件,Spring会遍历所有的监听器,并使用监听器的onApplicationEvent方法来进行监听器的处理。因此其实每个监听器都可以收到事件,是否进行处理则由监听器来决定。
9. 注册监听器
注册监听器由registerListeners();
1 | protected void registerListeners() { |
10. 初始化非延时加载的单例
其中包括ConversionService的配置,配置冻结以及非延迟加载的bean的初始化工作。所谓的冻结配置就是注册的bean的定义将不被修改或进行任何进一步的处理。ApplicationContext实现的默认行为就是在启动时将所有单例bean提前进行实例化。