Spring Boot 启动生命周期分析,每个组件的执行时序,扩展点分析等【建议收藏】(持续更新,见到一个分析一个)

452 阅读11分钟

文章目录

Spring Boot 启动核心

众所周知的SpringApplication.run(xxx.class, args);

SpringApplication启动

启动入口

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

SpringApplication.run

	public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
		return run(new Class<?>[] { primarySource }, args);
	}

SpringApplication构造函数

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
     // 设置资源加载器
     this.resourceLoader = resourceLoader; 
     // 设置主要资源类
     this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
     // 推断当前应用的类型
     this.webApplicationType = WebApplicationType.deduceFromClasspath();
     // 设置ApplicationContext的初始化器
    setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
    // 设置Application监听器
    setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    // 推断并设置主类
    this.mainApplicationClass = deduceMainApplicationClass();
}

deduceFromClasspathpuan推断当前应用的类型

static WebApplicationType deduceFromClasspath() {
     // 类路径中是否包含DispatcherHandler,且不包含DispatcherServlet,也不包含ServletContainer,那么是reactive应用
     if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
             && !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
         return WebApplicationType.REACTIVE;
     }
     // 如果Servlet或者ConfigurableWebApplicationContext不存在,那么就是非web应用
     for (String className : SERVLET_INDICATOR_CLASSES) {
         if (!ClassUtils.isPresent(className, null)) {
            return WebApplicationType.NONE;
        }
    }
    // 否则都是Servlet的web应用
    return WebApplicationType.SERVLET;
}

getSpringFactoriesInstances(ApplicationContextInitializer.class)
META-INF/spring.factories配置文件中找到所有ApplicationContextInitializer接口对应的实现类配置,然后通过反射机制构造出对应的实例对象。
getSpringFactoriesInstances(ApplicationListener.class)
这个方法也是一样的做法,将会获取ApplicationListener接口的所有配置实例对象
deduceMainApplicationClass根据堆栈来推断当前main方法所在的主类

private Class<?> deduceMainApplicationClass() {
     try {
         // 获取堆栈链路
         StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
         // 遍历每一个栈帧信息
         for (StackTraceElement stackTraceElement : stackTrace) {
             // 如果该栈帧对应的方法名等于main
             if ("main".equals(stackTraceElement.getMethodName())) {
                 // 获取该类的class对象
                return Class.forName(stackTraceElement.getClassName());
            }
        }
    }
    catch (ClassNotFoundException ex) {
        // Swallow and continue
    }
    return null;
}

