[SpringBoot 源码分析]SpringBoot 如何启动
- 2021 年 11 月 23 日
本文字数:8394 字
阅读完需:约 28 分钟
![[SpringBoot源码分析]SpringBoot如何启动](https://static001.geekbang.org/infoq/9a/9ae34783f6738017792311e03efb2b6e.jpeg)
SpringBoot 概念
SpringApplication 提供了从 main 方法开始的启动 spring 应用程序的便捷方法。
Spring Boot 基于 spring4.0 以后
特点
快速开发和构建微服务系统
内嵌容器,如 Tomcat、Undertow
自动加载
自动管理依赖
引言
那么上述的 springboot 有这么多好处,那先研究下 springboot 是怎么启动,后续我们再分篇研究下自动加载
开始分析源码
@SpringBootApplication(scanBasePackages = {"com.github.demo"})public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); }}// SpringApplication的run方法public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) { return run(new Class<?>[] { primarySource }, args);}// 最终调用的SpringApplication的run方法public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) { return new SpringApplication(primarySources).run(args);}
初始化 SpringApplication
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) { // 设置 resource this.resourceLoader = resourceLoader; Assert.notNull(primarySources, "PrimarySources must not be null"); this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); // 初始化类型 是 web 还是 reactor this.webApplicationType = WebApplicationType.deduceFromClasspath(); // 从类路径下找到META‐INF/spring.factories文件中,加载ApplicationContextInitializer接口的实现类, 并设置 setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class)); // 从类路径下找到META‐INF/spring.factories文件中,ApplicationListener接口的实现类,并设置 setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); this.mainApplicationClass = deduceMainApplicationClass();}
ApplicationContextInitializer
定义
Spring 容器刷新之前,初始化 ConfigurableApplicationContext 上下文的回调接口 ApplicationContextInitializer 是 Spring 框架原有的产物
实现类
# 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.rsocket.context.RSocketPortInfoApplicationContextInitializer,\org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer
ApplicationListener
定义
ApplicationListener:监听 spring 容器中发布的事件
实现类
# Application Listenersorg.springframework.context.ApplicationListener=\org.springframework.boot.ClearCachesApplicationListener,\org.springframework.boot.builder.ParentContextCloserApplicationListener,\org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,\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.context.logging.ClasspathLoggingApplicationListener,\org.springframework.boot.context.logging.LoggingApplicationListener,\org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener
核心的运行方法
// 运行springboot应用, 创建并且刷新ApplicationContext.public ConfigurableApplicationContext run(String... args) { // 1.计时器 非重点 StopWatch stopWatch = new StopWatch(); stopWatch.start(); ConfigurableApplicationContext context = null; // 2.支持报告spring应用启动错误 非重点 Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>(); configureHeadlessProperty(); // 3.获取listener 重点 SpringApplicationRunListeners listeners = getRunListeners(args); // 3.1 调用启动中事件的方法 重点 listeners.starting(); try { // 4. ApplicationArguments 重点 ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); // 5. 预处理环境参数 重点 ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments); configureIgnoreBeanInfo(environment); // 6. 打印Banner 非重点 Banner printedBanner = printBanner(environment); // 7.创建ApplicationContext context = createApplicationContext(); // 8.通过spi获取错误报告类 非重点 exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[] { ConfigurableApplicationContext.class }, context); // 9. 预处理上下文 重点 prepareContext(context, environment, listeners, applicationArguments, printedBanner); // 10. 刷新上下文 重点 refreshContext(context); // 11. 后置处理 重点 afterRefresh(context, applicationArguments); // 12. 计时器处理 非重点 stopWatch.stop(); if (this.logStartupInfo) { new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch); } // 13. 调用启动事件的方法 重点 listeners.started(context); // 14. 调用实现ApplicationRunner和CommandLineRunner的方法 重点 callRunners(context, applicationArguments); } catch (Throwable ex) { // 15.异常处理 重点 handleRunFailure(context, ex, exceptionReporters, listeners); throw new IllegalStateException(ex); }
try { // 16. 调用运行中事件 重点 listeners.running(context); } catch (Throwable ex) { // 17 异常处理 重点 handleRunFailure(context, ex, exceptionReporters, null); throw new IllegalStateException(ex); } return context;}
解释 3.获取事件监听
private SpringApplicationRunListeners getRunListeners(String[] args) { Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class }; return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));}
注:通过 SPI 加载 SpringApplicationRunListener 接口的所有实现
SpringApplicationRunListener 生命周期
定义
spring 应用运行方法的收听者
7 个阶段
default void starting() {}default void environmentPrepared(ConfigurableEnvironment environment) {}
default void contextPrepared(ConfigurableApplicationContext context) {}
default void contextLoaded(ConfigurableApplicationContext context) {}
default void started(ConfigurableApplicationContext context) {}
default void running(ConfigurableApplicationContext context) {}
default void failed(ConfigurableApplicationContext context, Throwable exception) {}
实现类
在 spring.factories 文件定义了 SpringApplicationRunListener 的实现类
# Run Listenersorg.springframework.boot.SpringApplicationRunListener=\org.springframework.boot.context.event.EventPublishingRunListener
EventPublishingRunListener 类的实现接口的七个阶段
@Overridepublic void starting() {// 广播开始的事件 this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(this.application, this.args));}
@Overridepublic void environmentPrepared(ConfigurableEnvironment environment) { this.initialMulticaster .multicastEvent(new ApplicationEnvironmentPreparedEvent(this.application, this.args, environment));}
@Overridepublic void contextPrepared(ConfigurableApplicationContext context) { this.initialMulticaster .multicastEvent(new ApplicationContextInitializedEvent(this.application, this.args, context));}
@Overridepublic void contextLoaded(ConfigurableApplicationContext context) { for (ApplicationListener<?> listener : this.application.getListeners()) { if (listener instanceof ApplicationContextAware) { ((ApplicationContextAware) listener).setApplicationContext(context); } context.addApplicationListener(listener); } this.initialMulticaster.multicastEvent(new ApplicationPreparedEvent(this.application, this.args, context));}
@Overridepublic void started(ConfigurableApplicationContext context) { context.publishEvent(new ApplicationStartedEvent(this.application, this.args, context));}
@Overridepublic void running(ConfigurableApplicationContext context) { context.publishEvent(new ApplicationReadyEvent(this.application, this.args, context));}
@Overridepublic void failed(ConfigurableApplicationContext context, Throwable exception) { ApplicationFailedEvent event = new ApplicationFailedEvent(this.application, this.args, context, exception); if (context != null && context.isActive()) { // Listeners have been registered to the application context so we should // use it at this point if we can context.publishEvent(event); } else { // An inactive context may not have a multicaster so we use our multicaster to // call all of the context's listeners instead if (context instanceof AbstractApplicationContext) { for (ApplicationListener<?> listener : ((AbstractApplicationContext) context) .getApplicationListeners()) { this.initialMulticaster.addApplicationListener(listener); } } this.initialMulticaster.setErrorHandler(new LoggingErrorHandler()); this.initialMulticaster.multicastEvent(event); }}
注:EventPublishingRunListener 类的实现其实就是在不同阶段广播不同的事件
CommandLineRunner
定义
属于 springboot 应用的扩展点,在 springboot 应用的 applicationContext 初始化后开始被执行的
@FunctionalInterfacepublic interface CommandLineRunner {
/** * Callback used to run the bean. * @param args incoming main method arguments * @throws Exception on error */ void run(String... args) throws Exception;
}
ApplicationRunner
定义
@FunctionalInterfacepublic interface ApplicationRunner {
/** * Callback used to run the bean. * @param args incoming application arguments * @throws Exception on error */ void run(ApplicationArguments args) throws Exception;
}
构建 ApplicationArguments
public interface ApplicationArguments { String[] getSourceArgs(); Set<String> getOptionNames(); boolean containsOption(String name); List<String> getOptionValues(String name); List<String> getNonOptionArgs();}
public class DefaultApplicationArguments implements ApplicationArguments {
private final Source source;
private final String[] args;
public DefaultApplicationArguments(String... args) { Assert.notNull(args, "Args must not be null"); this.source = new Source(args); this.args = args; } ...... private static class Source extends SimpleCommandLinePropertySource {
Source(String[] args) { super(args); }
}
}
创建 ApplicationContext
public static final String DEFAULT_CONTEXT_CLASS = "org.springframework.context." + "annotation.AnnotationConfigApplicationContext";
public static final String DEFAULT_SERVLET_WEB_CONTEXT_CLASS = "org.springframework.boot." + "web.servlet.context.AnnotationConfigServletWebServerApplicationContext";
public static final String DEFAULT_REACTIVE_WEB_CONTEXT_CLASS = "org.springframework." + "boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext";
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); } } catch (ClassNotFoundException ex) { throw new IllegalStateException( "Unable create a default ApplicationContext, please specify an ApplicationContextClass", ex); } } return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);}
注:
这个比较简单,就是通过 org.springframework.boot.WebApplicationType 创建不同的 ApplicationContext
预处理上下文(prepareContext 方法)
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) { // 设置容器的变量 context.setEnvironment(environment); // 执行容器的后置处理 postProcessApplicationContext(context); // 在上下文刷新之前,将ApplicationContextInitializer应用到上下文中。 applyInitializers(context); // 广播上下文已经准备好的事件 listeners.contextPrepared(context); if (this.logStartupInfo) { logStartupInfo(context.getParent() == null); logStartupProfileInfo(context); } // Add boot specific singleton beans // 在容器中注入applicationArguments 的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()); } // Load the sources Set<Object> sources = getAllSources(); Assert.notEmpty(sources, "Sources must not be empty"); // 加载我们的启动类,将启动类注入容器 load(context, sources.toArray(new Object[0])); // 广播容器已加载的事件 listeners.contextLoaded(context); }
刷新上下文
private void refreshContext(ConfigurableApplicationContext context) { if (this.registerShutdownHook) { try { // 注册容器关闭之前做一些资源回收的操作 context.registerShutdownHook(); } catch (AccessControlException ex) { // Not allowed in some environments. } } refresh(context); }
真正调用 AbstractApplicationContext#refresh
protected void refresh(ApplicationContext applicationContext) { Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext); ((AbstractApplicationContext) applicationContext).refresh(); }
AbstractApplicationContext#refresh 这个方法主要是 spring 应用上下文生命周期的方法,这里只做简单的介绍,不往深处深究了
public void refresh() throws BeansException, IllegalStateException {synchronized (this.startupShutdownMonitor) {// 留给子类去实现创建 BeanFactory.ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 预处理BeanFactory对象 prepareBeanFactory(beanFactory);
try { // 允许子类中对BeanFactory进行后期处理. postProcessBeanFactory(beanFactory);
// I调用注册为BeanFactory的工厂处理器 invokeBeanFactoryPostProcessors(beanFactory);
// 注册实现BeanPostProcessor接口的bean后置处理类 registerBeanPostProcessors(beanFactory);
// 初始化application实现广播器 initApplicationEventMulticaster();
// 又是留给子类实现刷新操作额 onRefresh();
// 检查bean的监听器并注册它们。. registerListeners();
//完成Bean的初始化 finishBeanFactoryInitialization(beanFactory);
// 发布上下文刷新完成的时间 finishRefresh(); }catch (BeansException ex) { // Propagate exception to caller. throw ex; }finally { resetCommonCaches(); } }}
后置处理
protected void afterRefresh(ConfigurableApplicationContext context, ApplicationArguments args) { }
这里是空方法,需要子类自己实现
调用启动事件的方法
void started(ConfigurableApplicationContext context) { for (SpringApplicationRunListener listener : this.listeners) { listener.started(context); } }
调用实现 ApplicationRunner 和 CommandLineRunner 的方法
private void callRunners(ApplicationContext context, ApplicationArguments args) { List<Object> runners = new ArrayList<>(); runners.addAll(context.getBeansOfType(ApplicationRunner.class).values()); runners.addAll(context.getBeansOfType(CommandLineRunner.class).values()); //排序 AnnotationAwareOrderComparator.sort(runners); for (Object runner : new LinkedHashSet<>(runners)) { if (runner instanceof ApplicationRunner) { callRunner((ApplicationRunner) runner, args); } if (runner instanceof CommandLineRunner) { callRunner((CommandLineRunner) runner, args); } } }
总结
运用了观察者模式
运用 SPI 技术来动态加载具体的实现类,dubbo 也是这样实现的,想了解 dubbo 的 spi 可以查看下面文章:Dubbo的SPI
SpringBoot 启动加载器有两种方式:CommandLineRunner 和 ApplicationRunner,并且是在应用程序启动完成后
CommandLineRunner 和 ApplicationRunner 的执行顺序,是将他们都统一添加到同一个 list 然后根据 @Order 注解派去
零点999
还未添加个人签名 2018.01.19 加入
还未添加个人简介











评论