SpringBoot原理发现(三)

 

说明:

本系列基于SpringBoot 2.2.9.RELEASE 版本,对SpringBoot的原理进行分析,一共分为四节:

SpringBoot原理发现(一):创建Hello World,对pom依赖以及@SpringBootApplication注解进行分析

SpringBoot原理发现(二):分析SpringBoot自动配置原理

SpringBoot原理发现(三):通过主配置类main方法分析SpringBoot启动配置原理

SpringBoot原理发现(四):了解SpringBoot启动中的几个重要回调机制
 

SpringBoot 启动配置原理

@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }

启动运行流程分析

  debug上面主配置类的run方法,它会执行到如下代码:

 由此可见,主配置类run方法分为两个部分。

第一步 :new SpringApplication(primarySources),创建出SrpingApplication对象

第二步 :调用返回对象SpringApplication.run(args)

  

1. new SpringApplication(primarySources)

  跟踪new SpringApplication(primarySources),最终会执行SpringApplication的构造方法,如下:

   根据方法形参可以看出配置类其实可以传入多个,构造函数执行流程解释如下:

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
    this.resourceLoader = resourceLoader;
    
    //检查主要来源类是否为空,此时就是我们传入的主配置类:class com.example.demo.DemoApplication
    Assert.notNull(primarySources, "PrimarySources must not be null");
    
    //将主要来源类保存到primarySources中,primarySources:Set<Class<?>> primarySources
    this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
    
    //判断当前应用的类型,返回WebApplicationType.SERVLET,代表是一个web应用
    this.webApplicationType = WebApplicationType.deduceFromClasspath();
    
    /**
     * 查找类路径下META-INF/spring.factories 文件中定义的ApplicationContextInitializer的值列表并保存在initializers属性中
     *   
     * 其实和 Springboot原理探究(一)
     *        2.2.2 @Import(AutoConfigurationImportSelector.class) 同理
     *  只要是getSpringFactoriesInstances()就是去查找类路径下META-INF/spring.factories文件指定的值列表
     */
    setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
    
    //同上面一样,查找类路径下META-INF/spring.factories 文件中定义的ApplicationListener的值列表并保存在listeners属性中
    setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    
    //查找配置类中有main方法的主配置类
    this.mainApplicationClass = deduceMainApplicationClass();
}

 META-INF/spring.factories

# Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener
# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.autoconfigure.BackgroundPreinitializer

至此new SpringApplication(primarySources)执行完毕,并创建出SpringApplication对象 ,可以看出此步骤主要是将  ApplicationContextInitializer  和  ApplicationListener 保存起来

 

 2. springApplication.run(args)

  调用的run方法解释如下:

/**
 * Run the Spring application, creating and refreshing a new
 * {@link ApplicationContext}.
 * @param args the application arguments (usually passed from a Java main method)
 * @return a running {@link ApplicationContext}
 */
