本文从源码的角度解析spring ioc的实现原理。
Spring容器可以分为两类:
- BeanFactory
- ApplicationContext
通常我们讲到Spring容器,一般是指ApplicationContext,因为通过上图我们可以看到ApplicationContext实际上也是BeanFactory下面的一个类。
创建BeanFactory及Bean的注册
Spring里建立一个ApplicationContext是通过以下一行语句进行:
1 | ApplicationContext context=new ClassPathXmlApplicationContext("classpath:application.xml"); |
简单的理解就是通过读取配置文件返回一个Spring容器ApplicationContext,那么具体的过程肯定是要通过分析ClassPathXmlApplicationContext的构造器开始。可以看到设置了配置文件地址后调用的refresh就是我们需要的核心方法。
1 | public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, @Nullable ApplicationContext parent) throws BeansException { |
refresh()方法是在一个上层类AbstractApplicationContext中定义的,它的行为比较多,我们可以重点关注这一行
ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
代码,返回创建了一个beanFactory,这个实际上就是一个Bean工厂,它的注册中心里包含了从配置文件中解析出来的BeanDefinition,也就是Bean对象,注册中心是一个concurrentHashMap<beanName, Object>。只是这些bean还没有初始化。
//ClassPathXmlApplicationContext.java
1 | public void refresh() throws BeansException, IllegalStateException { |
下面我们的就refresh方法中的核心流程进行分析。
prepareRefresh()
负责创建bean之前的准备工作,记录下容器的启动时间、标记“已启动”状态、处理配置文件中的占位符等。
接下来是比较重要的obtainFreshBeanFactory()
方法,这里会创建BeanFactory,加载Bean并注册Bean。
//AbstractApplicationContext.java
1 | protected ConfigurableListableBeanFactory obtainFreshBeanFactory() { |
//AbstractApplicationContext的子类AbstractRefreshableApplicationContext.java
1 | protected final void refreshBeanFactory() throws BeansException { |
通过图1可以看到在BeanFactory的继承树最下面的一个类就是DefaultListableBeanFactory
,虽然在继承体系中,ApplicationContext继承自BeanFactory,但它不能被理解为BeanFactory的实现类,而是它内部持有一个BeanFactory的实例(DefaultListableBeanFactory),以后所有的与BeanFactory有关的操作都是委托给DefaultListableBeanFactory实例来做的。
可以看到,ConfigurableListableBeanFactory继承了BeanFactory下面的三个子类,而它唯一的一个实现类就是DefaultListableBeanFactory
,而且DefaultListableBeanFactory
还从右边继承了AbstractAutowireCapableBeanFactory,这是用来自动装配(Autowire)bean实例的类,所以可以说,DefaultListableBeanFactory
这个BeanFactory可以做到最多的事,因此实例化它作为bean工厂。
创建了BeanFactory实例后接下来看refreshBeanFactory里的这两个方法
this.customizeBeanFactory(beanFactory);
this.loadBeanDefinitions(beanFactory);
customizeBeanFactory方法比较简单,设置是否允许覆盖BeanDefinition以及是否允许循环引用。
1 | protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) { |
关于BeanDefinition
,这也是一个比较重要的类,可以理解为Spring中的每一个Bean都是一个BeanDefinition实例,它是由配置文件中的每一个<bean>解析出来的。
接下来是比较重要的loadBeanDefinition()方法,它完成了Bean的加载与注册工作。
1 | protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException { |
1 | protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException { |
在spring中,把资源配置文件抽象为Resource实例,通过BeanDefinition解析Resource实例获取BeanDefition。这个过程我们就暂时忽略不讲,具体可以看BeanDefinitionReaderUtils这个类。我们接下来要关注bean的注册过程,同样是在这个类中。
1 | public static void registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) throws BeanDefinitionStoreException { |
registry.registerBaenDefinition()这个方法回到了DefaultListableBeanFactory
中。实际上仔细观察DefaultListableBeanFactory
类的定义,可以看到它实际上是实现了BeanDefinitionRegistry
接口,也就是说这个BeanFactory包括了BeanDefinition注册中心。
这个registerBeanDefinition方法比较长,我们只看它重要的地方。
1 | public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException { |
至此,我们的BeanFactory完成了创建,以及把配置文件里的bean添加到了注册中心里。
初始化Bean实例
上面的篇幅都仅仅讲了Bean的发现与注册,我们继续看refresh方法后面的部分,可以看到在实例化bean之前还有比较多的操作,不过我们可以把它理解为对BeanFactory或Bean的一些额外扩展或操作,暂时可以略过不细看,应该着眼于重点this.finishBeanFactoryInitialization(beanFactory);实例化操作。
1 | public void refresh() throws BeansException, IllegalStateException { |
我们来总结一下,到目前为止,应该说 BeanFactory 已经创建完成,并且所有的实现了 BeanFactoryPostProcessor 接口的 Bean 都已经初始化并且其中的 postProcessBeanFactory(factory) 方法已经得到回调执行了。而且 Spring 已经“手动”注册了一些特殊的 Bean,如 environment
、systemProperties
等。接下来,在finishBeanFactoryInitialization(beanFactory)方法中,如果没有设置懒加载,容器初始化了所有的单例Bean。
//AbstractApplicationContext.java
1 | protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) { |
preInstantiateSingletons()方法又回到了DefaultListableBeanFactory
里。需要关注的是getBean()方法
//DefaultListableBeanFactory
1 | public void preInstantiateSingletons() throws BeansException { |
//AbstractBeanFactory.java
1 | public Object getBean(String name) throws BeansException { |
接下来分析createBean方法,创建bean实例。
//AbstractAutowireCapableBeanFactory.java
1 | protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException { |
//AbstractAutowireCapableBeanFactory.java
1 | protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException { |
其实比较重要的就是createBeanInstance()创建实例的方法与populateBean()装配属性的方法,接下来我们依次看这两个方法。
createBeanInstance
创建Bean实例
1 | protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, Object[] args) { |
调用无参构造函数
1 | protected BeanWrapper instantiateBean(final String beanName, final RootBeanDefinition mbd) { |
实例化的操作在beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, parent);
中,也就是一个InstantiationStrategy实例的instantiate()方法进行了实例化的操作。
//SimpleInstantiationStrategy.java
1 |
|
bean属性注入
接下来是populateBean()注入属性的方法
// AbstractAutowireCapableBeanFactory
1 | protected void populateBean(String beanName, RootBeanDefinition mbd, BeanWrapper bw) { |
至此,实例化Bean及属性注入的过程都完成了。
本文仅关注BeanFactory的创建以及Bean的实例化过程,忽略了与Bean生命周期有关的一些重要的细节,比如BeanPostProcessor等,关于bean的生命周期可以参考这篇文章
总结
IOC容器的原理可以总结如下:
- 容器创建DefaultListableBeanFactory,用来存放BeanDefinition以及负责后续的初始化实例工作。
- ResourceLoad读取配置文件为Resource对象,BeanDefinitionReader读取Resource指向的配置文件,把每个<bean>解析为一个BeanDefinition对象,保存在BeanDefitionRegistry中,即BeanFactory的hashmap中。
- 容器扫描DefaultListableBeanFactory注册中心中的每个BeanDefinition,经过一系列的调用,最终通过InstantiateStrategy对象利用反射或CGLib进行Bean的实例创建,以及通过BeanWrapper对Bean的属性进行注入。
- 单例Bean缓存池:Spring 在DefaultSingletonBeanRegistry类中提供了一个用于缓存单实例 Bean 的缓存器,它是一个用HashMap实现的缓存器,单实例的Bean以beanName为键保存在这个HashMap中