Spring的IOC容器创建过程深入剖析

本文涉及的产品
容器镜像服务 ACR,镜像仓库100个 不限时长
简介:

前言

   本次对于Spring的IOC容器的创建过程是基于其源码进行研究分析的,主要涉及BeanFactory的创建过程,Bean的解析与注册过程,Bean实例化的过程以及诸如ClassPathXmlApplicationContext的IOC构建过程。

IOC容器创建过程

   在Spring中,三大核心组件Context、Bean以及Core中,Context将Bean和Core结合起来了,从而使得三大核心组件相互作用,共同构建整个Spring的基础。在Spring中,ApplicationContext的子类AbstractApplicationContext的refresh()是构建Bean的入口点,它设定了创建IOC的模板方法。子类创建IOC最终都会委派到该方法进行创建的。

   AbstractApplicationContext的refresh()源代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public  void  refresh()  throws  BeansException, IllegalStateException {
synchronized  ( this .startupShutdownMonitor) {
    // 准备上下文用于刷新
    prepareRefresh();
    // 创建BeanFactory,Bean定义的解析与注册
    ConfigurableListableBeanFactory beanFactory =     obtainFreshBeanFactory();
   // 为该上下文配置已经生成的BeanFactory
   prepareBeanFactory(beanFactory);
     try  {
     //可以调用用户自定义的BeanFactory来对已经生成的BeanFactory进行修改
     postProcessBeanFactory(beanFactory);
     invokeBeanFactoryPostProcessors(beanFactory);
     //可以对以后再创建Bean实例对象添加一些自定义操作
     registerBeanPostProcessors(beanFactory);
     //初始化信息源
     initMessageSource();
     //初始化事件
     initApplicationEventMulticaster();
     //初始化在特定上下文中的其它特殊Bean
     onRefresh();
     registerListeners();
     //Bean的真正实例化,创建非懒惰性的单例Bean
     finishBeanFactoryInitialization(beanFactory);
     finishRefresh();
    } catch  (BeansException ex) {
     destroyBeans();
     cancelRefresh(ex);
     throw  ex;
    }
}


   整个方法代码使用的就是模板方式设计模式,工厂方法模式等,定义了整个BeanFactory构建过程。构建BeanFactory,以便于产生所需要的Bean对象;注册可能感兴趣的事件;创建Bean实例对象;触发被监听的事件。IOC容器就是一个Context组合另外两个核心组件而形成的Bean的关系网,是Bean的大容器

   在解析IOC容器的创建过程中,以下面两行代码为原型,来穿插着解析该IOC的创建过程。这是一种常见的IOC容器的创建过程,仅仅通过new一个ClassPathXmlApplicationContext就完成了IOC创建。

1
2
ApplicationContext ctx =  new  ClassPathXmlApplicationContext( "beans.xml" );
     UserService service = ctx.getBean( "ser" ,UserService. class );

ClassPathXmlApplicationContext的继承关系如下

   当我们new一个ClassPathXmlApplicationContext,为其传递一个bean配置文件的地址,其最终调用的构造器如下:

1
2
3
4
5
6
7
8
9
10
11
public  ClassPathXmlApplicationContext(String[] configLocations,  boolean  refresh, ApplicationContext parent)
{
         super (parent);
         //设置配置文件的位置configLocations
     setConfigLocations(configLocations);
     if  (refresh) {
         //这里就是调用了父类AbstractApplicationContext的refresh
         //也证实了refresh是构建IOC的入口点
     refresh();
     }
}

   从源代码可以看出,第一句,最终会调用继承关系中的AbstractApplicationContext构造器,具体做的工作就是返回一个PathMatchingResourcePatternResolver。

   第二句,设置配置文件的位置,调用父类AbstractRefreshableConfigApplicationContext的方法,设置configLocations的值。利用这种构造器创建,并没有设置其属性configResources的值,则为null。(这个在下面讲IOC创建的地方需要用到)。

   第三句,在该构造器中调用了父类AbstractApplicationContext的refresh方法,从而进行IOC的真正创建了。

BeanFactory创建:

    BeanFactory创建是在obtainFreshBeanFactory方法中完成的。在该方法中,会调用子类实现了refreshBeanFactory的方法,刷新子类,如果BeanFactory存在则刷新,如果不存在就创建一个新的BeanFactory。最终默认的创建BeanFactory就是由DefaultListableBeanFactory来完成的。


   这是一个BeanFactory创建的时序图,在该时序图中包含了BeanFactory创建的过程,还有Bean定义的解析、加载以及注册过程,关于Bean定义的解析与加载注册过程,会在下面以详细的时序图来解释的。这里只是refresh()方法中,调用refreshBeanFactory()来构建BeanFactory的标准初始化过程。我们ClassPathXmlApplicationContext的BeanFactory就是遵循这样的构造过程。

Bean解析注册过程:

   在上面的时序图中,最右边可以看到对Bean的定义进行加载与解析和注册处理,

   在AbstractXmlApplicationContext中调用loadBeanDefinitions,这里最终是交给XmlBeanDefinitionReader来加载指定的bean定义。AbstractXmlApplicationContext类的loadBeanDefinitions()方法源码如下:

1
2
3
4
5
6
7
8
9
10
protected  void  loadBeanDefinitions(XmlBeanDefinitionReader reader)  throws  BeansException, IOException {
         Resource[] configResources = getConfigResources();
         if  (configResources !=  null ) {
             reader.loadBeanDefinitions(configResources);
         }
         String[] configLocations = getConfigLocations();
         if  (configLocations !=  null ) {
             reader.loadBeanDefinitions(configLocations);
         }
     }

   这个方法就是根据指定的XmlBeanDefinitionReader来加载我们的Bean定义,并且最其进行解析和注册处理。

   在这里,一般只会执行其中的一个if语句,这里我们的ClassPathXmlApplicationContext,前面已经说过,new的过程只设置了configLocations值,并没有设置configResources属性值,则就会直接执行第二个if语句。这里就会调用AbstractBeanDefinitionReader的一个重载方法loadBeanDefinitions方法,这个最终是调用子类XmlBeanDefinitionReader的loadBeanDefinitions()

    具体的标准Bean解析注册过程如下:


其它初始化与准备过程:

   在BeanFactory创建完成后,我们定义的Bean仅仅是已经被解析和注册了,实际上还没真正的创建他们的实例对象。当obtainFreshBeanFactory()方法返回后,就会为该上下文配置刚刚已经生成的BeanFactory,我们要想扩展Spring的IOC容器,则就可以在下面的三个代码处进行自定义操作

1
2
3
postProcessBeanFactory(beanFactory);
invokeBeanFactoryPostProcessors(beanFactory);
registerBeanPostProcessors(beanFactory);

   前两句主要就是当一个BeanFactory创建完成后,我们可以调用自定义的BeanFactory,对已经创建的配置信息进行修改。最后一句就是在以后的Bean的实例化过程中,可以定义一些自定义操作。

   接下来就是就是初始化上下文的信息源,应用程序事件,其他特殊bean。


Bean实例对象创建过程:

   在该refresh中,最后会调用finishBeanFactoryInitialization来完成Bean的实例对象创建过程。具体的Bean创建时序图如下:

      

   在Bean的创建过程,有个特殊的FactoryBean,这是个特殊的Bean他是个工厂Bean,可以产生Bean的Bean,这里的产生Bean是指 Bean的实例,如果一个类继承FactoryBean用户可以自己定义产生实例对象的方法只要实现他的getObject方法。在Spring内部这个Bean的实例对象是FactoryBean,通过调用这个对象的getObject方 法就能获取用户自定义产生的对象,从而为Spring提供了很好的扩展性。Spring获取FactoryBean本身的对象是在前面加上&来完成的。


   在我们的示例代码的第二行,当使用getBean的时候获取Bean的时候具体流程如下:

   1,它会调用抽象方法getBeanFactory()

   2,最终调用AbstractRefreshableApplicationContext的getBeanFactory,是final的方法。




本文转自 zhao_xiao_long 51CTO博客,原文链接:http://blog.51cto.com/computerdragon/1244016

相关文章
|
21天前
|
XML Java 数据格式
【SpringFramework】Spring IoC-基于XML的实现
本文主要讲解SpringFramework中IoC和DI相关概念,及基于XML的实现方式。
108 69
|
20天前
|
Java Spring 容器
【SpringFramework】Spring IoC-基于注解的实现
本文主要记录基于Spring注解实现IoC容器和DI相关知识。
49 21
|
26天前
|
设计模式 XML Java
【23种设计模式·全精解析 | 自定义Spring框架篇】Spring核心源码分析+自定义Spring的IOC功能,依赖注入功能
本文详细介绍了Spring框架的核心功能,并通过手写自定义Spring框架的方式,深入理解了Spring的IOC(控制反转)和DI(依赖注入)功能,并且学会实际运用设计模式到真实开发中。
【23种设计模式·全精解析 | 自定义Spring框架篇】Spring核心源码分析+自定义Spring的IOC功能,依赖注入功能
|
25天前
|
存储 Java 应用服务中间件
【Spring】IoC和DI,控制反转,Bean对象的获取方式
IoC,DI,控制反转容器,Bean的基本常识,类注解@Controller,获取Bean对象的常用三种方式
|
30天前
|
XML Java 数据格式
Spring容器Bean之XML配置方式
通过对以上内容的掌握,开发人员可以灵活地使用Spring的XML配置方式来管理应用程序的Bean,提高代码的模块化和可维护性。
65 6
|
1月前
|
存储 缓存 Java
Spring面试必问:手写Spring IoC 循环依赖底层源码剖析
在Spring框架中,IoC(Inversion of Control,控制反转)是一个核心概念,它允许容器管理对象的生命周期和依赖关系。然而,在实际应用中,我们可能会遇到对象间的循环依赖问题。本文将深入探讨Spring如何解决IoC中的循环依赖问题,并通过手写源码的方式,让你对其底层原理有一个全新的认识。
63 2
|
1月前
|
安全 Java 开发者
Spring容器中的bean是线程安全的吗?
Spring容器中的bean默认为单例模式,多线程环境下若操作共享成员变量,易引发线程安全问题。Spring未对单例bean做线程安全处理,需开发者自行解决。通常,Spring bean(如Controller、Service、Dao)无状态变化,故多为线程安全。若涉及线程安全问题,可通过编码或设置bean作用域为prototype解决。
41 1
|
2月前
|
XML 缓存 Java
搞透 IOC、Spring IOC ,看这篇就够了!
本文详细解析了Spring框架的核心内容——IOC(控制反转)及其依赖注入(DI)的实现原理,帮助读者理解如何通过IOC实现组件解耦,提高程序的灵活性和可维护性。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
|
2月前
|
前端开发 Java Docker
使用Docker容器化部署Spring Boot应用程序
使用Docker容器化部署Spring Boot应用程序
|
2月前
|
Java Docker 微服务
利用Docker容器化部署Spring Boot应用
利用Docker容器化部署Spring Boot应用
63 0