建议使用以下浏览器,以获得最佳体验。 IE 9.0+以上版本 Chrome 31+ 谷歌浏览器 Firefox 30+ 火狐浏览器
设置昵称

在此一键设置昵称,即可参与社区互动!

确定
我再想想
选择版块
DevCloud 主题:7779帖子:80804

【技术干货】

IOC 容器的初始化过程

斑馬斑馬 2021/8/10 674

  最近复习了一遍Spring IOC容器的初始化过程,结合书籍《Spring源码深度解析》总结了一下,IOC容器的初始化过程,大概分为以下三点:

1、定位资源:

  定位相关的配置文件,扫描相关注解

2、加载资源:

  将配置信息加载到内存中

3、注册:

  根据载入的配置信息,初始化对象,并将其装载至容器中

POM文件

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>springTest</artifactId>
    <version>1.0-SNAPSHOT</version>

<dependencies>
    <!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.0.2.RELEASE</version>
    </dependency>

</dependencies>
</project>


测试代码

public class ServiceB {

    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");

        Object serviceA = context.getBean("serviceA");
        System.out.println(serviceA);
    }


xml配置

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:cache="http://www.springframework.org/schema/cache"
       xmlns:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/cache
       http://www.springframework.org/schema/cache/spring-cache.xsd">

    <context:component-scan base-package="com.donkeys.spring"/>
    <bean id="serviceA" class="com.donkeys.spring.service.ServiceA"></bean>
</beans>

ClassPathXmlApplicationContext构造方法

    public ClassPathXmlApplicationContext(
            String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
            throws BeansException {

        super(parent);
        //根据传入的配置文件名称,调用父类的setConfigLocations方法,解析配置文件路径,
        setConfigLocations(configLocations);
        //refresh = true
        //refresh() 方法会重启整个容器
        if (refresh) {
            refresh();
        }
    }

  构造方法中一共做了2件事,首先是设置配置文件的路径,然后对整个容器进行刷新。

  这里我们重点关注refresh()方法;进入refresh()方法

AbstractApplicationContext类的refresh()方法

@Override
public void refresh() throws BeansException, IllegalStateException {
   synchronized (this.startupShutdownMonitor) {
      // Prepare this context for refreshing.
      //为刷新前做准备
      prepareRefresh();

      // Tell the subclass to refresh the internal bean factory.
      //获取IOC容器,这里就是处理资源定位以配置文件加载/注册的方法
      ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

      // Prepare the bean factory for use in this context.
      prepareBeanFactory(beanFactory);

      try {
         // Allows post-processing of the bean factory in context subclasses.
         postProcessBeanFactory(beanFactory);

         // Invoke factory processors registered as beans in the context.
         invokeBeanFactoryPostProcessors(beanFactory);

         // Register bean processors that intercept bean creation.
         registerBeanPostProcessors(beanFactory);

         // Initialize message source for this context.
         initMessageSource();

         // Initialize event multicaster for this context.
         initApplicationEventMulticaster();

         // Initialize other special beans in specific context subclasses.
         onRefresh();

         // Check for listener beans and register them.
         registerListeners();

         // Instantiate all remaining (non-lazy-init) singletons.
         finishBeanFactoryInitialization(beanFactory);

         // Last step: publish corresponding event.
         finishRefresh();
      }

      catch (BeansException ex) {
         if (logger.isWarnEnabled()) {
            logger.warn("Exception encountered during context initialization - " +
                  "cancelling refresh attempt: " + ex);
         }

         // Destroy already created singletons to avoid dangling resources.
         destroyBeans();

         // Reset 'active' flag.
         cancelRefresh(ex);

         // Propagate exception to caller.
         throw ex;
      }

      finally {
         // Reset common introspection caches in Spring's core, since we
         // might not ever need metadata for singleton beans anymore...
         resetCommonCaches();
      }
   }
}

  这里主要关注**obtainFreshBeanFactory()**方法

/**
 * Tell the subclass to refresh the internal bean factory.
 * @return the fresh BeanFactory instance
 * @see #refreshBeanFactory()
 * @see #getBeanFactory()
 */
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
   //刷新IOC容器
   //这里使用了委派设计模式,父类定义了抽象的refreshBeanFactory方法,具体调用实现调用子类的refreshBeanFactory方法
   refreshBeanFactory();
   //获取一个新的容器
   ConfigurableListableBeanFactory beanFactory = getBeanFactory();
   if (logger.isDebugEnabled()) {
      logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
   }
   return beanFactory;
}

  **obtainFreshBeanFactory()**方法总共干了2件事,

  重置容器,refreshBeanFactory()方法中会设置相关标志,清除旧的容器,同时为Spring上下文生成一个新的容器,获取一个新的容器