SpringApplication.run

	// 运行Spring应用程序,创建并刷新ApplicationContext
    // @param args应用程序参数(通常从Java main方法传递)
    // 返回正在运行的{@link ApplicationContext}
	public ConfigurableApplicationContext run(String... args) {
		StopWatch stopWatch = new StopWatch();
		stopWatch.start();
		ConfigurableApplicationContext context = null;
		Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
		configureHeadlessProperty();
		SpringApplicationRunListeners listeners = getRunListeners(args);
		listeners.starting();
		try {
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
			ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
			configureIgnoreBeanInfo(environment);
			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;
	}

组件

ApplicationListener

ApplicationListener 其实是老面孔,属于 Spring 框架对 Java 中实现的监听者模式的一种框架实现,这里唯一值得着重强调的是,对于初次接触 SpringBoot,但对 Spring 框架本身又没有过多接触的开发者来说,可能会将这个名字与 SpringApplicationRunListener 混淆。

关于 ApplicationListener 我们就不做过多介绍了,如果感兴趣,请参考 Spring 框架相关的资料和书籍。

如果我们要为 SpringBoot 应用添加自定义的 ApplicationListener,有两种方式:

  • 通过 SpringApplication.addListeners(…)或者 SpringApplication.setListeners(…)方法添加一个或者多个自定义的 ApplicationListener。
  • 借助 SpringFactoriesLoader 机制,在 META-INF/spring.factories 文件中添加配置(以下代码是为 SpringBoot 默认注册的 ApplicationListener 配置)。
org.springframework.context.ApplicationListener=
\org.springframework.boot.builder.ParentContextCloserApplicationListener,
\org.springframework.boot.cloudfoundry.VcapApplicationListener,
\org.springframework.boot.context.FileEncodingApplicationListener,
\org.springframework.boot.context.config.AnsiOutputApplicationListener,
\org.springframework.boot.context.config.ConfigFileApplicationListener,
\org.springframework.boot.context.config.DelegatingApplicationListener,
\org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicat-ionListener,
\org.springframework.boot.logging.ClasspathLoggingApplicationListener,
\org.springframework.boot.logging.LoggingApplicationListener

关于 ApplicationListener,我们就说这些。

ApplicationContextInitializer

ApplicationContextInitializer 也是 Spring 框架原有的概念,这个类的主要目的就是在 ConfigurableApplicationContext 类型(或者子类型)的 ApplicationContext 做 refresh 之前,允许我们对 ConfigurableApplicationContext 的实例做进一步的设置或者处理。

实现一个 ApplicationContextInitializer 很简单,因为它只有一个方法需要实现:

public class DemoApplicationContextInitializer implements ApplicationContextInitializer {    @Override    public void initialize(ConfigurableApplicationContext applicationContext) {        // do whatever you want with applicationContext,        // e.g.        applicationContext.registerShutdownHook();    }}

不过,一般情况下我们基本不会需要自定义一个 ApplicationContext-Initializer,即使 SpringBoot 框架默认也只是注册了三个实现:

org.springframework.context.ApplicationContextInitializer=
\org.springframework.boot.context.ConfigurationWarningsApplication-ContextInitializer,
\org.springframework.boot.context.ContextIdApplicationContextInitia-lizer,
\org.springframework.boot.context.config.DelegatingApplicationContex-tInitializer

如果我们真的需要自定义一个 ApplicationContextInitializer,那么只要像上面这样,通过 SpringFactoriesLoader 机制进行配置,或者通过 SpringApplication.addInitializers(…)设置即可。

1. SpringApplicationRunListener

  1. META-INF/spring.factories获取所有SpringApplicationRunListener.class的配置
  2. 通过反射获取SpringApplicationRunListener的实现类对象
  3. 把所有的实现类包装成SpringApplicationRunListeners
    核心代码:
// 把所有的实现类包装成`SpringApplicationRunListeners`
private SpringApplicationRunListeners getRunListeners(String[] args) {
		Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
		return new SpringApplicationRunListeners(logger,
				getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
	}
// 从`META-INF/spring.factories`获取所有`SpringApplicationRunListener.class`的配置	
SpringFactoriesLoader.loadFactoryNames(type, classLoader)

SpringApplicationRunListener接口规定了SpringBoot的生命周期,在各个生命周期广播相应的事件,调用实际的ApplicationListener类。

源码如下:

public interface SpringApplicationRunListener {
	// SpringApplication.run之后里面调用
	default void starting() {}
	 // environment创建之后,ApplicationContext之前
	default void environmentPrepared(ConfigurableEnvironment environment) {}
	 // ApplicationContext创建之后,sources 之前
	default void contextPrepared(ConfigurableApplicationContext context) {}
	 // ApplicationContext加载之后,ApplicationContext刷新之前
	default void contextLoaded(ConfigurableApplicationContext context) {}
	 // ApplicationContext刷新之后,CommandLineRunners、ApplicationRunner调用之前
	default void started(ConfigurableApplicationContext context) {}
	// CommandLineRunners、ApplicationRunner调用之后,兜底的了
	default void running(ConfigurableApplicationContext context) {}
	//在运行应用程序时发生故障时调用
	default void failed(ConfigurableApplicationContext context, Throwable exception) {}
}

看下在代码中调用位置:
在这里插入图片描述

EventPublishingRunListener(唯一默认实现)

默认只有一个实现类 EventPublishingRunListener类
EventPublishingRunListener类 实现了SpringApplicationRunListener,它具有广播事件的功能。
事件一览:

  • starting - ApplicationStartingEvent
  • environmentPrepared - ApplicationEnvironmentPreparedEvent
  • contextPrepared - ApplicationContextInitializedEvent
  • contextLoaded - ApplicationPreparedEvent
  • started - ApplicationStartedEvent
  • running - ApplicationReadyEvent
  • failed - ApplicationFailedEvent

如何扩展

直接参照EventPublishingRunListener实现

  1. 实现接口 SpringApplicationRunListener

  2. META-INF/spring.factories下配置

    org.springframework.boot.SpringApplicationRunListener=\
     com.xxx.xxx.xxx
    

    基本上不会去实现这个扩展。要用也是监听EventPublishingRunListener类发出的事件ApplicationEvent

总结

  • SpringApplicationRunListener就是一个ApplicationListener的代理。
  • SpringApplicationRunListeners是SpringApplicationRunListener的封装,SpringApplicationRunListeners中包含多个SpringApplicationRunListener,是为了批量执行的封装
  • 使用了Spring广播器SimpleApplicationEventMulticaster。它把监听的过程封装成了SpringApplicationEvent事件并让内部属性(属性名为multicaster)ApplicationEventMulticaster接口的实现类SimpleApplicationEventMulticaster广播出去,广播出去的事件对象会被SpringApplication中的listeners属性进行处理。

扩展点

  • SpringApplicationRunListener
  • 一堆事件

2. Environment

运行时机:
在这里插入图片描述

核心代码

	private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
			ApplicationArguments applicationArguments) {
		// 创建一个web运行环境或者标准运行环境
		ConfigurableEnvironment environment = getOrCreateEnvironment();
		// 配置Environment对象
		configureEnvironment(environment, applicationArguments.getSourceArgs());
		ConfigurationPropertySources.attach(environment);
       // 触发监听器(主要是触发ConfigFileApplicationListener,这个监听器将会加载如application.properties/yml这样的配置文件)
		listeners.environmentPrepared(environment);
		// 绑定ConfigurableEnvironment到当前的SpringApplication实例中
		bindToSpringApplication(environment);
		if (!this.isCustomEnvironment) {
			environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
					deduceEnvironmentClass());
		}
		ConfigurationPropertySources.attach(environment);
		return environment;
	}

1.getOrCreateEnvironment 根据环境判断创建的运行环境

getOrCreateEnvironment方法中根据WebApplicationType类型选择具体的Environment类型,也就是我们提到过的Servlet类型、Reative类型或者非Web应用类型。

  • 增加servletConfigInitParams属性源和servletContextInitParams属性源,添加jndi属性源
  • 增加两个属性源(系统属性源和系统环境属性源)主要有Java的版本号,Java虚拟机名称等
   switch (this.webApplicationType) {
    case SERVLET:
        return new StandardServletEnvironment();
    case REACTIVE:
        return new StandardReactiveWebEnvironment();
    default:
        return new StandardEnvironment();
    }

2.configureEnvironment

  • 添加defaultProperties默认属性源(sources.addLast(new MapPropertySource(“defaultProperties”, this.defaultProperties));
  • 命令属性源sources.addFirst(new SimpleCommandLinePropertySource(args));
  • 解析命令的时候,判断是否以“–”开头
  • 触发监听器(主要是触发ConfigFileApplicationListener,这个监听器将会加载如application.properties/yml这样的配置文件
protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) {
    if (this.addConversionService) {
        ConversionService conversionService = ApplicationConversionService.getSharedInstance();
        environment.setConversionService((ConfigurableConversionService) conversionService);
    }
    // 添加初始的properties(注意:当前并未加载如application.properties/yml的properties)
    configurePropertySources(environment, args);
    // 添加初始的profile(注意:当前并未加载如application.properties/yml配置profile)
    configureProfiles(environment, args);
}
  • 发出事件ApplicationEnvironmentPreparedEvent,监听器为ConfigFileApplicationListener的onApplicationEvent方法
@Override
public void onApplicationEvent(ApplicationEvent event) {
    // 只触发Environment相关的事件
    if (event instanceof ApplicationEnvironmentPreparedEvent) {
        onApplicationEnvironmentPreparedEvent((ApplicationEnvironmentPreparedEvent) event);
    }
    if (event instanceof ApplicationPreparedEvent) {
        onApplicationPreparedEvent(event);
    }
}

继续跟进

private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) {
    List<EnvironmentPostProcessor> postProcessors = loadPostProcessors();
    postProcessors.add(this);
    AnnotationAwareOrderComparator.sort(postProcessors);
    for (EnvironmentPostProcessor postProcessor : postProcessors) {
        // 执行后置处理器
        postProcessor.postProcessEnvironment(event.getEnvironment(), event.getSpringApplication());
    }
}
我们看到,首先加载了Environment的后置处理器,然后经过排序以后遍历触发每个处理器。这里注意,ConfigFileApplicationListener本身也实现了EnvironmentPostProcessor接口,所以这里将会触发ConfigFileApplicationListener内部方法执行

跟进ConfigFileApplicationListener的postProcessEnvironment方法
@Override
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
    addPropertySources(environment, application.getResourceLoader());
}
跟进addPropertySources方法
protected void addPropertySources(ConfigurableEnvironment environment, ResourceLoader resourceLoader) {
    RandomValuePropertySource.addToEnvironment(environment);
    new Loader(environment, resourceLoader).load();
}
这里实例化了一个Loader用来加载application配置文件,而核心逻辑就在load方法当中。
EnvironmentPostProcessor

ConfigFileApplicationListener 收到 ApplicationEnvironmentPreparedEvent 事件后通过 SPI 加载所有的 EnvironmentPostProcessor 实现,触发其 postProcessEnviroment 方法。

SpringApplication.run() ->
SpringFactoriesLoader.loadFactories(ApplicationListener) ->
SpringApplication.prepareEnviroment() -> EventPublishingRunListener.enviromentPrepared(ApplicationEnviromentPraparedEvent) ->
SimpleApplicationEventMulticaster.multicastEvent() ->
ConfigFileApplicationListener.onApplicationOnEnviromentPreparedEvent() ->
EnviromentPostProcessor.postProcessEnviroment()

比较重要的 EnviromentPostProcessor 实现是 HostInfoEnvironmentPostProcessorConfigFileApplicationListener

  • HostInfoEnvironmentPostProcessor.postProcessEnviroment

获取本机的 主机名和IP地址,封装在 PropertySource 添加到 environment 里。

  • ConfigFileApplicationListener.postProcessEnviroment

ConfigFileApplicationListener 自身也实现了 EnvironmentPostProcessor ,通过内部类 Loader 去加载配置文件,其主要流程如下:

  1. 从 Environment 中获取 active 和 include 的 profile 集合。进行迭代:
  2. 获取所有的搜索路径,进行迭代,默认的搜索路径是 classpath:/,classpath:/config/,file:./,file:./config/
  3. 如果某个搜索路径不以 / 结尾的则认为是一个文件,直接加载,否则,找出所有的搜索文件名 name 进行迭代搜索,默认的搜索文件名是 “application”。
  4. 通过 PropertySourcesLoader 找出支持的所有配置文件后缀进行迭代。
  5. 最终得到 location + name + "-" + profile + "." + ext 组成的一个具体的完整路径,通过 PropertiesLoader.load 方法加载该路径指向的配置文件。
  6. PropertiesLoader.load 内部又根据配置文件的后缀用不同的 PropertySourceLoader 去加载得到一个 PropertySource
  7. 对于解析得到的 PropertySource ,找出里面激活的 profile,添加到 proflie 集合里进行迭代。
  8. 继续迭代下一个 profile 。
  • PropertySourceLoader

PropertySourceLoader 是用来加载 PropertySource 的一个策略接口,有两个具体的实现类 PropertiesPropertySourceLoaderYamlPropertySourceLoader ,前者用于加载 properties/xml 后缀的配置文件,后者用于加载 yml 后者的配置文件。

@ConfigurationProperties 使用的变量也是从environment中取的

spring boot默认可以加载当前启动路径{user.dir}下 /config目录中的配置文件, /home/config和docker没有关系, 在一些自动划构建工具可能用到这个特性
话不多说,直接下面看重点:

img

扩展点

Spring使用EnvironmentPostProcessor自定义启动配置项
public class MyEnvironmentPostProcessor implements EnvironmentPostProcessor {
 
    private static final String PROPERTY_SOURCE_NAME = "defaultProperties";
 
    @Override
    public void postProcessEnvironment(ConfigurableEnvironment environment,
                                       SpringApplication application) {
        Map<String, Object> map = new HashMap<>();
        map.put("localProperey", "value");
        MapPropertySource target = (MapPropertySource) environment.getPropertySources().get(PROPERTY_SOURCE_NAME);
        if (target == null) {
            target = new MapPropertySource(PROPERTY_SOURCE_NAME, map);
        }
        environment.getPropertySources().addLast(target);
    }
 
}

在META-INF的spring.factories中添加

org.springframework.boot.env.EnvironmentPostProcessor=\
com.xxxxx.MyEnvironmentPostProcessor

调用时间比使用BootstrapConfiguration(PropertySourceLocator的实现)更早

3. Banner

banner的位置在 resources目录下 默认为 banner.txt 和 banner命名的jpg\png\gif图片

  • 加载图片banner,定义如下。如果是 banner.gif 或 banner.jpg 或banner.png 就会被加载 SpringApplicationBannerPrinter.getImageBanner
  • 加载文字banner,默认是banner.txt文件 SpringApplicationBannerPrinter.getTextBanner

4. ConfigurableApplicationContext

在这里插入图片描述
反射获取注解上下文

	public static final String DEFAULT_SERVLET_WEB_CONTEXT_CLASS = "org.springframework.boot."
			+ "web.servlet.context.AnnotationConfigServletWebServerApplicationContext";

	protected ConfigurableApplicationContext createApplicationContext() {
		Class<?> contextClass = this.applicationContextClass;
		if (contextClass == null) {
			try {
				switch (this.webApplicationType) {
				case SERVLET:
					contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
					break;
				case REACTIVE:
					contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
					break;
				default:
					contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
				}
			}

5. SpringBootExceptionReporter

一般都是解析META-INF/spring.factories文件下

ApplicationContextInitializer、ApplicationListener、SpringBootExceptionReporter、SpringApplicationRunListeners等接口的实现类,基于SPI可插拔

setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
       setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));

只有一个实现FailureAnalyzers
一个Spring Boot 应用偶尔会因为某些原因启动失败,此时Spring Boot会友好地输出类似于这样一段文字,告诉你发生了什么,甚至应该采取什么行动:

***************************

APPLICATION FAILED TO START

***************************

Description:

Parameter 0 of constructor in com.example.B required a bean of type 'com.example.A' that could not be found.

Action:

Consider defining a bean of type 'com.example.A' in your configuration.

这是怎么做到的呢?或许第一想法是SpringBoot会在出现问题的地方构造这样完整的异常和输出。实际上并非如此,Spring Boot提供了统一的接口进行问题的分析和诊断,这就是org.springframework.boot.diagnostics.FailureAnalyzer 接口。

扩展

如果做一个自己的组件,就可能会遇上需要处理异常并向使用者提供建议的情况。

我们定义一个异常

public class WannaStopException extends RuntimeException {
}

我们定义一个Bean在某(全)种(部)情况下会抛出异常

@Service
public class A {
    public A() {
        throw new WannaStopException();
    }
}

我们定义一个FailureAnalyzer专门处理这个WannaStopException

public class StopFailureAnalyzer extends AbstractFailureAnalyzer<WannaStopException> {
    @Override
    protected FailureAnalysis analyze(Throwable rootFailure, WannaStopException cause) {
        for (StackTraceElement stackTraceElement : cause.getStackTrace()) {
            if (stackTraceElement.getClassName().equals("com.example.A")) {
                return new FailureAnalysis("A想停止", "别要A了", cause);
            }
        }
        return null;
    }
}

AbstractFailureAnalyzer 帮助我们找到特定异常

接下来我们还要把StopFailureAnalyzer放进spring.factories中,在resources/META-INF/spring.factories(自己建)里添加

org.springframework.boot.diagnostics.FailureAnalyzer=\
  com.example.flowable.StopFailureAnalyzer

启动这个应用,就能看到如下输出

6. prepareContext 准备上下文

prepareContext 准备上下文
在这里插入图片描述

	private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
			SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
		// 设置上下文的environment
		context.setEnvironment(environment);
		 // 应用上下文后处理
		postProcessApplicationContext(context);
		 // 在context refresh之前,对其应用ApplicationContextInitializer
		applyInitializers(context);
		 // 上下文准备(目前是空实现,可用于拓展)
		listeners.contextPrepared(context);
		  // 打印启动日志和启动应用的Profile
		if (this.logStartupInfo) {
			logStartupInfo(context.getParent() == null);
			logStartupProfileInfo(context);
		}
		//  向beanFactory注册单例bean:命令行参数bean
		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());
		}
		//  获取全部资源,其实就一个:SpringApplication的primarySources属性
		Set<Object> sources = getAllSources();
		Assert.notEmpty(sources, "Sources must not be empty");
		 // 将bean加载到应用上下文中
		load(context, sources.toArray(new Object[0]));
		  // 向上下文中添加ApplicationListener,并广播ApplicationPreparedEvent事件
		listeners.contextLoaded(context);
	}
  • context.setEnvironment(environment)
    之前我们的应用中有两个environment,一个在context中,一个在SpringApplication中。经过此方法后,就只会存在SpringApplication中的environment了,而context中的原environment会被回收。
  • postProcessApplicationContext(context);
