美文网首页
SpringBoot2.2.6启动run方法之prepareCo

SpringBoot2.2.6启动run方法之prepareCo

作者: 噼咔丘 | 来源:发表于2020-06-02 15:36 被阅读0次

    SpringBoot2.2.6启动run方法之prepareContext

    前言

    此系列是针对springboot的启动,旨在于和大家一起来看看springboot启动的过程中到底做了一些什么事。文中有不清楚或错误的地方
    欢迎留言指正。

    源码解读进度

    首先我们的源码阅读进度

    public ConfigurableApplicationContext run(String... args) {
        // 用于记录启动时间
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        // 声明Spring上下文
        ConfigurableApplicationContext context = null;
        // 声明启动错误回掉
        Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
        // 设置jdk系统属性java.awt.headless,默认情况为true即开启
        configureHeadlessProperty();
        // 装饰者模式创建启动监听器(EventPublishingRunListener实例)
        SpringApplicationRunListeners listeners = getRunListeners(args);
        // 触发ApplicationStartingEvent事件,包括转换器的初始化
        listeners.starting();
        try {
            // 参数封装,也就是在命令行下启动应用带的参数
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
            // 准备环境:1、加载外部化配置的资源到environment;2、触发ApplicationEnvironmentPreparedEvent事件
            ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
            // 配置spring.beaninfo.ignore,并添加到名叫systemProperties的PropertySource中;默认为true即开启
            configureIgnoreBeanInfo(environment);
            // 打印banner图
            Banner printedBanner = printBanner(environment);
            // 创建应用上下文
            context = createApplicationContext();
            // 创建异常报告对象,报告异常原因
            exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
                    new Class[] { ConfigurableApplicationContext.class }, context);
            // 初始化上下文,本文重点
            prepareContext(context, environment, listeners, applicationArguments, printedBanner);
            refreshContext(context);
            afterRefresh(context, applicationArguments);
            stopWatch.stop();
            if (this.logStartupInfo) {
                new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
            }
            listeners.started(context);
            callRunners(context, applicationArguments);
        }
        catch (Throwable ex) {
            handleRunFailure(context, ex, exceptionReporters, listeners);
            throw new IllegalStateException(ex);
        }
        try {
            listeners.running(context);
        }
        catch (Throwable ex) {
            handleRunFailure(context, ex, exceptionReporters, null);
            throw new IllegalStateException(ex);
        }
        return context;
    }
    

    prepareContext做了哪些事情

    首先看代码,我们会在代码上做相应的注释,后面需要详细分析的,前面都会标注序号

    private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
                SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
        // 给上下文对象设置环境对象,给AnnotatedBeanDefinitionReader和ClassPathBeanDefinitionScanner设置环境对象
        context.setEnvironment(environment);
        // 1. 上下文后置处理,包括向BeanFactory中注册BeanNameGenerator和ConversionService
        postProcessApplicationContext(context);
        // 2. SpringApplication构造器中初始化了各种ApplicationContextInitializer,循环调用他们的initialize方法
        applyInitializers(context);
        // 3. 发送ApplicationContextInitializedEvent事件
        listeners.contextPrepared(context);
        if (this.logStartupInfo) {
            // 打印启动信息,包括pid,用户等
            logStartupInfo(context.getParent() == null);
            // 答应profile信息
            logStartupProfileInfo(context);
        }
        // Add boot specific singleton beans
        ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
        // 将ApplicationArguments注册到容器中
        beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
        if (printedBanner != null) {
            // 将Banner注册到容器中
            beanFactory.registerSingleton("springBootBanner", printedBanner);
        }
        if (beanFactory instanceof DefaultListableBeanFactory) {
            // 设置不允许定义同名的BeanDefinition,重复注册时抛出异常
            ((DefaultListableBeanFactory) beanFactory)
                    .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
        }
        if (this.lazyInitialization) {
            // 如果懒加载,添加懒加载后置处理器
            context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
        }
        // Load the sources
        Set<Object> sources = getAllSources();
        Assert.notEmpty(sources, "Sources must not be empty");
        load(context, sources.toArray(new Object[0]));
        // 发送ApplicationPreparedEvent事件
        listeners.contextLoaded(context);
    }
    

    下面详细分析一下重要的几个步骤

      1. postProcessApplicationContext
    protected void postProcessApplicationContext(ConfigurableApplicationContext context) {
          // BeanNameGenerator是生成bean名字的接口,用户可以自己实现。用户没有设置自己的BeanNameGenerator的时候
          // AnnotatedBeanDefinitionReader和ClassPathBeanDefinitionScanner的默认实现是AnnotationBeanNameGenerator
          // AnnotationBeanNameGenerator生成名称规则是判断注解中的value是否有值,没有的话使用类名首字母小写作为bean名
          // 如果用户自定义了beanNameGenerator,prepareContext方法里面的load(context, sources.toArray(new Object[0]));这一步会设置
        if (this.beanNameGenerator != null) {
              // 默认情况下不会走到这一步,将用户自定义的beanNameGenerator注册到beanfactory中
            context.getBeanFactory().registerSingleton(AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR,
                    this.beanNameGenerator);
        }
        // 默认不走这一步
        // 如果用户设置了自己的resourceLoader,会设置resourceLoader到GenericApplicationContext(SpringBoot默认使用环境)
        // 然后看一下GenericApplicationContext的getRoesource方法,
        // @Override
        // public Resource getResource(String location) {
          //      if (this.resourceLoader != null) {
        //        return this.resourceLoader.getResource(location);
        //    }
        //    return super.getResource(location);
        // }
          // 看到如果设置了resourceLoader就调用resourceLoader的getResource,否则调用
        // GenericApplicationContext的父类DefaultResourceLoader的getResource方法
        if (this.resourceLoader != null) {
            if (context instanceof GenericApplicationContext) {
                ((GenericApplicationContext) context).setResourceLoader(this.resourceLoader);
            }
            if (context instanceof DefaultResourceLoader) {
                ((DefaultResourceLoader) context).setClassLoader(this.resourceLoader.getClassLoader());
            }
        }
        // 默认会走该方法
        // 上文中我们分析createApplicationContext的时候,说过构造器中初始化了DefaultListableBeanFactory实例对象
        // 所以这里context.getBeanFactory()拿到的就是DefaultListableBeanFactory实例
        // setConversionService设置个各种转换器,比如:调用接口时传了一个参数为age=“18”,
        // 接口接收参数为Integer类型,转换器StringToNumberConverterFactory就会将String转换为Integer
        if (this.addConversionService) {
            context.getBeanFactory().setConversionService(ApplicationConversionService.getSharedInstance());
        }
    }
    
      1. applyInitializers
    protected void applyInitializers(ConfigurableApplicationContext context) {
            // SpringApplication构造器中初始化了各种ApplicationContextInitializer,getInitializers()方法把他们排序并放到Set中
            // 1. DelegatingApplicationContextInitializer 从environment中获取context.initializer.classes属性,默认为null。
            // 如果配置了数据并且是一个逗号分隔的列表,分别判断是不是ApplicationContextInitializer的实现,然后调用initialize方法.
            // 2. SharedMetadataReaderFactoryContextInitializer  向beanFactoryPostProcessors(beanFactory后置处理器)中
            // 添加一个CachingMetadataReaderFactoryPostProcessor。该后置处理器会在后面讲到的refreshContext方法中会执行,
            // 在执行方法中向beanFactory中注册一个 BeanDefinition(SharedMetadataReaderFactoryBean)
            // 然后向internalConfigurationAnnotationProcessor的BeanDefinition中注册一个RuntimeBeanReference,在Bean实例化的
            // 时候设置metadataReaderFactory为SharedMetadataReaderFactoryBean
            // 3. ContextIdApplicationContextInitializer 设置ContextId,通过spring.application.name设置
            // 4. ConfigurationWarningsApplicationContextInitializer 向beanFactoryPostProcessors(beanFactory后置处理器)中
            // 添加一个ConfigurationWarningsPostProcessor,作用是添加一下检查。默认有一个ComponentScanPackageCheck,作用是检查@ComponentScan
            // 扫描的包路径是否合法
            // 5. ServerPortInfoApplicationContextInitializer 添加一个ApplicationListener。监听WebServerInitializedEvent事件,
            // 向Environment中添加端口号local.sever.port
            // 6. ConditionEvaluationReportLoggingListener 监听ContextRefreshedEvent和ApplicationFailedEvent事件,并做日志
        for (ApplicationContextInitializer initializer : getInitializers()) {
            Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(initializer.getClass(),
                    ApplicationContextInitializer.class);
            Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
            initializer.initialize(context);
        }
    }
    

    相关文章

      网友评论

          本文标题:SpringBoot2.2.6启动run方法之prepareCo

          本文链接:https://www.haomeiwen.com/subject/yniuzhtx.html