springboot启动流程(六)ioc容器refresh过程(上篇)

所有文章

https://www.cnblogs.com/lay2017/p/11478237.html

正文

在前面的几篇文章中,我们看到Environment创建、application配置文件的加载、ApplicationContext实例对象的创建、以及主类加载成为BeanDefinition。做了这么多的准备,终于到了核心的部分,也就是ioc容器的刷新。

这里,我们不免要再次回顾一下SpringAplication的run方法

public ConfigurableApplicationContext run(String... args) {
    // 声明一个Context容器
    ConfigurableApplicationContext context = null;
    // 获取监听器
    SpringApplicationRunListeners listeners = getRunListeners(args);
    // 调用监听器的启动
    listeners.starting();

    try {
        // 创建并配置Environment(这个过程会加载application配置文件)
        ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
        // 根据应用类型创建对应的Context容器
        context = createApplicationContext();

        // 刷新Context容器之前的准备
        prepareContext(context, environment, listeners, applicationArguments, printedBanner);
        // 刷新Context容器
        refreshContext(context);
        // 刷新Context容器之后处理
        afterRefresh(context, applicationArguments);

        // Context容器refresh完毕发布
        listeners.started(context);

        // 触发Context容器refresh完以后的执行
        callRunners(context, applicationArguments);
    } catch (Throwable ex) {}

    try {
        // Context启动完毕,Runner运行完毕发布
        listeners.running(context);
    } catch (Throwable ex) {}

    return context;
}

run方法中,prepareContext和afterRefresh之间的refreshContext方法正是ioc容器刷新的入口方法。

ApplicationContext和BeanFactory

但是在阅读refreshContext方法之前,我们得先区分一下ApplicationContext和BeanFactory两者之间的关系。在之前的文章中,我们并没有把二者进行区分。比如,我们总是说"把Bean注册到ApplicationContext容器"。

在我们的理解中,容器应该是一个空间的概念,用于存放事物的东西。在spring中,存放的是Bean。而BeanFactory提供了这么一个空间用于存放Bean,所以BeanFactory才是Bean所在的主要容器,而不是我们一直说的ApplicationContext。

既然ApplicationContext不是容器,那它又是啥呢?我们称之为"上下文"。"上下文"的概念我们也许不见得那么熟,但是"场景","场所"这样的概念我们应该就比较熟悉了。比如说"拍摄场景","交易场所"等。它们的共同点都是事件发生的地方。所以ApplicationContext正是spring定义的应用程序的事件发生场所,也就是所谓的应用上下文。

上面,我了解了BeanFactory作为Bean容器,而ApplicationContext作为上下文。那么Bean容器和上下文之间是什么关系呢?我们可以看一个代码片段

// 通用的应用上下文实现
public class GenericApplicationContext extends AbstractApplicationContext implements BeanDefinitionRegistry {
        // 默认BeanFactory的实现
    private final DefaultListableBeanFactory beanFactory;

    // 省略
}

我们看到BeanFactory是被组合在ApplicationContext当中的,所以它们的其中一种关系就是组合关系。也就是说应用上下文中包含着Bean工厂。

接着,我们分别看看ApplicationContext的类图

我们看到ApplicationContext和BeanFactory还存在着继承关系,这意味着ApplicationContext可以对外被当做BeanFactory来使用,这也是为什么我们总是把ApplicationContext当做容器来看的主要原因,因为对外来看两者是一体的。

结合上面的组合关系,我们可以知道对内的话ApplicationContext的BeanFactory相关实现会由内部组合的BeanFactory的实现类来完成具体工作。

到这里,我们基本就明白了ApplicationContext和BeanFactory之间的关系有两种:组合、继承。

后面我们将称呼ApplicationContext为上下文,而BeanFactory为Bean容器,进行区分。

上下文组合Bean工厂

那么BeanFactory是什么时候被组合到ApplicationContext当中的呢?我们先看看ApplicationContext的实现类的类图

注意!springboot默认的servlet项目的ApplicationContext实现类是AnnotationCofnigServletWebServerApplicationContext,所以我们会以它作为实现类来看