protected void postProcessApplicationContext(ConfigurableApplicationContext context) {
  if (this.beanNameGenerator != null) {
      context.getBeanFactory().registerSingleton(
              AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR,
              this.beanNameGenerator);
  }
  if (this.resourceLoader != null) {
      if (context instanceof GenericApplicationContext) {
          ((GenericApplicationContext) context)
                  .setResourceLoader(this.resourceLoader);
      }
      if (context instanceof DefaultResourceLoader) {
          ((DefaultResourceLoader) context)
                  .setClassLoader(this.resourceLoader.getClassLoader());
      }
  }
}

由于当前SpringApplication实例的属性:beanNameGenerator和resourceLoader都为null,所以此方法目前相当于什么也没做。此方法可能是我们定制SpringApplication所用

  • applyInitializers(context);
protected void applyInitializers(ConfigurableApplicationContext context) {
  for (ApplicationContextInitializer initializer : getInitializers()) {
      // 解析当前initializer实现的ApplicationContextInitializer的泛型参数
      Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(
              initializer.getClass(), ApplicationContextInitializer.class);
      // 断言context是否是requiredType的实例
      Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
      // 向context应用初始化器
      initializer.initialize(context);
  }
}

在context refresh之前应用ApplicationContextInitializer到context中
DelegatingApplicationContextInitializer相当于context.initializer.classes的代理,最终还是会执行到被代理的initializer的initialize方法。
ContextIdApplicationContextInitializer

