从顶层视图看:
本质上的工作原理就是:把应用程序的类和配置元数据组装起来,以便在ApplicationContext创建并初始化好之后,IoC容器直接为你提供了一个已经配置好并且可执行的系统或应用。
1、IoC容器系列的设计与实现:BeanFactory和ApplicationContext
1.1、IoC容器接口设计图
其中的BeanFactory定义了基本的IoC容器的规范
1.2、BeanFactory容器设计原理
以XmlBeanFactory的实现为例来说明简单IoC容器的设计原理:
- BeanFactory实现是IoC容器的基本形式,各种ApplicationContext的实现是IoC容器的高级表现形式。
- DefaultListableBeanFactory作为一个默认的功能完整的IoC容器来使用;
- Resource是Spring用来封装I/O操作的类;
1.2.1、编程式使用IoC容器
1 | ClassPathResource res = new ClassPathResource("spring.xml"); |
1.3、ApplicationContext
1.3.1、应用场景
ApplicationContext是一个高级形态意义的IoC容器:
- 支持不同的信息源:继承接口Messagesource;
- 访问资源:继承接口ResourceLoader;
- 支持应用事件
- 在ApplicationContext中提供附加功能;
1.3.2、设计原理
以FileSystemXmlApplicationContext为例子来说明ApplicationContext的设计原理。
AbstractRefreshableApplicationContext
ApplicationContext的主要功能在基类AbstractRefreshableApplicationContext
中实现了,来看看FileSystemXmlApplicationContext
的构造方法:
1 | public FileSystemXmlApplicationContext( |
ConfigurableApplicationContext
其中的refresh()
方法定义在ConfigurableApplicationContext
接口中。
ConfigurableApplicationContext
接口主要封装配置和生命周期相关方法,这些方法几乎只用在容器启动和关闭的代码中。
ApplicationContext
根据上面的UML图可以知道,ConfigurableApplicationContext
的父接口ApplicationContext
:
- 继承ListableBeanFactory用于提供访问应用组件的方法;
- 继承ResourceLoader用于提供加载资源文件的方法;
- 继承ApplicationEventPublisher用于提供发布事件与注册监听器的方法;
- 继承MessageSource提供国际化的支持;
AbstractApplicationContext
其中refresh()
方法的具体实现在AbstractApplicationContext抽象类中,这个refresh()
方法会涉及到IoC容器启动的一系列负载操作,针对不同的容器实现,这些操作都是类似的。
不同的ApplicationContext对应不同的BeanDefinition读取方式,而FileSystemXmlApplicationContext
类中提供类一个这样的方法实现:
1 |
|
调用这个方法,可以获取到FileSystemResource。
2、IoC容器的初始化
其中IoC容器的初始化过程,主要就体现在AbstractApplicationContext
的refresh()
方法中,下面是该方法关键代码:
1 |
|
各个方法功能说明如下:
2.1、为刷新流程准备好context
设置启动日期,记录容器标识位,初始化容器变量。
2.2、加载bean definitions配置并解析注册到BeanFactory中
这里调用具体的子类refreshBeanFactory()方法实现来刷新内部的BeanFactory。
这一步是Bean定义信息载入的环节。最开始会创建一个DefaultListableBeanFactory,保存在ApplicationContext的beanFactory中。这一步的主要的方法是:loadBeanDefinitions()
,下面来看看AbstractXMLApplicationContext中该方法的调用层次:
loadBeanDefinitions
方法中创建了一个XmlBeanDefinitionReader
来读取xml的配置并解析为bean definitions:
1 |
|
其中loadBeanDefinitions最终会调用到DefaultListableBeanFactory.registerBeanDefinition()
方法注册bean definitions。所谓的注册,就是把解析到的BeanDefinition放入到DefaultListableBeanFactory中定义的一个beanDefinitionMap中。调用关系如下:
容器的作用就是对beanDefinitionMap里面的信息进行处理和维护。有了这些数据,就可以进行依赖注入了
2.3、准备context中的BeanFactory
在使用ApplicationContext时需要做一些准备工作,这些准备工作是在这一步处理的,包括:为容器配置ClassLoader、PropertyEditor和BeanPost-Processor等,从而为容器的启动做好必要的准备。
2.4、允许context的子类对BeanFactory进行后处理
postProcessBeanFactory后处理beanFactory。该方法是在所有的beanDenifition加载完成之后,bean实例化之前执行。为了能够修改bean definitions,或者对BeanFactory做一些其他配置,可以使用这个方法。如可以实现ClassPathXmlApplicationContext类并重新该方法即可。Spring中很多ApplicationContext的子类都是通过重写这个方法来达到这个目的(AbstractApplicationContext中的实现为空)。
spring4.2.9 java项目环境下ioc源码分析 (九)——refresh之postProcessBeanFactory方法
2.5、调用BeanFactory的后置处理器,这些处理器也是context中的beans
拿到当前应用上下文 beanFactoryPostProcessors 变量中的值,实例化并调用所有已注册的 BeanFactoryPostProcessor。必须在singleton之前调用。
2.6、注册Bean的后置处理器,这些处理器在 bean创建的过程中被调用
实例化并且注册所有的BeanPostProcessor beans
,必须在任何bean实例化之前调用。
2.7、对上下文中的消息源进行初始化
初始化消息源,支持消息的参数化和国际化。
2.8、初始化context中的事件机制
初始化应用事件广播器,这里使用了事件驱动机制。如果自定义了广播器,就用自对应的,否则用默认的。然后把该广播器设置到context的applicationEventMulticaster属性中。
2.9、初始化其他特殊的Context中的特殊的bean
特殊的context子类中初始化其他特殊的bean,使得子类在实例化单例之前,调用初始化bean,默认是空实现。
2.10、检查监听器,并且向容器注册它们
注册监听器,在发布事件的时候会从这里注册的监听器中获取。
2.11、实例化剩下的所有的非懒加载的单例
这里的代码比较多,我们来看看关键代码:
1 | List<String> beanNames = new ArrayList<>(this.beanDefinitionNames); |
这里会调用getBean(beanName)
触发bean的实例化。
如果bean实现了SmartInitializingSingleton接口,则会执行该接口的afterSingletonsInstantiated方法,该方法是在所有的 单例bean创建完成之后(依赖注入完成,BeanPostProcessor,InitializingBean, initMethod相关方法执行完成后)触发执行,对于懒加载的单例bean无效。
这个接口的功能类似于ContextRefreshedEvent,都是在容器加载完成之后处理其他的事情,但是无需实现ApplicationListener,使用更佳简单。
2.12、最后一步:发布容器事件,结束Refresh过程
完成容器刷新,调用LifecycleProcessor的onRefresh方法,发布ContextRefreshedEvent事件。
关于LifecycleProcessor
负责管理ApplicationContext的生命周期。
3、IoC容器的依赖注入
在没有配置lazy-init=false的情况下,依赖注入的过程是用户第一次向IoC容器索要Bean的时候出发的。具体的触发方法:
BeanFactory.getBean(String name)
来看看这个方法的调用关系:
可以发现,最终是由createBean方法执行。createBean方法生成需要的bean,并且对Bean初始化进行了处理(init-method或者Bean后置处理器等)。
其中createBean方法的RootBeanDefinition对象是在AbstractBeanFactory的doGetBean方法里面生成的:
1 | final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName); |
如果需要获取的bean是一个子bean定义,那么会遍历父bean的定义,返回一个合并RootBeanDefinition。这个方法会递归一直往上寻找父bean,构造包含继承关系的RootBeanDefinition:
1
2
3
4
5 pbd = getMergedBeanDefinition(parentBeanName);
...
mbd = new RootBeanDefinition(pbd);
// 使用bd(可能是子bean)覆盖mbd(可能是父bean的副本),这样就把父bean和子bean的属性配置合并在一起了。
mbd.overrideFrom(bd);
方法中的RootBeanDefinition
,如2.7、Bean定义继承
,指的是父定义。在配置文件中可以定义父<bean>
和子<bean>
,父<bean>
用RootBeanDefinition表示,而子<bean>
用ChildBeanDefiniton表示,而没有父<bean>
的<bean>
就使用RootBeanDefinition表示。see Spring中BeanDefinition的继承体系。
3.1、在createBean这个方法包含以下方法调用:
Bean实例化
调用关系如下
以下是instantiate方法:
1 |
|
可以发现,如果是接口的实现,那么就会使用CGLIB来实例化Bean,否则使用BeanUtils中的JVM反射来实例化Bean。生成的实例最终会被封装为BeanWrapper,BeanWrapper相当于一个代理器,Spring委托BeanWrapperwancehngBean属性的填充工作。在Bean实例被InstantiatioonStrategy创建出来之后,容器主控程序将Bean实例通过BeanWrapper包装起来,这是通过调用BeanWrapper#setWrappedInstance(Object obj)方法完成的。see Spring容器技术内幕之BeanWrapper类介绍
Bean的初始化,依赖注入一般发生在这里
createBean中调用了populateBean方法,该方法进行了依赖注入处理,主要通过bean definition中的属性值填充BeanWrapper中的bean实例,以下是关键处理代码:
1 | protected void populateBean(String beanName, RootBeanDefinition mbd, { BeanWrapper bw) |
其中最后一个方法调用applyPropertyValues是属性注入的处理方法:
1 | protected void applyPropertyValues(String beanName, BeanDefinition mbd, BeanWrapper bw, PropertyValues pvs) { |
这里通过BeanDefinitionValueResolver来对BeanDefinition进行解析,然后注入到PropertyValues中。
主要的处理逻辑是为解析值创建一个副本:
1 | List<PropertyValue> deepCopy = new ArrayList<>(original.size()); |
副本的数据最后会被注入到Bean中:
1 | bw.setPropertyValues(new MutablePropertyValues(deepCopy)); |
其中resolveValueIfNecessary方法主要的作用是:
解析PropertyValue中对工厂中其他bean的任何引用。该值可以是:
- 一个BeanDefinition,它导致创建相应的新bean实例;
- 一个RuntimeBeanReference;
- 一个ManagedList。 这是一个特殊的集合,其中可能包含需要解析的RuntimeBeanReferences或Collections;
- 一个ManagedSet。 也可能包含需要解决的RuntimeBeanReferences或Collections;
- 一个ManagedMap。 在这种情况下,该值可能是需要解析的RuntimeBeanReference或Collection;
- 一个普通对象或
null
,在这种情况下,将不做解析。
而最后的setPropertyValues方法会为BeanWraper完成Bean的属性值注入。
总的来说,以getBean为入口,触发Bean的创建和依赖注入,其间需要依赖BeanDefinition中的信息来递归地完成依赖注入。