Loading

深入理解Spring容器初始化(一):上下文的初始化

前言

我们知道,spring 的启动其实就是容器的启动,而一般情况下,容器指的其实就是上下文 ApplicationContext

AbstractApplicationContext 作为整个 ApplicationContext 体系中最高级的抽象类,为除了 ComplexWebApplicationContextSimpleWebApplicationContext 这两个容器外的全部容器,规定好了 refresh 的整体流程,所有的容器在完成一些自己的初始化配置后,都需要调用该 refresh 方法,依次完成指定内容的初始化。

也就是说,读懂了 AbstractApplicationContext.refresh() 方法,其实就读懂了容器的启动流程:

public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {

        // ================= 一、上下文的初始化 =================
        // 准备上下文
        prepareRefresh();
        // 通知子类刷新内部工厂
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
        // 准备bean工厂以便在当前上下文中使用
        prepareBeanFactory(beanFactory);

        try {
            // ================= 二、BeanFactory的初始化 =================
            // 对工厂进行默认后置处理
            postProcessBeanFactory(beanFactory);
            // 使用后置处理器对工厂进行处理
            invokeBeanFactoryPostProcessors(beanFactory);
            // 注册Bean后置处理器
            registerBeanPostProcessors(beanFactory);

            // ================= 三、事件,Bean及其他配置的初始化 =================
            // 初始化此上下文的消息源
            initMessageSource();
            // 为此上下文初始化事件广播者
            initApplicationEventMulticaster();
            // 初始化特定上下文子类中的其他特殊bean
            onRefresh();
            // 检查侦听器bean并注册
            registerListeners();
            // 实例化所有非懒加载的剩余单例
            finishBeanFactoryInitialization(beanFactory);
            // 完成刷新
            finishRefresh();
        }


        // ================= 异常处理 =================
        catch (BeansException ex) {
            if (logger.isWarnEnabled()) {
                logger.warn("Exception encountered during context initialization - " +
                            "cancelling refresh attempt: " + ex);
            }
            // 销毁已创建的单例
            destroyBeans();
            // 重置上下文的激活状态
            cancelRefresh(ex);
            throw ex;
        }
        finally {
            // 重置内部的一些元数据缓存
            resetCommonCaches();
        }
    }
}

从总体来看,该方法描述的初始化过程大概分为三步:

笔者将基于 spring 源码 5.2.x 分支,分别通过五篇文章从源码分析 spring 容器的初始化过程。

本文是其中的第一篇文章,将介绍上下文的初始化过程。

相关文章:

一、刷新上下文属性

上下文的初始化,对应 prepareRefreshobtainFreshBeanFactoryprepareBeanFactory 三个步骤。

其中,调用 prepareRefresh 是第一个步骤,它用于用于初始化 ApplicationContext 本身的一些属性。

protected void prepareRefresh() {
    // 设置启动状态,并输出日志
    this.startupDate = System.currentTimeMillis();
    this.closed.set(false);
    this.active.set(true);

    if (logger.isDebugEnabled()) {
        if (logger.isTraceEnabled()) {
            logger.trace("Refreshing " + this);
        }
        else {
            logger.debug("Refreshing " + getDisplayName());
        }
    }

    // 初始化上下文环境中的属性数据源占位符
    initPropertySources();
    
    // 校验必要的属性参数
    getEnvironment().validateRequiredProperties();

    // 刷新早期事件监听器
    if (this.earlyApplicationListeners == null) {
        this.earlyApplicationListeners = new LinkedHashSet<>(this.applicationListeners);
    }
    else {
        this.applicationListeners.clear();
        this.applicationListeners.addAll(this.earlyApplicationListeners);
    }
    // 允许使用的早期事件,在广播器完成初始化后可以发布
    this.earlyApplicationEvents = new LinkedHashSet<>();
}

1、初始化属性数据源

initPropertySources 方法在 AbstractApplicationContext 是个空方法:

protected void initPropertySources() {
    // For subclasses: do nothing by default.
}

这里需要先强调一下占位符的概念。由于在 spring 启动过程,很多对象的需要经过多个阶段才能彻底完成配置,但是在未加载完毕的时候就需要在其他地方被引用,为此 spring 中很多地方引入了名称为 XXXHolder 的类,用 Holder 表示一个还未加载完成的实例。

initPropertySources 的本意是将一些上下文属性里面数据源的占位符替换为真正的资源对象,实际场景中,该方法只有在 WebApplicationContext 体系下的实现类会去重写该方法,比如 AbstractRefreshableWebApplicationContext

@Override
protected void initPropertySources() {
    ConfigurableEnvironment env = getEnvironment();
    if (env instanceof ConfigurableWebEnvironment) {
        ((ConfigurableWebEnvironment) env).initPropertySources(this.servletContext, this.servletConfig);
    }
}