AbstractRefreshableApplicationContext的refreshBeanFactory()方法

  下面我们进入**refreshBeanFactory()**方法

/**
 * This implementation performs an actual refresh of this context's underlying
 * bean factory, shutting down the previous bean factory (if any) and
 * initializing a fresh bean factory for the next phase of the context's lifecycle.
 * 该方法会将之前的bean工厂全部关闭,并初始化一个全新的bean 工厂类 用于Spring 上下文的生命周期
 * bean工厂就是IOC容器
 */
@Override
protected final void refreshBeanFactory() throws BeansException {
   //判断是否之前也有容器
   //如果有就销毁掉
   if (hasBeanFactory()) {
      destroyBeans();
      closeBeanFactory();
   }
   try {
      //创建一个新的工厂
      DefaultListableBeanFactory beanFactory = createBeanFactory();
      beanFactory.setSerializationId(getId());
      customizeBeanFactory(beanFactory);
      //读取Bean对象的定义
      //这里也是使用的委派设计模式
      loadBeanDefinitions(beanFactory);
      synchronized (this.beanFactoryMonitor) {
         this.beanFactory = beanFactory;
      }
   }
   catch (IOException ex) {
      throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
   }
}

  这里创建了新的容器工厂,同时将新的工厂传入了loadBeanDefinitions()方法中,下面来看一下在loadBeanDefinitions方法中具体做了什么操作。

AbstractXmlApplicationContext的 loadBeanDefinitions(DefaultListableBeanFactory beanFactory)方法

  在AbstractRefreshableApplicationContext类的refreshBeanFactory方法中,调用了loadBeanDefinitions方法,但是这个方法它的一个抽象方法,具体实现应由子类去实现,我们在程序启动时,使用的ClassPathXmlApplicationContext类。根据文章开头的类图可以知道,这里会调用子类AbstractXmlApplicationContext的loadBeanDefinitions方法去完成本次加载

@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
   // Create a new XmlBeanDefinitionReader for the given BeanFactory.
   //使用默认的beanFactory去创建 XmlBeanDefinitionReader
   XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

   // Configure the bean definition reader with this context's
   // resource loading environment.
   //设置资源的加载环境
   beanDefinitionReader.setEnvironment(this.getEnvironment());
    //设置资源读取器
   beanDefinitionReader.setResourceLoader(this);
    //设置实体解析器
   beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

   // Allow a subclass to provide custom initialization of the reader,
   // then proceed with actually loading the bean definitions.
    //初始化 bean对象定义读取器
   initBeanDefinitionReader(beanDefinitionReader);
    //使用初始化完成的读取器,调用loadBeanDefinitions方法
   loadBeanDefinitions(beanDefinitionReader);
}

  总的来说,这里只干了一件事,那就是初始化配置

//设置资源读取器
beanDefinitionReader.setResourceLoader(this);
//初始化 bean对象定义读取器
initBeanDefinitionReader(beanDefinitionReader);

  设置资源读取器,这里设置的资源读取器就是当前这个对象本身

  通过类图我们可以发现我们这个类的顶级父类ApplicationContext,继承自DefaultResourceLoader这个类,该类实现了ResourceLoader接口,说明这个类的实例化对象本身是具有资源读取器的功能的

  初始化bean对象定义读取器,这里设置xml文件的校验方式

  下面我们继续看**loadBeanDefinitions(XmlBeanDefinitionReader reader)**方法

protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
   //从子类对象中获取到资源定位
   Resource[] configResources = getConfigResources();
   if (configResources != null) {
      //XmlBeanDefinitionReader 读取器调用其父类的
      reader.loadBeanDefinitions(configResources);
   }
   String[] configLocations = getConfigLocations();
   if (configLocations != null) {
      reader.loadBeanDefinitions(configLocations);
   }
}

  先看这一行