public ConfigurableApplicationContext run(String... args) {

    //StopWatch开始监听,设置IOC容器属性null
    StopWatch stopWatch = new StopWatch();
    stopWatch.start();
    ConfigurableApplicationContext context = null;
    Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
    
    //AWT应用,忽略
    configureHeadlessProperty();
    
    //获取类路径下META-INF/spring.factories 文件中SpringApplicationRunListener定义的值,参见下面 2.1
    SpringApplicationRunListeners listeners = getRunListeners(args);
    
    //回调所有SpringApplicationRunListener的starting(),可以查看SpringApplicationRunListener监听器中定义的几个回调方法 ,参见下面 2.2
    listeners.starting();
    
    try {
        //将args封装成ApplicationArguments对象
        ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
        
        //准备环境,并回调SpringApplicationRunListener的environmentPrepared方法,参见下面 2.3
        ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
        configureIgnoreBeanInfo(environment);
        
        //打印Spring Banner图
        Banner printedBanner = printBanner(environment);
        
        //通过BeanUtils反射创建IOC容器,此处会根据this.webApplicationType判断是否为web环境,而this.webApplicationType已经在第一大步骤中得到
        context = createApplicationContext();
        
        exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
                new Class[] { ConfigurableApplicationContext.class }, context);
                
        /**  准备上下文:将environment保存在IOC容器中,
      *    并 回调所有的ApplicationContextInitializer的initialize方法, ApplicationContextInitializer则是在第一大步骤中已经获取且保存了
* 回调所有SpringApplicationRunListenercontextPrepared方法, * 回调所有SpringApplicationRunListenercontextLoaded方法
      * 参见下面 2.4
      */ prepareContext(context, environment, listeners, applicationArguments, printedBanner);
//刷新容器:初始化IOC,初始化所有的组件,如果是web应用此处还会创建嵌入式的tomcat refreshContext(context); //2.2.9.RELEASE版本的spingboot这就是一个空方法,之前的版本中会调用callRunners方法 afterRefresh(context, applicationArguments); //StopWatch停止监听 stopWatch.stop(); if (this.logStartupInfo) { new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch); } //调用所有SpringApplicationRunListenerstarted方法 listeners.started(context); //从IOC容器中获取ApplicationRunner 和 CommandLineRunner, SpringBoot中比较重要的几个回调机制
     //先回调ApplicationRunner的run在回调CommandLineRunner
callRunners(context, applicationArguments); } catch (Throwable ex) { handleRunFailure(context, ex, exceptionReporters, listeners); throw new IllegalStateException(ex); } try { //回调所有SpringApplicationRunListenerrunning方法 listeners.running(context); } catch (Throwable ex) { handleRunFailure(context, ex, exceptionReporters, null); throw new IllegalStateException(ex); } //返回IOC容器 return context; }

 

2.1 SpringApplicationRunListener 监听器

  SpringBoot中比较重要的回调机制之一

  可以看出又是getSpringFactoriesInstances方法,只要是getSpringFactoriesInstances()就是去查找类路径下META-INF/spring.factories文件指定的值列表,而这里就是去获取SpringApplicationRunListener的值列表

private SpringApplicationRunListeners getRunListeners(String[] args) {
    Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
    return new SpringApplicationRunListeners(logger,
            getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
}

 

2.2 SpringApplicationRunListener监听器

  回调SpringApplicationRunListener starting方法

void starting() {
        for (SpringApplicationRunListener listener : this.listeners) {
            listener.starting();
        }
    }

 

2.3 prepareEnvironment(listeners, applicationArguments) 环境准备

  回调SpringApplicationRunListener 的 environmentPrepared方法

private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
            ApplicationArguments applicationArguments) {
    // 创建环境
    ConfigurableEnvironment environment = getOrCreateEnvironment();
    configureEnvironment(environment, applicationArguments.getSourceArgs());
    ConfigurationPropertySources.attach(environment);
    
    //回调SpringApplicationRunListener的environmentPrepared方法,环境准备完成
    listeners.environmentPrepared(environment);
    
    bindToSpringApplication(environment);
    if (!this.isCustomEnvironment) {
        environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
                deduceEnvironmentClass());
    }
    ConfigurationPropertySources.attach(environment);
    return environment;
}

 2.4 准备上下文

  回调之前已经保存的所有ApplicationContextInitializerinitialize方法
  回调所有SpringApplicationRunListenercontextPrepared方法
  回调所有SpringApplicationRunListenercontextLoaded方法

private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
            SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
    context.setEnvironment(environment);
    postProcessApplicationContext(context);
    
    //回调所有的ApplicationContextInitializer的initialize方法,而ApplicationContextInitializer就是之前创建SpringApplication时保存的
    applyInitializers(context);
    
    //回调所有SpringApplicationRunListener的contextPrepared方法
    listeners.contextPrepared(context);
    
    //日志记录
    if (this.logStartupInfo) {
        logStartupInfo(context.getParent() == null);
        logStartupProfileInfo(context);
    }
    
    ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
    beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
    if (printedBanner != null) {
        beanFactory.registerSingleton("springBootBanner", printedBanner);
    }
    if (beanFactory instanceof DefaultListableBeanFactory) {
        ((DefaultListableBeanFactory) beanFactory)
                .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
    }
    if (this.lazyInitialization) {
        context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
    }
    
    Set<Object> sources = getAllSources();
    Assert.notEmpty(sources, "Sources must not be empty");
    load(context, sources.toArray(new Object[0]));
    
    //回调所有SpringApplicationRunListener的contextLoaded方法
    listeners.contextLoaded(context);
}

 至此springApplication.run(args)执行完毕,可以看出第二大步骤就是环境准备,初始化IOC容器,扫描Bean创建组件,并调用几个重要的回调机制。

 

SpringBoot中四个重要的回调机制

  1. ApplicationContextInitializer

    在第一大步骤中扫描META-INF/spring.factories 文件得到,在第二大步骤中回调其initialize方法

  2. SpringApplicationRunListener

    在第二大步骤中扫描META-INF/spring.factories 文件得到,并依次回调

        a. starting()
        b. environmentPrepared()
        c. contextPrepared()
        d. contextLoaded()
        e. started()
        f. running()

  3. ApplicationRunner

    在第二大步骤中,通过IOC容器获取,且回调run方法

  4. CommandLineRunner

    在第二大步骤中,通过IOC容器获取,且回调run方法

 

posted @ 2020-11-02 14:02  swayer  阅读(147)  评论(0编辑  收藏  举报