这个方法的作用是在 web 环境中,把 servlet 相关的一些配置的占位符替换为真正的 servletContextservletConfig

2、必要属性参数的校验

getEnvironment().validateRequiredProperties() 这行代码的作用的校验必要的属性参数,实际操作的是 Environment,我们以 AbstractEnvironment#validateRequiredProperties 为例,该方法最后会调用 AbstractPropertyResolver#validateRequiredProperties

// AbstractEnvironment
@Override
public void validateRequiredProperties() throws MissingRequiredPropertiesException {
    this.propertyResolver.validateRequiredProperties();
}

// AbstractPropertyResolver
@Override
public void validateRequiredProperties() {
    MissingRequiredPropertiesException ex = new MissingRequiredPropertiesException();
    for (String key : this.requiredProperties) {
        if (this.getProperty(key) == null) {
            ex.addMissingRequiredProperty(key);
        }
    }
    if (!ex.getMissingRequiredProperties().isEmpty()) {
        throw ex;
    }
}

这里的处理非常简单,遍历非空属性,如果是空就抛出 MissingRequiredPropertiesException 异常。

二、刷新上下文中的工厂

调用 AbstarctApplicationContext.obtainFreshBeanFactory() 方法是初始化容器的第二步。

该方法主要用于关闭上下文中可能存在的旧 BeanFactory,并创建新的 BeanFactory

AbstractApplicationContext 中对 refreshBeanFactory 的解释,该方法的实现按容器是否可重复刷新分为两种:

  • 直接返回上下文中原有的工厂,如果重复刷新会抛出 IllegalStateException 异常;
  • 直接创建一个新工厂,然后替换上下文中原有的工厂;

1、刷新工厂

可重复刷新的上下文

不允许重复刷新的容器包括 GenericApplicationContext 与它的子类,或者更明确一点,除了 AbstractRefreshableApplicationContext 子类外的全部上下文:

@Override
protected final void refreshBeanFactory() throws IllegalStateException {
    if (!this.refreshed.compareAndSet(false, true)) { // 如果刷新成功就报错
        throw new IllegalStateException(
            "GenericApplicationContext does not support multiple refresh attempts: just call 'refresh' once");
    }
    this.beanFactory.setSerializationId(getId());
}

不可重复刷新的上下文

可重复刷新的容器包括 AbstractRefreshableApplicationContext 的子类:

@Override
protected final void refreshBeanFactory() throws BeansException {、
    // 若已有工厂,则先销毁单例然后关闭该工厂
    if (hasBeanFactory()) {
        destroyBeans();
        closeBeanFactory();
    }
    try {
        // 创建一个新工厂,并同步旧工厂的配置信息到新工厂,然后替换掉上下文中的旧工厂
        DefaultListableBeanFactory beanFactory = createBeanFactory();
        beanFactory.setSerializationId(getId());
        // 定制工厂的一些参数
        customizeBeanFactory(beanFactory);
        // 加载BeanDefinition
        loadBeanDefinitions(beanFactory);
        this.beanFactory = beanFactory;
    }
    catch (IOException ex) {
        throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
    }
}

2、向工厂注册BeanDefinition

加载 BeanDefinition 这一步其实是在 refreshBeanFactory() 完成的,在 AbstractRefreshableApplicationContext 中他是一个抽象方法,而有三个子类实现了该方法,它们分别是:

  • AnnotationConfigWebApplicationContext
  • GroovyWebApplicationContext
  • XmlWebApplicationContext

光看着三个上下文的名称,用脚指头都能猜到,不同的配置环境会对应不同的实现类,实现类会在这一步根据不同的配置文件解析并加载 BeanDefinitionBeanFactory 里。

我们以注解配置对应的 AnnotationConfigWebApplicationContext 为例:

protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) {
    // BeanDefinition配置解析器
    AnnotatedBeanDefinitionReader reader = getAnnotatedBeanDefinitionReader(beanFactory);
    // class扫描器
    ClassPathBeanDefinitionScanner scanner = getClassPathBeanDefinitionScanner(beanFactory);

    // 名称生成器
    BeanNameGenerator beanNameGenerator = getBeanNameGenerator();
    if (beanNameGenerator != null) {
        reader.setBeanNameGenerator(beanNameGenerator);
        scanner.setBeanNameGenerator(beanNameGenerator);
        beanFactory.registerSingleton(AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR, beanNameGenerator);
    }

    // 作用域解析器
    ScopeMetadataResolver scopeMetadataResolver = getScopeMetadataResolver();
    if (scopeMetadataResolver != null) {
        reader.setScopeMetadataResolver(scopeMetadataResolver);
        scanner.setScopeMetadataResolver(scopeMetadataResolver);
    }

    // 扫描在注解中声明的Class
    if (!this.componentClasses.isEmpty()) {
        if (logger.isDebugEnabled()) {
            logger.debug("Registering component classes: [" +
                         StringUtils.collectionToCommaDelimitedString(this.componentClasses) + "]");
        }
        reader.register(ClassUtils.toClassArray(this.componentClasses));
    }

    // 扫描basePackage下的类型
    if (!this.basePackages.isEmpty()) {
        if (logger.isDebugEnabled()) {
            logger.debug("Scanning base packages: [" +
                         StringUtils.collectionToCommaDelimitedString(this.basePackages) + "]");
        }
        scanner.scan(StringUtils.toStringArray(this.basePackages));
    }

    // 获取配置文件所在路径
    String[] configLocations = getConfigLocations();
    if (configLocations != null) {
        for (String configLocation : configLocations) {
            try {
                Class<?> clazz = ClassUtils.forName(configLocation, getClassLoader());
                if (logger.isTraceEnabled()) {
                    logger.trace("Registering [" + configLocation + "]");
                }
                reader.register(clazz);
            }
            catch (ClassNotFoundException ex) {
                if (logger.isTraceEnabled()) {
                    logger.trace("Could not load class for config location [" + configLocation +
                                 "] - trying package scan. " + ex);
                }
                int count = scanner.scan(configLocation);
                if (count == 0 && logger.isDebugEnabled()) {
                    logger.debug("No component classes found for specified class/package [" + configLocation + "]");
                }
            }
        }
    }
}

不同的实现对应不同的代码,不过逻辑基本都一致,无外乎创建 Reader 然后解析配置文件/配置类,然后把配置中的 Bean 或者扫描配置的 class 或项目路径上的 Class 得到的 Bean 加载到 BeanFactory 里。

3、关闭旧工厂

若当前已有 BeanFactory,上下文会先尝试移除该工厂,然后再新建工厂。

destroyBeans调用 将销毁工厂中全部的单例 bean,并清空相关缓存,比如我们熟悉的三级缓存:

protected void destroyBeans() {
    getBeanFactory().destroySingletons();
}

@Override
public void destroySingletons() {
    // 调用DefaultSingletonBeanRegistry的destroySingletons方法
    super.destroySingletons();
    // 清空所有非空的bean缓存
    updateManualSingletonNames(Set::clear, set -> !set.isEmpty());
    clearByTypeCache();
}

// DefaultSingletonBeanRegistry
public void destroySingletons() {
    if (logger.isTraceEnabled()) {
        logger.trace("Destroying singletons in " + this);
    }
    synchronized (this.singletonObjects) {
        this.singletonsCurrentlyInDestruction = true;
    }

    // 根据bean名称将bean从各个缓存中移除
    String[] disposableBeanNames;
    synchronized (this.disposableBeans) {
        disposableBeanNames = StringUtils.toStringArray(this.disposableBeans.keySet());
    }
    for (int i = disposableBeanNames.length - 1; i >= 0; i--) {
        destroySingleton(disposableBeanNames[i]);
    }

    // 清空三级缓存
    this.containedBeanMap.clear();
    this.dependentBeanMap.clear();
    this.dependenciesForBeanMap.clear();

    // 完全清空全部bean缓存
    clearSingletonCache();
}

然后再在 closeBeanFactory 方法中移除当前上下文对应旧 BeanFactory 实例的引用:

protected final void closeBeanFactory() {
    DefaultListableBeanFactory beanFactory = this.beanFactory;
    if (beanFactory != null) {
        beanFactory.setSerializationId(null);
        this.beanFactory = null;
    }
}

4、创建新工厂

新建工厂时,先通过 createBeanFactory 方法创建一个 DefaultListableBeanFactory,该 DefaultListableBeanFactory 会具备当前上下文的父工厂实例:

protected DefaultListableBeanFactory createBeanFactory() {
    return new DefaultListableBeanFactory(getInternalParentBeanFactory());
}
protected BeanFactory getInternalParentBeanFactory() {
    return (getParent() instanceof ConfigurableApplicationContext ?
            ((ConfigurableApplicationContext) getParent()).getBeanFactory() : getParent());
}

然后获得 DefaultListableBeanFactory 实例后,再在通过 customizeBeanFactory 方法同步一些参数:

protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
    if (this.allowBeanDefinitionOverriding != null) {
        beanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
    }
    if (this.allowCircularReferences != null) {
        beanFactory.setAllowCircularReferences(this.allowCircularReferences);
    }
}