//这行代码的具体实现是由子类完成的,通过类图可以知道AbstractXmlApplicationContext的子类为ClassPathXmlApplicationContext
//这里主要将我们最开始在构造方法中设置好的配置文件进行返回
Resource[] configResources = getConfigResources();

  再看这一行

//如果返回的配置文件不为空,就将返回的已经封装好的资源文件进行读取,   
if (configResources != null) {
      //XmlBeanDefinitionReader 读取器调用其父类的
      reader.loadBeanDefinitions(configResources);
   }

  至此。资源文件定位过程已经加载完成。后续就是读取和注册。整个IOC容器加载过程中最重要的是读取过程,我们可以从刚刚的定位过程来看,虽然叫定位过程,但是其实就是一个配置文件读取器的初始化过程,这个过程会设置相关的解析策略以及校验策略。最终读取器生成后,就可以将我们早早设置好的配置文件载入然后进行读取。

回复5

2021/8/10 17:33

感谢分享

Jack20
0 0
2021/8/10 18:08

感谢分享~

LSMM
0 0
2021/8/11 10:05

深度解析,厉害。

2021/8/11 22:57

感谢分享

猎心者
0 0
2021/8/12 10:26

解析的真细致

上划加载中
直达楼层
标签
您还可以添加5个标签
  • 没有搜索到和“关键字”相关的标签
  • 云产品
  • 解决方案
  • 技术领域
  • 通用技术
  • 平台功能
取消

采纳成功

您已采纳当前回复为最佳回复

斑馬斑馬

发帖: 210粉丝: 2

发消息 + 关注

发表于2021年08月10日 16:18:33 674 5
直达本楼层的链接
楼主
显示全部楼层
[技术干货] IOC 容器的初始化过程

  最近复习了一遍Spring IOC容器的初始化过程,结合书籍《Spring源码深度解析》总结了一下,IOC容器的初始化过程,大概分为以下三点:

1、定位资源:

  定位相关的配置文件,扫描相关注解

2、加载资源:

  将配置信息加载到内存中

3、注册:

  根据载入的配置信息,初始化对象,并将其装载至容器中

POM文件

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>springTest</artifactId>
    <version>1.0-SNAPSHOT</version>

<dependencies>
    <!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.0.2.RELEASE</version>
    </dependency>

</dependencies>
</project>


测试代码

public class ServiceB {

    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");

        Object serviceA = context.getBean("serviceA");
        System.out.println(serviceA);
    }


xml配置

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:cache="http://www.springframework.org/schema/cache"
       xmlns:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/cache
       http://www.springframework.org/schema/cache/spring-cache.xsd">

    <context:component-scan base-package="com.donkeys.spring"/>
    <bean id="serviceA" class="com.donkeys.spring.service.ServiceA"></bean>
</beans>

