尚硅谷SpringBoot顶尖教程

1. 启动流程简介

SpringBoot应用从主启动类启动后的运行流程主要包含下面几个要点:

(1)准备环境

  • 执行ApplicationContextInitializer#initialize()方法;
  • 监听器SpringApplicationRunListener回调contextPrepared方法
  • 加载主配置类定义信息
  • 监听器SpringApplicationRunListener回调contextLoaded方法;

(2)刷新启动IOC容器

  • 扫描加载所有容器中的组件;
  • 包括从META-INF/spring.factories文件中获取的所有EnableAutoConfiguration组件;

(3)回调容器中所有的ApplicationRunner,CommandLineRunner的run方法;

(4)监听器SpringApplicationRunListener回调finished方法

下面来看详细的启动流程分析, 课程讲解使用的是 1.5.10.RELEASE版本的SpringBoot源码。

2. 启动流程源码分析

2.1 创建SpringApplication对象

主启动类的main方法是Springboot应用启动的入口。

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

通过SpringApplication.run方法会进行SpringApplication对象的创建及部分初始化工作。

public class SpringApplication {// ...省略非核心代码// 主启动类入口调用的run方法public static ConfigurableApplicationContext run(Object source, String... args) {return run(new Object[] { source }, args);}// 继续走重载的run方法public static ConfigurableApplicationContext run(Object[] sources, String[] args) {// 第一步:调用SpringApplication构造方法实例化对象// 第二步:创建SpringApplication对象后,继续调用重载的run方法return new SpringApplication(sources).run(args);}// SpringApplication构造方法public SpringApplication(Object... sources) {// 初始化,参数是springboot应用的主启动类class对象initialize(sources);}// 初始化private void initialize(Object[] sources) {// 保存主配置类到this.sources属性中if (sources != null && sources.length > 0) {this.sources.addAll(Arrays.asList(sources));}// 判断当前springBoot应用是否是一个web应用,并保存判断结果到this.webEnvironment属性中this.webEnvironment = deduceWebEnvironment();// 从类路径下找到META-INF/spring.factories文件中配置的所有ApplicationContextInitializer并保存到this.initializers属性中。setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));// 从类路径下找到META-INF/spring.factories文件中配置的所有ApplicationListener并保存到this.listeners属性中setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));// 从多个配置类中找到有main方法的主配置类,比如:SpringBoot02ConfigApplicationthis.mainApplicationClass = deduceMainApplicationClass();}// 判断当前springBoot应用是否是一个web应用 private static final String[] WEB_ENVIRONMENT_CLASSES = { "javax.servlet.Servlet","org.springframework.web.context.ConfigurableWebApplicationContext" };private boolean deduceWebEnvironment() {for (String className : WEB_ENVIRONMENT_CLASSES) {// 判断当前环境是否存在ConfigurableWebApplicationContextif (!ClassUtils.isPresent(className, null)) {return false;}}return true;}// 保存ApplicationContextInitializer到this.initializers属性中public void setInitializers(Collection<? extends ApplicationContextInitializer<?>> initializers) {this.initializers = new ArrayList<ApplicationContextInitializer<?>>();this.initializers.addAll(initializers);}// 保存ApplicationListener到this.listeners属性中public void setListeners(Collection<? extends ApplicationListener<?>> listeners) {this.listeners = new ArrayList<ApplicationListener<?>>();this.listeners.addAll(listeners);}private <T> Collection<? extends T> getSpringFactoriesInstances(Class<T> type) {return getSpringFactoriesInstances(type, new Class<?>[] {});}// 从META-INF/spring.factories文件中查找ApplicationContextInitializer.class,ApplicationListener.classprivate <T> Collection<? extends T> getSpringFactoriesInstances(Class<T> type,Class<?>[] parameterTypes, Object... args) {ClassLoader classLoader = Thread.currentThread().getContextClassLoader();// Use names and ensure unique to protect against duplicatesSet<String> names = new LinkedHashSet<String>(// META-INF/spring.factories SpringFactoriesLoader.loadFactoryNames(type, classLoader));// 实例化ApplicationContextInitializer,ApplicationListenerList<T> instances = createSpringFactoriesInstances(type, parameterTypes,classLoader, args, names);AnnotationAwareOrderComparator.sort(instances);return instances;}}// 从多个配置类中找到有main方法的主配置类,比如:SpringBoot02ConfigApplicationprivate Class<?> deduceMainApplicationClass() {try {StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();for (StackTraceElement stackTraceElement : stackTrace) {// 通过当前的栈中找到有main方法的配置类,就是主启动类if ("main".equals(stackTraceElement.getMethodName())) {return Class.forName(stackTraceElement.getClassName());}}}catch (ClassNotFoundException ex) {// Swallow and continue}return null;}

spring-boot-1.5.10.RELEASE.jar!/META-INF/spring.factories文件配置了类的全限定名称;

# Application Context Initializersorg.springframework.context.ApplicationContextInitializer=\org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\org.springframework.boot.context.ContextIdApplicationContextInitializer,\org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\org.springframework.boot.context.embedded.ServerPortInfoApplicationContextInitializer# Application Listenersorg.springframework.context.ApplicationListener=\org.springframework.boot.ClearCachesApplicationListener,\org.springframework.boot.builder.ParentContextCloserApplicationListener,\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.LiquibaseServiceLocatorApplicationListener,\org.springframework.boot.logging.ClasspathLoggingApplicationListener,\org.springframework.boot.logging.LoggingApplicationListener

当然类路径下还有其他spring.factories文件都有可能配置的有要初始化的组件类名称,自定义spring.factories也可以。比如: 1.5.10.RELEASE\spring-boot-autoconfigure-1.5.10.RELEASE.jar!\META-INF\spring.factories中的初始化配置。

# Initializersorg.springframework.context.ApplicationContextInitializer=\org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\org.springframework.boot.autoconfigure.logging.AutoConfigurationReportLoggingInitializer# Application Listenersorg.springframework.context.ApplicationListener=\org.springframework.boot.autoconfigure.BackgroundPreinitializer

2.2 运行run方法

运行run方法继续完善IOC容器内容,源码分析如下:

public class SpringApplication {// ... public ConfigurableApplicationContext run(String... args) {StopWatch stopWatch = new StopWatch();// 配置IOC容器开始stopWatch.start();ConfigurableApplicationContext context = null;FailureAnalyzers analyzers = null;configureHeadlessProperty();// 从类路径下/META-INF/spring.factories,获取SpringApplicationRunListenerSpringApplicationRunListeners listeners = getRunListeners(args);// 调用所有的SpringApplicationRunListener#starting方法listeners.starting();try {// 封装命令行参数,主启动类main方法的args参数ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);// 准备环境ConfigurableEnvironment environment = prepareEnvironment(listeners,applicationArguments);// 打印banner图标Banner printedBanner = printBanner(environment);// 创建IOC容器, 里面决定创建Web的IOC容器还是普通IOC容器。context = createApplicationContext();analyzers = new FailureAnalyzers(context);// 准备上下文环境,将environment保存到ioc容器中;prepareContext(context, environment, listeners, applicationArguments,printedBanner);// 刷新IOC容器及初始化refreshContext(context);// IOC容器初始化完成后afterRefresh(context, applicationArguments);// 所有的SpringApplicationRunListener回调finished()方法listeners.finished(context, null);// 配置IOC容器结束stopWatch.stop();if (this.logStartupInfo) {new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);}//整个SpringBoot应用启动完成后返回启动的IOC容器return context;}catch (Throwable ex) {handleRunFailure(context, listeners, analyzers, ex);throw new IllegalStateException(ex);}}// 从类路径下/META-INF/spring.factories,获取SpringApplicationRunListenersprivate SpringApplicationRunListeners getRunListeners(String[] args) {Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };// /META-INF/spring.factoriesreturn new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));}// type=SpringApplicationRunListenerprivate <T> Collection<? extends T> getSpringFactoriesInstances(Class<T> type,Class<?>[] parameterTypes, Object... args) {ClassLoader classLoader = Thread.currentThread().getContextClassLoader();// Use names and ensure unique to protect against duplicatesSet<String> names = new LinkedHashSet<String>(// /META-INF/spring.factoriesSpringFactoriesLoader.loadFactoryNames(type, classLoader));// 创建SpringApplicationRunListener实例List<T> instances = createSpringFactoriesInstances(type, parameterTypes,classLoader, args, names);AnnotationAwareOrderComparator.sort(instances);// 返回SpringApplicationRunListener实例return instances;}// 调用所有的SpringApplicationRunListener#starting方法public void starting() {for (SpringApplicationRunListener listener : this.listeners) {listener.starting();}}// 准备环境private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,ApplicationArguments applicationArguments) {// Create and configure the environmentConfigurableEnvironment environment = getOrCreateEnvironment();configureEnvironment(environment, applicationArguments.getSourceArgs());// 创建环境完成后调用所有的SpringApplicationRunListener#environmentPrepared方法,表示环境准备完成。listeners.environmentPrepared(environment);if (!this.webEnvironment) {environment = new EnvironmentConverter(getClassLoader()).convertToStandardEnvironmentIfNecessary(environment);}return environment;}//调用所有的SpringApplicationRunListener#environmentPrepared方法,表示环境准备完成。public void environmentPrepared(ConfigurableEnvironment environment) {for (SpringApplicationRunListener listener : this.listeners) {listener.environmentPrepared(environment);}}// 嵌入式WEB环境的IOC容器public static final String DEFAULT_WEB_CONTEXT_CLASS = "org.springframework."+ "boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext";// 普通IOC容器public static final String DEFAULT_CONTEXT_CLASS = "org.springframework.context."+ "annotation.AnnotationConfigApplicationContext";// 创建IOC容器protected ConfigurableApplicationContext createApplicationContext() {Class<?> contextClass = this.applicationContextClass;if (contextClass == null) {// 创建web的IOC容器,还是普通的IOC容器contextClass = Class.forName(this.webEnvironment? DEFAULT_WEB_CONTEXT_CLASS : DEFAULT_CONTEXT_CLASS);}// 返回实例化的IOC容器return (ConfigurableApplicationContext) BeanUtils.instantiate(contextClass);}// 准备上下文环境,将environment保存到ioc容器中;private void prepareContext(ConfigurableApplicationContext context,ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,ApplicationArguments applicationArguments, Banner printedBanner) {// 将environment保存到ioc容器中context.setEnvironment(environment);postProcessApplicationContext(context);// 回调之前保存的所有ApplicationContextInitializer#initialize(context)方法applyInitializers(context);// 回调之前保存的所有SpringApplicationRunListener#contextPrepared(context)方法listeners.contextPrepared(context);if (this.logStartupInfo) {logStartupInfo(context.getParent() == null);logStartupProfileInfo(context);}// Add boot specific singleton beanscontext.getBeanFactory().registerSingleton("springApplicationArguments",applicationArguments);if (printedBanner != null) {context.getBeanFactory().registerSingleton("springBootBanner", printedBanner);}// Load the sourcesSet<Object> sources = getSources();Assert.notEmpty(sources, "Sources must not be empty");load(context, sources.toArray(new Object[sources.size()]));// 回调所有的SpringApplicationRunListener#contextLoaded(context)方法listeners.contextLoaded(context);}// 回调之前保存的所有的ApplicationContextInitializer#initialize(context)方法protected void applyInitializers(ConfigurableApplicationContext context) {for (ApplicationContextInitializer initializer : getInitializers()) {Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(initializer.getClass(), ApplicationContextInitializer.class);Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");// ApplicationContextInitializer#initialize(context)initializer.initialize(context);}}// 回调之前保存的所有SpringApplicationRunListener#contextPrepared(context)方法public void contextPrepared(ConfigurableApplicationContext context) {for (SpringApplicationRunListener listener : this.listeners) {// SpringApplicationRunListener#contextPrepared(context),表示IOC容器准备完成listener.contextPrepared(context);}}// 回调所有的SpringApplicationRunListener#contextLoaded(context)方法public void contextLoaded(ConfigurableApplicationContext context) {for (SpringApplicationRunListener listener : this.listeners) {//SpringApplicationRunListener#contextLoaded(context), 表示IOC容器加载完成listener.contextLoaded(context);}}private void refreshContext(ConfigurableApplicationContext context) {refresh(context);if (this.registerShutdownHook) {try {context.registerShutdownHook();}catch (AccessControlException ex) {// Not allowed in some environments.}}}//刷新IOC容器protected void refresh(ApplicationContext applicationContext) {Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);//扫描、创建、加载IOC容器中的所有组件(配置类,组件,自动配置)。如果是web环境,还会创建嵌入式tomcat容器。((AbstractApplicationContext) applicationContext).refresh();}// IOC容器初始化完成后protected void afterRefresh(ConfigurableApplicationContext context,ApplicationArguments args) {//从IOC容器中获取所有的ApplicationRunner和CommandLineRunner进行回调。callRunners(context, args);}private void callRunners(ApplicationContext context, ApplicationArguments args) {List<Object> runners = new ArrayList<Object>();runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());AnnotationAwareOrderComparator.sort(runners);for (Object runner : new LinkedHashSet<Object>(runners)) {if (runner instanceof ApplicationRunner) { //从IOC容器中获取所有的ApplicationRunner进行回调。callRunner((ApplicationRunner) runner, args);}if (runner instanceof CommandLineRunner) { //从IOC容器中获取所有的CommandLineRunner进行回调。callRunner((CommandLineRunner) runner, args);}}}private void callRunner(ApplicationRunner runner, ApplicationArguments args) {try {//回调ApplicationRunner#run(runner).run(args);}catch (Exception ex) {throw new IllegalStateException("Failed to execute ApplicationRunner", ex);}}private void callRunner(CommandLineRunner runner, ApplicationArguments args) {try {//回调CommandLineRunner#run(runner).run(args.getSourceArgs());}catch (Exception ex) {throw new IllegalStateException("Failed to execute CommandLineRunner", ex);}}// 所有的SpringApplicationRunListener回调finished()方法public void finished(ConfigurableApplicationContext context, Throwable exception) {for (SpringApplicationRunListener listener : this.listeners) {callFinishedListener(listener, context, exception);}}private void callFinishedListener(SpringApplicationRunListener listener,ConfigurableApplicationContext context, Throwable exception) {// 回调SpringApplicationRunListener#finishedlistener.finished(context, exception);}// ...}

我们来看运行run方法的debug过程:

从类路径下/META-INF/spring.factories,获取SpringApplicationRunListener

spring-boot-1.5.10.RELEASE.jar!\META-INF\spring.factories文件中的配置

debug调试结果:

回调所有的获取SpringApplicationRunListeners.starting方法

封装命令行参数:

准备创建环境,创建环境完成后回调SpringApplicationRunListener#environmentPrepared(environment)方法表示环境准备完成。

创建ApplicationContext容器,

里面决定创建Web的IOC容器还是普通IOC容器。

准备上下文环境,将environment保存到ioc容器中

applyInitializers(context) 回调之前保存的所有的ApplicationContextInitializer的initialize(context)方法

回调所有之前保存的SpringApplicationRunListener的contextPrepared(context)方法

prepareContext(…)方法运行完成后回调所有的SpringApplicationRunListener的contextLoaded(context)方法

刷新容器,IOC容器初始化,扫描、创建、加载IOC容器中的所有组件(配置类,组件,自动配置)。如果是web环境,还会创建嵌入式tomcat容器。

IOC容器初始化完成后,web应用(嵌入式tomcat)也已经启动完成。

从IOC容器中获取所有的ApplicationRunner和CommandLineRunner进行回调。ApplicationRunner先回调,CommandLineRunner后回调。

所有的SpringApplicationRunListener回调finished方法

整个SpringBoot应用启动完成后返回启动的IOC容器

2.3 自定义回调配置

主要看几个重要的事件回调机制 :

ApplicationContextInitializer, SpringApplicationRunListener配置在spring-boot-1.5.10.RELEASE.jar!/META-INF/spring.factories文件中;如果自定义xxxListener, 需要将自定义xxxListener或xxxInitializer放在当前应用类路径下的/META-INF/spring.factories文件中。

自定义ApplicationContextInitializer

public class HelloApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {@Overridepublic void initialize(ConfigurableApplicationContext configurableApplicationContext) {System.out.println("HelloApplicationContextInitializer.initialize....." + configurableApplicationContext.getClass().getSimpleName());}}

自定义SpringApplicationRunListener

public class HelloSpringApplicationRunListener implements SpringApplicationRunListener {public HelloSpringApplicationRunListener(SpringApplication application,String[] args) {}// 开始ioc配置@Overridepublic void starting() {System.out.println("HelloSpringApplicationRunListener.starting.....");}// 准备环境@Overridepublic void environmentPrepared(ConfigurableEnvironment environment) {Map<String, Object> systemProperties = environment.getSystemProperties();System.out.println("HelloSpringApplicationRunListener.environmentPrepared...," + systemProperties.get("os.name")+ ',' + systemProperties.get("java.home"));}// ioc容器准备@Overridepublic void contextPrepared(ConfigurableApplicationContext context) {System.out.println("HelloSpringApplicationRunListener.contextPrepared.....");}// ioc容器加载@Overridepublic void contextLoaded(ConfigurableApplicationContext context) {System.out.println("HelloSpringApplicationRunListener.contextLoaded.....");}// ioc容器配置完成@Overridepublic void finished(ConfigurableApplicationContext context, Throwable exception) {System.out.println("HelloSpringApplicationRunListener.finished....");}}

将自定义的ApplicationContextInitializer, SpringApplicationRunListener配置在当前项目类路径下的/META-INF/spring.factories文件中

# Application Context Initializersorg.springframework.context.ApplicationContextInitializer=\com.atgugui.listener.HelloApplicationContextInitializer# Run Listenersorg.springframework.boot.SpringApplicationRunListener=\com.atgugui.listener.HelloSpringApplicationRunListener

ApplicationRunner,CommandLineRunner组件只要放在IOC容器中就可以了;自定义xxxRunner,类上面加@Component即可.

/** * ApplicationRunner在IOC容器初始化过程中的SpringApplicationRunListeners#finished中执行 */@Componentpublic class HelloApplicationRunner implements ApplicationRunner {@Overridepublic void run(ApplicationArguments args) throws Exception {System.out.println("HelloApplicationRunner.run.....");}}/** * CommandLineRunner在IOC容器初始化过程中的SpringApplicationRunListeners#finished中执行 */@Componentpublic class HelloCommandLineRunner implements CommandLineRunner {@Overridepublic void run(String... args) throws Exception {System.out.println("HelloCommandLineRunner.run..." + Arrays.asList(args));}}

配置完成,启动应用,查看启动日志,和上面分析的源码执行顺序一样。

HelloSpringApplicationRunListener.starting 开始初始化
HelloSpringApplicationRunListener.environmentPrepared 准备环境

HelloApplicationContextInitializer.initialize方法初始化;

HelloSpringApplicationRunListener.contextPrepared方法准备上下文环境;

HelloSpringApplicationRunListener.contextLoaded方法加载上下文环境;

HelloSpringApplicationRunListener.finished方法上下文环境准备完成。