最后通过 loadBeanDefinitions 将 bean 的信息重新加载到新的工厂中。这里需要注意的是,在 AbstractApplicationContextloadBeanDefinitions 是个抽象方法,根据子类的不同会有不同的实现,我们以比较熟悉的 XmlWebApplicationContext 为例:

@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
    // 配置一个xml配置文件bean配置信息阅读器
    XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
    beanDefinitionReader.setEnvironment(getEnvironment());
    beanDefinitionReader.setResourceLoader(this);
    beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
    initBeanDefinitionReader(beanDefinitionReader);
    // 使用阅读器加载配置文件,并将其解析为对应的bean信息
    loadBeanDefinitions(beanDefinitionReader);
}

protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws IOException {
    String[] configLocations = getConfigLocations();
    if (configLocations != null) {
        for (String configLocation : configLocations) {
            reader.loadBeanDefinitions(configLocation);
        }
    }
}

这里的 Reader 与上下文的类型对应,或者说,上下文能读取的配置就取决于 Reader 的类型,不同类型的上下文有不同的 Reader,比如 AnnotationConfigApplicationContext 中使用的就是 AnnotatedBeanDefinitionReader

这部分具体的解析流程会另外开一篇文章分析,这里暂且知道是重新加载配置文件,获取 bean 定义信息就行。

至此,旧的 BeanFactory 已经被新的 BeanFactory 替换,配置和数据都已经转移到新的 BeanFactory 中。

三、对工厂进行预处理

现在,经过 refreshBeanFactory 方法的处理,旧的工厂被销毁,新的 BeanFactory 也已经准备好了。接着,上下文需要通过 prepareBeanFactory 方法对这个新的工厂再进行初始化:

protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
    // 配置类加载器,SpEL表达式解析器与属性注册器
    beanFactory.setBeanClassLoader(getClassLoader());
    beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
    beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));

    // 添加后置处理器
    beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
    
    // 下述接口的实现类不需要自动装配
    beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
    beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);
    beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
    beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
    beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
    beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);

    // 直接把下述实例注册到工厂中
    beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
    beanFactory.registerResolvableDependency(ResourceLoader.class, this);
    beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
    beanFactory.registerResolvableDependency(ApplicationContext.class, this);

    // 添加默认的后置处理器ApplicationListenerDetector
    // 实现了ApplicationListener接口的bean会被记录在上下文的applicationListeners列表
    // 并且指定方法调用的时候会广播事件
    beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));

    // 如果存在名为LoadTimeWeaver的bean,说明需要添加后置处理器LoadTimeWeaverAwareProcessor
    // 被实现LoadTimeWeaverAware类设置LoadTimeWeaver实例
    if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
        beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
        beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
    }

    // 若存在environment,systemProperties与SystemEnvironment这三个bean,则默认注册到容器
    if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) {
        beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());
    }
    if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) {
        beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties());
    }
    if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) {
        beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment());
    }
}

这个方法主要用于为 BeanFactory 添加默认的后置处理器,与一些默认的 bean 实例。

设置默认Bean为已解析

需要注意的是,正常情况下,我们通过容器获取到的 bean 都是根据 BeanDefinition 创建的,而在这里注册的四个 bean :

  • ResourceLoader
  • BeanFactory
  • ApplicationEventPublisher
  • ApplicationContext

这四个类要么是容器本身,要么是容器本身持有的成员变量,因此都是没有对应 BeanDefinition 的。

它在这一步会被直接设置为已经完成解析。

后处理器

这里默认注册了 ApplicationContextAwareProcessor 这个 Bean 后处理器,当它调用 postProcessBeforeInitialization 方法时,会再去调用 6 个 Aware 接口:

  • EnvironmentAware
  • EmbeddedValueResolverAware
  • ResourceLoaderAware
  • ApplicationEventPublisherAware
  • MessageSourceAware
  • ApplicationContextAware

也就说,这 6 个 Aware 接口会在 Bean 初始化前被调用。

此外,还注册了 ApplicationListenerDetector 这个后处理,该后处理器会在 Bean 初始化前被调用,然后实现了 ApplicationEventListener 接口的 Bean 就会被注册到广播器中。

总结

BeanFactory 的初始化共分为三个方法,对应三个主要过程:

  1. prepareRefresh:初始化上下文的属性以及一些状态;
  2. obtainFreshBeanFactory:销毁上下文中的旧 BeanFactory,然后创建新的 BeanFactory 对象;
  3. prepareBeanFactory:为新 BeanFactory 配置一些基本的参数,包括注册一些默认 Bean、Bean 作用域以及一些后置处理器,以及根据配置文件加载 BeanDefinition

image-20220925135655804

posted @ 2022-08-14 15:31  Createsequence  阅读(1103)  评论(0编辑  收藏  举报