设置application id:从environment中获取spring.application.name配置项的值,并把设置成application id,若没有配置spring.application.name,则取默认值application;

将application id封装成ContextId对象,注册到beanFactory中。

ConfigurationWarningsApplicationContextInitializer

向上下文注册了一个BeanFactoryPostProcessor:ConfigurationWarningsPostProcessor实例;

实例化ConfigurationWarningsPostProcessor的时候,也实例化了它的属性Check[] checks,check中只有一个类型是ComponentScanPackageCheck的实例。
ServerPortInfoApplicationContextInitializer

向上下文注册了一个ApplicationListener:ServerPortInfoApplicationContextInitializer对象自己;

ServerPortInfoApplicationContextInitializer实现了ApplicationListener,所以他本身就是一个ApplicationListener。
SharedMetadataReaderFactoryContextInitializer

向context注册了一个BeanFactoryPostProcessor:CachingMetadataReaderFactoryPostProcessor实例。
ConditionEvaluationReportLoggingListener

将上下文赋值给自己的属性applicationContext;

向上下文注册了一个ApplicationListener:ConditionEvaluationReportListener实例;

从beanFactory中获取名为autoConfigurationReport的bean赋值给自己的属性report。

  • listeners.contextPrepared(context);
    还记得SpringApplicationRunListeners中listeners属性吗,没错,里面就一个EventPublishingRunListener对象。

