您的位置 首页 golang

万字长文带你阅读Spring源码之四IoC容器初始化

IoC容器初始化是一块非常重要、也是非常难的内容。我会分开几篇介绍。

IoC容器的初始化包括 BeanDefinition Resource 定位、载入和注册三个基本过程。我们以 ApplicationContext 为例,这个类是我们最常见也是最常用的。

主要类关系如下:

IoC实现

ApplicationContext 运行上下文嵌套,通过保持父上下文可以维持一个上下文体系。对于bean的查找可以在这个上下文体系中发生,首先检查当前上下文,其次是父上下文,逐级向上,这样为不同的Spring应用提供了一个共享的bean定义环境。

主要有三种方式可以使用容器: ClassPathXmlApplicationContext FileSystemXmlApplicationContext AnnotactionConfigApplicationContext XmlWebApplicationContext ,其中第三种是最常用的,也是使用最广泛、最简单的。对于3.0之前更多是使用的xml配置方式,但是现在使用最多的是基于注解的方式,特别是springboot,全部都是基于注解做到零配置。 XmlWebApplicationContext 基本都是在SpringMVC的项目中使用。

bean定义资源准备(设置xml配置文件路径、设置资源加载器)

 public class FileSystemXmlApplicationContext extends AbstractXmlApplicationContext {

    public FileSystemXmlApplicationContext() {
    }

    public FileSystemXmlApplicationContext(ApplicationContext parent) {
        super(parent);
    }

    public FileSystemXmlApplicationContext(String configLocation) throws BeansException {
        this(new String[] {configLocation}, true, null);
    }

    public FileSystemXmlApplicationContext(String... configLocations) throws BeansException {
        this(configLocations, true, null);
    }

    public FileSystemXmlApplicationContext(String[] configLocations, ApplicationContext parent) throws BeansException {
        this(configLocations, true, parent);
    }

    public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh) throws BeansException {
        this(configLocations, refresh, null);
    }

    public FileSystemXmlApplicationContext(
            String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
            throws BeansException {
        // 创建资源加载器
        super(parent);
        setConfigLocations(configLocations);
        if (refresh) {
            refresh();
        }
    }
  // 这个方法会在解析xml的子类AbstractBeanDefinitionReader中回调
    @Override
    protected Resource getResourceByPath(String path) {
        if (path.startsWith("/")) {
            path = path.substring(1);
        }
        return new FileSystemResource(path);
}  

这个类提供了非常多的扩展,可以使用不同类型的构造函数来创建容器。但是都最终会调用到这个构造方法。

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

        super(parent);
        setConfigLocations(configLocations);
        if (refresh) {
            refresh();
        }
}  

这个构造方法在执行的时候首先执行父类 AbstractXmlApplicationContext 的构造方法,父类构造方法会先设置bean资源加载器。然后再调用父类的 setConfigLocations(configLoacations) 方法,这个方法用于设置bean定义资源文件(xml文件)的路径。通过类层架追踪,xml资源的加载最终是在 FileSystemXmlApplicationContext 的父类再网上几层的父类 AbstractXmlApplicationContext 中实现的。

FileSystemXmlApplicationContext主要父类

AbstractApplicationContext 的资源加载过程如下:

 // FileSystemXmlApplicationContext不断的通过super()方法找到父类的父类的父类。。。
// 最终到达它真正的实现地方
public AbstractApplicationContext() {
  // 获取资源处理器(读取spring配置文件)
  this.resourcePatternResolver = getResourcePatternResolver();
}

protected ResourcePatternResolver getResourcePatternResolver() {
  // 因为AbstractApplicationContext继承了DefaultResourceLoader
  // DefaultResourceLoader实现了ResourceLoader接口,所以可以直接传this进去
  return new PathMatchingResourcePatternResolver(this);
}  

资源加载器初始化完成之后,开始设置资源路径。 setConfigLocations(configLocations); 这个方法也是调用父类 AbstractRefreshableConfigApplicationContext 的方法完成的。

 public void setConfigLocations(@Nullable String... locations) {
  if (locations != null) {
    Assert.noNullElements(locations, "Config locations must not be null");
    this.configLocations = new String[locations.length];
    for (int i = 0; i < locations.length; i++) {
      this.configLocations[i] = resolvePath(locations[i]).trim();
    }
  }
  else {
    this.configLocations = null;
  }
}  