我们自下而上,顺着继承链找到GenericApplicationContext我们就会看到之前出现过的代码片段

public class GenericApplicationContext extends AbstractApplicationContext implements BeanDefinitionRegistry {

    private final DefaultListableBeanFactory beanFactory;

    public GenericApplicationContext() {
        this.beanFactory = new DefaultListableBeanFactory();
    }

    // 省略
}

这里,在GenericApplicationContext的构造方法当中构建了一个DefaultListableBeanFactory的实例对象。DefaultListableBeanFactory也就是BeanFactory的默认实现,那么也就是说在构造ApplicationContext实例对象的时候创建并组合了一个BeanFactory的实例。

我们顺便也看看DefaultListableBeanFactory的继承关系吧

这个类图只保留了BeanFactory的东西,设计路线自BeanFactory到DefaultListableBeanFactory也很清晰。

refreshContext刷新过程

下面,我们将正式进行refreshContext方法的阅读。打开refreshContext方法

private void refreshContext(ConfigurableApplicationContext context) {
    refresh(context);
    if (this.registerShutdownHook) {
        try {
            context.registerShutdownHook();
        } catch (AccessControlException ex) {
            // Not allowed in some environments.
        }
    }
}

跟进refresh方法

protected void refresh(ApplicationContext applicationContext) {
    Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
    ((AbstractApplicationContext) applicationContext).refresh();
}

我们看到,这里调用的是AbstractApplicationContext的refresh方法,顺序AnnotationConfigServletWebServerApplicationContext的继承链向上可以找到AbstractApplicationContext。

我们继续跟进AbstractApplicationContext的refresh方法,refresh方法有点长

public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
        // 刷新前准备,设置flag、时间,初始化properties等
        prepareRefresh();

        // 获取ApplicationContext中组合的BeanFactory
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

        // 设置类加载器,添加后置处理器等准备
        prepareBeanFactory(beanFactory);

        try {
            // 供子类实现的后置处理
            postProcessBeanFactory(beanFactory);

            // 调用Bean工厂的后置处理器
            invokeBeanFactoryPostProcessors(beanFactory);

            // 注册Bean的后置处理器
            registerBeanPostProcessors(beanFactory);

            // 初始化消息源
            initMessageSource();

            // 初始化事件广播
            initApplicationEventMulticaster();

            // 供之类实现的,初始化特殊的Bean
            onRefresh();

            // 注册监听器
            registerListeners();

            // 实例化所有的(non-lazy-init)单例Bean
            finishBeanFactoryInitialization(beanFactory);

            // 发布刷新完毕事件
            finishRefresh();
        }

        catch (BeansException ex) {
            //
        } finally {
            //
        }
    }
}

上一篇文章中,我们提到了这么一个初始化过程:

annotation或者xml中Bean的配置 --> 内存中的BeanDefinition --> Bean

也就是实际的配置,转化成内存中的配置对象,再根据配置对象转化成具体的实例对象。说白了就是从元数据到实例的一个转化过程。

为什么会提及这么一个转化过程呢?因为我们的refresh过程主要包含的就是其中的一步,也就是从annotation或者xml的Bean配置 --> 内存中的BeanDefinition的过程。这个过程的实现在调用Bean工厂的后置处理器的时候完成,也就是invokeBeanFactoryPostProcessors方法

我们跟进invokeBeanFactoryPostProcessors方法

protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
    PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
    //
}

这里先获取了所有后置处理器,然后调用处理。再跟进PostProcessorRegistrationDelegate的invokeBeanFactoryFactoryPostProcessors方法

该方法很长,我们删减掉大部分内容

public static void invokeBeanFactoryPostProcessors(
        ConfigurableListableBeanFactory beanFactory,
        List<BeanFactoryPostProcessor> beanFactoryPostProcessors
        ) {
    //
    if (beanFactory instanceof BeanDefinitionRegistry) {
        //
        while (reiterate) {
            // 调用BeanDefinition注册的后置处理器
            invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
            //
        }
        //
    } else {
        //
    }
    //
}