调用EventPublishingRunListener的contextPrepared,发现其是空实现。

也就是相当于啥事也没做。

  • listeners.contextLoaded(context);

一共过滤出5个监听器,他们的onApplicationEvent方法会被调用,具体做了如下事情:

ConfigFileApplicationListener

向context注册了一个BeanFactoryPostProcessor:PropertySourceOrderingPostProcessor实例;该实例后面会对我们的property sources进行重排序,另外该实例拥有上下文的引用。

LoggingApplicationListener

向beanFactory中注册了一个名叫springBootLoggingSystem的单例bean,也就是我们的日志系统bean。

BackgroundPreinitializer

目前什么也没做

DelegatingApplicationListener

目前什么也没做

EnableEncryptablePropertiesBeanFactoryPostProcessor

仅仅打印了一句debug日志,相当于什么也没做

7. refreshContext

	private void refreshContext(ConfigurableApplicationContext context) {
		if (this.registerShutdownHook) {
			try {
				context.registerShutdownHook();
			}
			catch (AccessControlException ex) {
				// Not allowed in some environments.
			}
		}
		refresh((ApplicationContext) context);
	}
	protected void refresh(ConfigurableApplicationContext applicationContext) {
		applicationContext.refresh();
	}
这就是spring的上下文刷新逻辑了
```java
	public final void refresh() throws BeansException, IllegalStateException {
	try {
		super.refresh(); 	这就是spring的上下文刷新逻辑了
	}
	catch (RuntimeException ex) {
		WebServer webServer = this.webServer;
		if (webServer != null) {
			webServer.stop();
		}
		throw ex;
	}
}
```