ClassPathXmlApplicationContext构造方法

    public ClassPathXmlApplicationContext(
            String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
            throws BeansException {

        super(parent);
        //根据传入的配置文件名称,调用父类的setConfigLocations方法,解析配置文件路径,
        setConfigLocations(configLocations);
        //refresh = true
        //refresh() 方法会重启整个容器
        if (refresh) {
            refresh();
        }
    }

  构造方法中一共做了2件事,首先是设置配置文件的路径,然后对整个容器进行刷新。

  这里我们重点关注refresh()方法;进入refresh()方法

AbstractApplicationContext类的refresh()方法

@Override
public void refresh() throws BeansException, IllegalStateException {
   synchronized (this.startupShutdownMonitor) {
      // Prepare this context for refreshing.
      //为刷新前做准备
      prepareRefresh();

      // Tell the subclass to refresh the internal bean factory.
      //获取IOC容器,这里就是处理资源定位以配置文件加载/注册的方法
      ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

      // Prepare the bean factory for use in this context.
      prepareBeanFactory(beanFactory);

      try {
         // Allows post-processing of the bean factory in context subclasses.
         postProcessBeanFactory(beanFactory);

         // Invoke factory processors registered as beans in the context.
         invokeBeanFactoryPostProcessors(beanFactory);

         // Register bean processors that intercept bean creation.
         registerBeanPostProcessors(beanFactory);

         // Initialize message source for this context.
         initMessageSource();

         // Initialize event multicaster for this context.
         initApplicationEventMulticaster();

         // Initialize other special beans in specific context subclasses.
         onRefresh();

         // Check for listener beans and register them.
         registerListeners();

         // Instantiate all remaining (non-lazy-init) singletons.
         finishBeanFactoryInitialization(beanFactory);

         // Last step: publish corresponding event.
         finishRefresh();
      }

      catch (BeansException ex) {
         if (logger.isWarnEnabled()) {
            logger.warn("Exception encountered during context initialization - " +
                  "cancelling refresh attempt: " + ex);
         }

         // Destroy already created singletons to avoid dangling resources.
         destroyBeans();

         // Reset 'active' flag.
         cancelRefresh(ex);

         // Propagate exception to caller.
         throw ex;
      }

      finally {
         // Reset common introspection caches in Spring's core, since we
         // might not ever need metadata for singleton beans anymore...
         resetCommonCaches();
      }
   }
}

  这里主要关注**obtainFreshBeanFactory()**方法

/**
 * Tell the subclass to refresh the internal bean factory.
 * @return the fresh BeanFactory instance
 * @see #refreshBeanFactory()
 * @see #getBeanFactory()
 */
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
   //刷新IOC容器
   //这里使用了委派设计模式,父类定义了抽象的refreshBeanFactory方法,具体调用实现调用子类的refreshBeanFactory方法
   refreshBeanFactory();
   //获取一个新的容器
   ConfigurableListableBeanFactory beanFactory = getBeanFactory();
   if (logger.isDebugEnabled()) {
      logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
   }
   return beanFactory;
}

  **obtainFreshBeanFactory()**方法总共干了2件事,

  重置容器,refreshBeanFactory()方法中会设置相关标志,清除旧的容器,同时为Spring上下文生成一个新的容器,获取一个新的容器

AbstractRefreshableApplicationContext的refreshBeanFactory()方法

  下面我们进入**refreshBeanFactory()**方法

/**
 * This implementation performs an actual refresh of this context's underlying
 * bean factory, shutting down the previous bean factory (if any) and
 * initializing a fresh bean factory for the next phase of the context's lifecycle.
 * 该方法会将之前的bean工厂全部关闭,并初始化一个全新的bean 工厂类 用于Spring 上下文的生命周期
 * bean工厂就是IOC容器
 */
@Override
protected final void refreshBeanFactory() throws BeansException {
   //判断是否之前也有容器
   //如果有就销毁掉
   if (hasBeanFactory()) {
      destroyBeans();
      closeBeanFactory();
   }
   try {
      //创建一个新的工厂
      DefaultListableBeanFactory beanFactory = createBeanFactory();
      beanFactory.setSerializationId(getId());
      customizeBeanFactory(beanFactory);
      //读取Bean对象的定义
      //这里也是使用的委派设计模式
      loadBeanDefinitions(beanFactory);
      synchronized (this.beanFactoryMonitor) {
         this.beanFactory = beanFactory;
      }
   }
   catch (IOException ex) {
      throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
   }
}

  这里创建了新的容器工厂,同时将新的工厂传入了loadBeanDefinitions()方法中,下面来看一下在loadBeanDefinitions方法中具体做了什么操作。

AbstractXmlApplicationContext的 loadBeanDefinitions(DefaultListableBeanFactory beanFactory)方法

  在AbstractRefreshableApplicationContext类的refreshBeanFactory方法中,调用了loadBeanDefinitions方法,但是这个方法它的一个抽象方法,具体实现应由子类去实现,我们在程序启动时,使用的ClassPathXmlApplicationContext类。根据文章开头的类图可以知道,这里会调用子类AbstractXmlApplicationContext的loadBeanDefinitions方法去完成本次加载