可以看到,调用后置处理器的时候会调用到注册BeanDefinition的后置处理器。也就是从这里开始作为BeanDefinition的注册入口

跟进invokeBeanDefinitionRegistryPostProcessors

private static void invokeBeanDefinitionRegistryPostProcessors(
            Collection<? extends BeanDefinitionRegistryPostProcessor> postProcessors,
            BeanDefinitionRegistry registry
            ) {
    for (BeanDefinitionRegistryPostProcessor postProcessor : postProcessors) {
        postProcessor.postProcessBeanDefinitionRegistry(registry);
    }
}

这里我们需要断点一下,看看有哪些后置处理器处理BeanDefinition注册

我们看到了ConfigurationClassPostProcessor也就是它完成BeanDefinition注册这项工作的

我们跟进ConfigurationClassPostProcessor的postProcessBeanDefinitionRegistry方法

public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
    // 省略
    processConfigBeanDefinitions(registry);
}

继续跟进,我们看到processConfigBeanDefinitions方法挺长的,进行了大量的缩减

public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
    List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
    String[] candidateNames = registry.getBeanDefinitionNames();

    for (String beanName : candidateNames) {
        BeanDefinition beanDef = registry.getBeanDefinition(beanName);
        if (ConfigurationClassUtils.isFullConfigurationClass(beanDef) ||
                ConfigurationClassUtils.isLiteConfigurationClass(beanDef)) {
            //
        } else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
            // 默认仅有主类被添加
            configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
        }
    }

    //

    // 解析被 @Configuration 注解的类
    ConfigurationClassParser parser = new ConfigurationClassParser(
            this.metadataReaderFactory,
            this.problemReporter,
            this.environment,
            this.resourceLoader,
            this.componentScanBeanNameGenerator,
            registry);

    Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
    //
    do {
        // 解析的核心方法
        parser.parse(candidates);
        parser.validate();

        // 

        candidates.clear();
        //
    } while (!candidates.isEmpty());
    //
}

上一篇文章中,也就是在prepareContext方法的核心逻辑里,main方法所在的主类将会被作为BeanDefinition加载到BeanFactory当中。而在这里,该主类将被作为一个配置类被解析,解析器即ConfigurationClassParser。

我们跟进parse方法看看

public void parse(Set<BeanDefinitionHolder> configCandidates) {
    for (BeanDefinitionHolder holder : configCandidates) {
        BeanDefinition bd = holder.getBeanDefinition();
        try {
            if (bd instanceof AnnotatedBeanDefinition) {
                // 主类的解析将从这里进入
                parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
            } else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
                parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
            } else {
                parse(bd.getBeanClassName(), holder.getBeanName());
            }
        } catch (BeanDefinitionStoreException ex) {}
          catch (Throwable ex) {}
    }

    this.deferredImportSelectorHandler.process();
}

继续跟进parse方法

protected final void parse(AnnotationMetadata metadata, String beanName) throws IOException {
    processConfigurationClass(new ConfigurationClass(metadata, beanName));
}

可以看到主类作为配置类的解析过程将从processConfigurationClass这里开始

总结

到这里,ioc容器的refresh过程先做一个小结。我们知道了上下文和Bean容器是继承关系又是组合关系。refreshContext的核心就是为了加载BeanDefinition,而加载BeanDefinition将从main方法所在的主类开始,主类作为一个配置类将由ConfigurationClassParser解析器来完成解析的职责。下一篇文章,我们将会看到从主类中解析出BeanDefinition的主要逻辑。

原文地址:https://www.cnblogs.com/lay2017/p/11494581.html

时间: 2024-08-01 20:23:18

springboot启动流程(六)ioc容器refresh过程(上篇)的相关文章

springboot启动流程(八)ioc容器refresh过程(下篇)

所有文章 https://www.cnblogs.com/lay2017/p/11478237.html 正文 上一篇文章,我们知道了解析过程将从解析main方法所在的主类开始.在文章的最后我们稍微看了一下ConfigurationClassParser这个解析器的parse方法 protected final void parse(AnnotationMetadata metadata, String beanName) throws IOException { processConfigur