AbstractApplicationContext.refresh

@Override
public void refresh() throws BeansException, IllegalStateException {
	synchronized (this.startupShutdownMonitor) {
	    // 准备刷新
	    prepareRefresh();
 
	    // 通知子类刷新内部bean工厂
	    ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
 
	    // 准备bean工厂以便在此上下文中使用
	    prepareBeanFactory(beanFactory);
 
	    try {
	    	// 允许上下文子类中对bean工厂进行后处理
	    	postProcessBeanFactory(beanFactory);
 
	    	// 在bean创建之前调用BeanFactoryPostProcessors后置处理方法
	    	invokeBeanFactoryPostProcessors(beanFactory);
 
	    	// 注册BeanPostProcessor
	    	registerBeanPostProcessors(beanFactory);
 
	    	// 注册DelegatingMessageSource
	    	initMessageSource();
 
	    	// 注册multicaster
	    	initApplicationEventMulticaster();
 
	    	// 创建内置的Servlet容器 tomcat等
	    	onRefresh();
 
	    	// 注册Listener
	    	registerListeners();
 
	    	// 完成BeanFactory初始化,初始化剩余单例bean
	    	finishBeanFactoryInitialization(beanFactory);
 
	    	// 发布对应事件
	    	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();
	    }
    }
}
//ServletWebServerApplicationContext.java
@Override
protected void onRefresh() {
	super.onRefresh();
	try {
		createWebServer();
	}
	catch (Throwable ex) {
		throw new ApplicationContextException("Unable to start web server", ex);
	}
}

CommandLineRunner

CommandLineRunner 是很好的扩展接口,不是 Spring 框架原有的“宝贝”,它属于 SpringBoot 应用特定的回调扩展接口。源码如下所示:

public interface CommandLineRunner {   
	void run(String... args) throws Exception;
}

CommandLineRunner 需要大家关注的其实就两点:

1)所有 CommandLineRunner 的执行时点在 SpringBoot 应用的 Application-Context 完全初始化开始工作之后(可以认为是 main 方法执行完成之前最后一步)。

2)只要存在于当前 SpringBoot 应用的 ApplicationContext 中的任何 Command-LineRunner,都会被加载执行(不管你是手动注册这个 CommandLineRunner 到 IoC 容器,还是自动扫描进去的)。

与其他几个扩展点接口类型相似,建议 CommandLineRunner 的实现类使用 @org.springframework.core.annotation.Order 进行标注或者实现 org.springframework.core.Ordered 接口,便于对它们的执行顺序进行调整,这其实十分重要,我们不希望顺序不当的 CommandLineRunner 实现类阻塞了后面其他 CommandLineRunner 的执行。

参考:
www.cnblogs.com/lay2017/p/1…