@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
   // Create a new XmlBeanDefinitionReader for the given BeanFactory.
   //使用默认的beanFactory去创建 XmlBeanDefinitionReader
   XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

   // Configure the bean definition reader with this context's
   // resource loading environment.
   //设置资源的加载环境
   beanDefinitionReader.setEnvironment(this.getEnvironment());
    //设置资源读取器
   beanDefinitionReader.setResourceLoader(this);
    //设置实体解析器
   beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

   // Allow a subclass to provide custom initialization of the reader,
   // then proceed with actually loading the bean definitions.
    //初始化 bean对象定义读取器
   initBeanDefinitionReader(beanDefinitionReader);
    //使用初始化完成的读取器,调用loadBeanDefinitions方法
   loadBeanDefinitions(beanDefinitionReader);
}

  总的来说,这里只干了一件事,那就是初始化配置

//设置资源读取器
beanDefinitionReader.setResourceLoader(this);
//初始化 bean对象定义读取器
initBeanDefinitionReader(beanDefinitionReader);

  设置资源读取器,这里设置的资源读取器就是当前这个对象本身

  通过类图我们可以发现我们这个类的顶级父类ApplicationContext,继承自DefaultResourceLoader这个类,该类实现了ResourceLoader接口,说明这个类的实例化对象本身是具有资源读取器的功能的

  初始化bean对象定义读取器,这里设置xml文件的校验方式

  下面我们继续看**loadBeanDefinitions(XmlBeanDefinitionReader reader)**方法

protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
   //从子类对象中获取到资源定位
   Resource[] configResources = getConfigResources();
   if (configResources != null) {
      //XmlBeanDefinitionReader 读取器调用其父类的
      reader.loadBeanDefinitions(configResources);
   }
   String[] configLocations = getConfigLocations();
   if (configLocations != null) {
      reader.loadBeanDefinitions(configLocations);
   }
}

  先看这一行

//这行代码的具体实现是由子类完成的,通过类图可以知道AbstractXmlApplicationContext的子类为ClassPathXmlApplicationContext
//这里主要将我们最开始在构造方法中设置好的配置文件进行返回
Resource[] configResources = getConfigResources();

  再看这一行

//如果返回的配置文件不为空,就将返回的已经封装好的资源文件进行读取,   
if (configResources != null) {
      //XmlBeanDefinitionReader 读取器调用其父类的
      reader.loadBeanDefinitions(configResources);
   }

  至此。资源文件定位过程已经加载完成。后续就是读取和注册。整个IOC容器加载过程中最重要的是读取过程,我们可以从刚刚的定位过程来看,虽然叫定位过程,但是其实就是一个配置文件读取器的初始化过程,这个过程会设置相关的解析策略以及校验策略。最终读取器生成后,就可以将我们早早设置好的配置文件载入然后进行读取。

Spring 容器

举报
分享

分享文章到朋友圈

分享文章到微博

采纳成功

您已采纳当前回复为最佳回复

仙女本仙

发帖: 608粉丝: 52

发消息 + 关注

发表于2021年08月10日 17:33:25
直达本楼层的链接
沙发
显示全部楼层

感谢分享

点赞 评论 引用 举报

采纳成功

您已采纳当前回复为最佳回复

Jack20

发帖: 388粉丝: 214

发消息 + 关注

发表于2021年08月10日 18:08:24
直达本楼层的链接
板凳
显示全部楼层

感谢分享~

点赞 评论 引用 举报

采纳成功

您已采纳当前回复为最佳回复

LSMM

发帖: 0粉丝: 0

发消息 + 关注

发表于2021年08月11日 10:05:07
直达本楼层的链接
地板
显示全部楼层

深度解析,厉害。

点赞 评论 引用 举报

采纳成功

您已采纳当前回复为最佳回复

是否阿萨法

发帖: 1粉丝: 0

发消息 + 关注

发表于2021年08月11日 22:57:51
直达本楼层的链接
5#
显示全部楼层

感谢分享

点赞 评论 引用 举报

采纳成功

您已采纳当前回复为最佳回复

猎心者

发帖: 415粉丝: 4

发消息 + 关注

发表于2021年08月12日 10:26:57
直达本楼层的链接
6#
显示全部楼层

解析的真细致

点赞 评论 引用 举报

游客

您需要登录后才可以回帖 登录 | 立即注册

结贴

您对问题的回复是否满意?
满意度
非常满意 满意 一般 不满意
我要反馈
0/200