SpringBoot启动流程分析(四):IoC容器的初始化过程

SpringBoot系列文章简介 SpringBoot源码阅读辅助篇: Spring IoC容器与应用上下文的设计与实现 SpringBoot启动流程源码分析: SpringBoot启动流程分析(一):SpringApplication类初始化过程 SpringBoot启动流程分析(二):SpringApplication的run方法 SpringBoot启动流程分析(三):SpringApplication的run方法之prepareContext()方法 SpringBoot启动流程分析(四

SpringBoot启动流程分析(五):SpringBoot自动装配原理实现

SpringBoot系列文章简介 SpringBoot源码阅读辅助篇: Spring IoC容器与应用上下文的设计与实现 SpringBoot启动流程源码分析: SpringBoot启动流程分析(一):SpringApplication类初始化过程 SpringBoot启动流程分析(二):SpringApplication的run方法 SpringBoot启动流程分析(三):SpringApplication的run方法之prepareContext()方法 SpringBoot启动流程分析(四

SpringBoot 启动流程

SpringBoot 启动流程 加载 resources/META-INF/spring.factories 中配置的 ApplicationContextInitializer 和 ApplicationListener. /** * 加载在框架内部使用的各种通用工厂 bean. * spring.factories 文件必须满足 Properties 文件格式,属性的 key 是接口或抽象类的全限定类名, * value 是一组由逗号分隔的实现类全类名. */ public final cl

springboot启动流程(一)构造SpringApplication实例对象

所有文章 https://www.cnblogs.com/lay2017/p/11478237.html 启动入口 本文是springboot启动流程的第一篇,涉及的内容是SpringApplication这个对象的实例化过程.为什么从SpringApplication这个对象说起呢?我们先看一段很熟悉的代码片段 @SpringBootApplication public class SpringBootLearnApplication { public static void main(Str

springboot启动流程(九)ioc依赖注入

所有文章 https://www.cnblogs.com/lay2017/p/11478237.html 正文 在前面的几篇文章中,我们多次提到这么一个转化过程: Bean配置 --> BeanDefinition --> Bean对象 Bean的配置可以是xml配置,也可以是java配置.BeanDefinition配置在内存中数据对象,也是Bean的元数据.在springboot启动过程当中,refresh上下文这个步骤将会解析xml配置以及java配置,从而把Bean的配置解析成为Bea

spring的启动过程就是创建ioc容器的过程

1. spring简介 spring的最基本的功能就是创建对象及管理这些对象之间的依赖关系,实现低耦合.高内聚.还提供像通用日志记录.性能统计.安全控制.异常处理等面向切面的能力,还能帮我们管理最头疼的数据库事务,本身提供了一套简单的JDBC访问实现,提供与 第三方数据访问框架集成(如hibernate.JPA),与各种Java EE技术整合(如JavaMail.任务调度等等),提供一套自己的web层框架Spring MVC.而且还能非常简单的与第三方web框架集成.从这里我们可以认为Sprin

springboot启动流程简析

Spring Boot可以轻松创建独立的,生产级的基于Spring的应用程序,而这只需要很少的一些Spring配置.本文将从SpringBoot的启动流程角度简要的分析SpringBoot启动过程中主要做了哪些事情. 说明: springboot 2.0.6.RELEASE SpringBoot启动简要流程图 附原始大图链接 启动流程概述 启动流程从角度来看,主要分两个步骤.第一个步骤是构造一个SpringApplication应用,第二个步骤是调用它的run方法,启动应用. 1 构造Sprin

Spring之IOC容器初始化过程

Ioc容器的初始化是由refresh()方法来启动的,这个方法标志着Ioc容器的正式启动. 具体来说这个启动过程包括三个基本过程: 1.BeanDifinition的Resource定位 2.BeanDifinition的载入与解析 3.BeanDifinition在Ioc容器中的注册 需要注意的是,Spring把这三个过程分开,并使用不同的模块来完成,如使用相应的ResourceLoader.BeanDifinitionReader等模块,通过这样的实际方式,可以让用户更加灵活的对这三个过程进