这个方法执行完毕之后,我们需要加载的bean资源文件都转成一个 String 数组,保存到变量 configLocations 中。

资源加载器准备好之后就是开始加载解析bean配置,然后根据bean定义执行各种操作,包括定义解析、bean初始化等等。

加载、解析、转换XML定义,实例化bean

Spring IoC容器对bean定义的加载是从 refresh() 方法开始的,这个方法是一个模板方法,它的作用是:在创建IoC容器前,如果已经有容器则把容器销毁关闭,然后再重新创建一个新的容器,然后再对容器做初始化。 FileSystemXmlApplicationContext 通过调用父类的 AbstractApplicationContext.refresh() 方法实现容器的创建,加载,初始等一系列操作。回到这个类的构造方法,可以看到在构造方法内部调用了 refresh() 方法。

 public FileSystemXmlApplicationContext(
  String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
throws BeansException {
  // 通过类层级,一直通过super()方法一层层往上找父类的父类的父类。。。。
  // 最终找到AbstractApplicationContext的构造方法,
  super(parent);
  setConfigLocations(configLocations);
  if (refresh) {
    refresh();
  }
}  

通过类层级往上找,他的父类 AbstractXmlApplicationContext AbstractRefreshableConfigApplicationContext AbstractRefreshableApplicationContext 都没有重写 refresh() 方法,这个方法的实现是在 AbstractApplicationContet 中实现的。

 public void refresh() throws BeansException, IllegalStateException {
  synchronized (this.startupShutdownMonitor) {
    // Prepare this context for refreshing.
    // 准备此上下文以进行刷新。
    prepareRefresh();

    // Tell the subclass to refresh the internal bean factory.
    // 获取beanFactory实例
    ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

    // Prepare the bean factory for use in this context.
    // 准备上下文使用的BeanFactory
    prepareBeanFactory(beanFactory);

    try {
      // Allows post-processing of the bean factory in context subclasses.
      // 允许子类处理BeanFactory后置处理器
      postProcessBeanFactory(beanFactory);

      // Invoke factory processors registered as beans in the context.
      // 转到ConfigurationClassPostProcessor里面执行
      // 执行bean工厂的后置处理器,里面会有非常多的,非常复杂的BeanFactoryPostProcessor实现类的执行。
      invokeBeanFactoryPostProcessors(beanFactory);

      // Register bean processors that intercept bean creation.
      // 转到PostProcessorRegistrationDelegate
      // 注册bean工厂的后置处理器(仅仅是把Spring内部定义的和用户定义的BeanPostProcessor注册到容器中,后面的步骤才会真正执行),
      // 这些后置处理器在bean的构造方法执行之后,在执行init方法前后执行指定的逻辑
      registerBeanPostProcessors(beanFactory);

      // Initialize message source for this context.
      // 初始化上下文的消息资源,比如message-xxx.properties
      initMessageSource();

      // Initialize event multicaster for this context.
      // 初始化上下文多波器
      initApplicationEventMulticaster();

      // Initialize other special beans in specific context subclasses.
      // 在上下文中初始化其他的bean,子类实现
      onRefresh();

      // Check for listener beans and register them.
      // 检查并注册监听器
      registerListeners();

      // Instantiate all remaining (non-lazy-init) singletons.
      // 初始化剩下其他的单例bean,包括FactoryBean,(用户定义的大部分类都是在这里实例化的)
      // 非常非常重要的方法,是整个IOC容器核心部分
      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();
    }
  }
}  

refresh() 方法的主要作用为了IoC容器bean的生命周期管理提供条件,Spring IoC容器载入bean定义资源文件从其子类 refreshBeanFactory() 方法启动。在创建IoC容器前,如果已经存在则把已经创建的容器销毁关闭,以保障 refresh() 之后是新建的、单例的容器。

这个方法就是Spring最最最核心的实现,整个Spring最核心的特性也是在这个方法实现的,包括后置处理器、事件发布、bean加载、 @Component @Autowired 等等注解、AOP等,下面逐个方法解析学习。

文章来源:智云一二三科技

文章标题:万字长文带你阅读Spring源码之四IoC容器初始化

文章地址:https://www.zhihuclub.com/91864.shtml

关于作者: 智云科技

热门文章

网站地图