写点什么

[SpringBoot 源码分析]SpringBoot 如何启动

作者:零点999
  • 2021 年 11 月 23 日
  • 本文字数:8394 字

    阅读完需:约 28 分钟

[SpringBoot源码分析]SpringBoot如何启动

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 加入

还未添加个人简介

评论

发布
暂无评论
[SpringBoot源码分析]SpringBoot如何启动