疯狂java


您现在的位置: 疯狂软件 >> 新闻资讯 >> 正文

深入了解SpringBoot的启动过程


 

   

  摘要

  看完本文你将掌握如下知识点:

  SpringApplication的作用及运行过程

  SpringBootServletInitializer的作用及运行过程

  PS:本节内容略显枯燥,如果对SpringBoot的启动过程不感兴趣,可以略过。

  SpringBoot系列:Spring Boot学习笔记

  深入了解SpringApplication

  @SpringBootApplication

  public class SpringBootWebDemoApplication {

  public static void main(String[] args) {

  SpringApplication.run(SpringBootWebDemoApplication.class, args);

  }

  }

  这就是SpringBoot的启动入口,通过前面的学习我们大体上了解了@SpringBootApplication的作用,接下来我们来认识一下SpringApplication。

  SpringApplication (Spring Boot Docs 1.4.2.RELEASE API)。

  SpringApplication.run(SpringBootWebDemoApplication.class, args);

  通过源码我们来看一下SpringApplication.run()方法的执行过程

  1.调用static方法

  //1

  public static ConfigurableApplicationContext run(Object source, String... args) {return run(new Object[]{source}, args);}

  public static ConfigurableApplicationContext run(Object[] sources, String[] args)

  {return (new SpringApplication(sources)).run(args);}

  2.创建SpringApplication对象

  //2

  public SpringApplication(Object... sources) {

  this.bannerMode = Mode.CONSOLE; //banner的打印模式,此时是控制台模式

  this.logStartupInfo = true; //开启日志

  this.addCommandLineProperties = true;//启用CommandLineProperties

  this.headless = true;//开启headless模式支持

  this.registerShutdownHook = true;//启用注册ShutdownHook,用于在非Web应用中关闭IoC容器和资源

  this.additionalProfiles = new HashSet();

  this.initialize(sources);//初始化

  }

  PS:Headless参考资料:在 Java SE 平台上使用 Headless 模式

  3.初始化相关对象和属性

  //3

  private void initialize(Object[] sources) {

  if(sources != null && sources.length > 0) {

  this.sources.addAll(Arrays.asList(sources));

  }

  //3.1判断是否是web运行环境,如果classpath中是否含有**WEB_ENVIRONMENT_CLASSES**指定的全部类,则返回true

  this.webEnvironment = this.deduceWebEnvironment();

  //3.2找到*META-INF/spring.factories*中声明的所有ApplicationContextInitializer的实现类并将其实例化

  this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));

  //3.3找到*META-INF/spring.factories*中声明的所有ApplicationListener的实现类并将其实例化

  this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));

  //3.4获得当前执行main方法的类对象,这里就是SpringBootWebDemoApplication的实例

  this.mainApplicationClass = this.deduceMainApplicationClass();

  }

  3.1 判断是否是web运行环境

  如果classpath中是否含有WEB_ENVIRONMENT_CLASSES指定的全部类,则返回true,用于创建指定类型的ApplicationContext对象。

  //3.1

  private static final String[] WEB_ENVIRONMENT_CLASSES = new String[]{"javax.servlet.Servlet", "org.springframework.web.context.ConfigurableWebApplicationContext"};

  3.2 大体的过程就是通过SpringFactoriesLoader检索META-INF/spring.factories,找到声明的所有ApplicationContextInitializer的实现类并将其实例化。

  ApplicationContextInitializer是Spring框架中的接口,其作用可以理解为在ApplicationContext执行refresh之前,调用ApplicationContextInitializer的initialize()方法,对ApplicationContext做进一步的设置和处理。

  public interface ApplicationContextInitializer {

  void initialize(C var1);

  }

  spring-boot-1.4.2.RELEASE.jar中的META-INF/spring.factories包含的ApplicationContextInitializer

  # Application Context Initializers

  org.springframework.context.ApplicationContextInitializer=

  org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,

  org.springframework.boot.context.ContextIdApplicationContextInitializer,

  org.springframework.boot.context.config.DelegatingApplicationContextInitializer,

  org.springframework.boot.context.web.ServerPortInfoApplicationContextInitializer

  spring-boot-autoconfigure-1.4.2.RELEASE.jar中的META-INF/spring.factories包含的ApplicationContextInitializer

  # Initializers

  org.springframework.context.ApplicationContextInitializer=

  org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,

  org.springframework.boot.autoconfigure.logging.AutoConfigurationReportLoggingInitializer

  3.3 大体的过程就是通过SpringFactoriesLoader检索META-INF/spring.factories,找到声明的所有ApplicationListener的实现类并将其实例化。

  ApplicationListener是Spring框架中的接口,就是事件监听器,其作用可以理解为在SpringApplicationRunListener发布通知事件时,由ApplicationListener负责接收。

  public interface ApplicationListener extends EventListener {

  void onApplicationEvent(E var1);

  }

  SpringBoot只提供了一个SpringApplicationRunListener的实现类,就是EventPublishingRunListener,起作用就是在SpringBoot启动过程中,负责注册ApplicationListener监听器,在不同的时点发布不同的事件类型,如果有哪些ApplicationListener的实现类监听了这些事件,则可以接收并处理。

  public interface SpringApplicationRunListener {

  //通知监听器,SpringBoot开始执行

  void started();

  //通知监听器,Environment准备完成

  void environmentPrepared(ConfigurableEnvironment var1);

  //通知监听器,ApplicationContext已经创建并初始化完成

  void contextPrepared(ConfigurableApplicationContext var1);

  //通知监听器,ApplicationContext已经完成IoC配置加载

  void contextLoaded(ConfigurableApplicationContext var1);

  //通知监听器,SpringBoot启动完成

  void finished(ConfigurableApplicationContext var1, Throwable var2);

  }

  spring-boot-1.4.2.RELEASE.jar中的META-INF/spring.factories包含的ApplicationListener

  # Application Listeners

  org.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-boot-autoconfigure-1.4.2.RELEASE.jar中的META-INF/spring.factories包含的ApplicationListener

  # Application Listeners

  org.springframework.context.ApplicationListener=

  org.springframework.boot.autoconfigure.BackgroundPreinitializer

  spring-boot-1.4.2.RELEASE.jar中的META-INF/spring.factories包含的SpringApplicationRunListener

  # Run Listeners

  org.springframework.boot.SpringApplicationRunListener=

  org.springframework.boot.context.event.EventPublishingRunListener

  3.4 获得当前执行main方法的类对象,这里就是SpringBootWebDemoApplication的实例。

  4.核心方法

  //4

  public ConfigurableApplicationContext run(String... args) {

  //开启任务执行时间监听器

  StopWatch stopWatch = new StopWatch();

  stopWatch.start();

  ConfigurableApplicationContext context = null;

  Object analyzers = null;

  //设置系统属性『java.awt.headless』,为true则启用headless模式支持

  this.configureHeadlessProperty();

  //通过*SpringFactoriesLoader*检索*META-INF/spring.factories*,

  //找到声明的所有SpringApplicationRunListener的实现类并将其实例化,

  //之后逐个调用其started()方法,广播SpringBoot要开始执行了。

  SpringApplicationRunListeners listeners = this.getRunListeners(args);

  listeners.started();

  try {

  DefaultApplicationArguments ex = new DefaultApplicationArguments(args);

  //创建并配置当前SpringBoot应用将要使用的Environment(包括配置要使用的PropertySource以及Profile),

  //并遍历调用所有的SpringApplicationRunListener的environmentPrepared()方法,广播Environment准备完毕。

  ConfigurableEnvironment environment = this.prepareEnvironment(listeners, ex);

  //决定是否打印Banner

  Banner printedBanner = this.printBanner(environment);

  //根据webEnvironment的值来决定创建何种类型的ApplicationContext对象

  //如果是web环境,则创建org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext

  //否则创建org.springframework.context.annotation.AnnotationConfigApplicationContext

  context = this.createApplicationContext();

  //注册异常分析器

  new FailureAnalyzers(context);

  //为ApplicationContext加载environment,之后逐个执行ApplicationContextInitializer的initialize()方法来进一步封装ApplicationContext,

  //并调用所有的SpringApplicationRunListener的contextPrepared()方法,【EventPublishingRunListener只提供了一个空的contextPrepared()方法】,

  //之后初始化IoC容器,并调用SpringApplicationRunListener的contextLoaded()方法,广播ApplicationContext的IoC加载完成,

  //这里就包括通过**@EnableAutoConfiguration**导入的各种自动配置类。

  this.prepareContext(context, environment, listeners, ex, printedBanner);

  //初始化所有自动配置类,调用ApplicationContext的refresh()方法

  this.refreshContext(context);

  //遍历所有注册的ApplicationRunner和CommandLineRunner,并执行其run()方法。

  //该过程可以理解为是SpringBoot完成ApplicationContext初始化前的最后一步工作,

  //我们可以实现自己的ApplicationRunner或者CommandLineRunner,来对SpringBoot的启动过程进行扩展。

  this.afterRefresh(context, ex);

  //调用所有的SpringApplicationRunListener的finished()方法,广播SpringBoot已经完成了ApplicationContext初始化的全部过程。

  listeners.finished(context, (Throwable)null);

  //关闭任务执行时间监听器

  stopWatch.stop();

  //如果开启日志,则答应执行是时间

  if(this.logStartupInfo) {

  (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);

  }

  return context;

  } catch (Throwable var9) {

  //调用异常分析器打印报告,调用所有的SpringApplicationRunListener的finished()方法将异常信息发布出去

  this.handleRunFailure(context, listeners, (FailureAnalyzers)analyzers, var9);

  throw new IllegalStateException(var9);

  }

  }

  spring-boot-1.4.2.RELEASE.jar中的META-INF/spring.factories包含的FailureAnalyzer和FailureAnalysisReporters

  # Failure Analyzers

  org.springframework.boot.diagnostics.FailureAnalyzer=

  org.springframework.boot.diagnostics.analyzer.BeanCurrentlyInCreationFailureAnalyzer,

  org.springframework.boot.diagnostics.analyzer.BeanNotOfRequiredTypeFailureAnalyzer,

  org.springframework.boot.diagnostics.analyzer.BindFailureAnalyzer,

  org.springframework.boot.diagnostics.analyzer.ConnectorStartFailureAnalyzer,

  org.springframework.boot.diagnostics.analyzer.NoUniqueBeanDefinitionFailureAnalyzer,

  org.springframework.boot.diagnostics.analyzer.PortInUseFailureAnalyzer,

  org.springframework.boot.diagnostics.analyzer.ValidationExceptionFailureAnalyzer

  # FailureAnalysisReporters

  org.springframework.boot.diagnostics.FailureAnalysisReporter=

  org.springframework.boot.diagnostics.LoggingFailureAnalysisReporter

  说明

  SpringBoot的启动过程,实际上就是对ApplicationContext的初始化过程。

  ApplicationContext创建后立刻为其设置Environmen,并由ApplicationContextInitializer对其进一步封装。

  通过SpringApplicationRunListener在ApplicationContext初始化过程中各个时点发布各种广播事件,并由ApplicationListener负责接收广播事件。

  初始化过程中完成IoC的注入,包括通过@EnableAutoConfiguration导入的各种自动配置类。

  初始化完成前调用ApplicationRunner和CommandLineRunner的实现类。

  扩展SpringApplication

  通过上面的学习,我们基本上了解了,如果要对SpringApplication进行扩展,我们可以选择如下三种方案:

  创建ApplicationContextInitializer的实现类

  创建ApplicationListener的实现类

  创建ApplicationRunner和CommandLineRunner的实现类

  1.可以通过如下方式加载自定义的ApplicationContextInitializer和ApplicationListener

  @SpringBootApplication

  public class SpringBootWebDemoApplication {

  public static void main(String[] args) {

  //SpringApplication.run(SpringBootWebDemoApplication.class, args);

  SpringApplication springApplication = new SpringApplication(SpringBootWebDemoApplication.class);

  springApplication.addInitializers(MyApplicationContextInitializer1,MyApplicationContextInitializer2);

  springApplication.addListeners(MyApplicationListener1,MyApplicationListener2);

  springApplication.run(args);

  }

  }

  2.也可以在当前项目的类路径下创建META-INF/spring.factories文件,并声明相应的ApplicationContextInitializer和ApplicationListener

  org.springframework.context.ApplicationContextInitializer=

  xxx.xxx.MyApplicationContextInitializer1,

  xxx.xxx.MyApplicationContextInitializer2

  # Application Listeners

  org.springframework.context.ApplicationListener=

  xxx.xxx.MyApplicationListener1,

  xxx.xxx.MyApplicationListener2

  3.至于ApplicationRunner和CommandLineRunner,只需要在其实现类上加上@Component注解或者在@Configuration配置类中通过@Bean注解注入。

  深入了解SpringBootServletInitializer

  熟悉了SpringApplication的原理之后,我们再来了解SpringBootServletInitializer的原理就比较容易了。

  public class ServletInitializer extends SpringBootServletInitializer {

  @Override

  protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {

  return application.sources(DemoWarApplication.class);

  }

  }

  SpringBootServletInitializer就是一个org.springframework.web.context.WebApplicationContext,容器启动时会调用其onStartup(ServletContext servletContext)方法,接下来我么就来看一下这个方法:

  public void onStartup(ServletContext servletContext) throws ServletException {

  this.logger = LogFactory.getLog(this.getClass());

  final WebApplicationContext rootAppContext = this.createRootApplicationContext(servletContext);

  if(rootAppContext != null) {

  servletContext.addListener(new ContextLoaderListener(rootAppContext) {

  public void contextInitialized(ServletContextEvent event) {

  }

  });

  } else {

  this.logger.debug("No ContextLoaderListener registered, as createRootApplicationContext() did not return an application context");

  }

  }

  这里的核心方法就是createRootApplicationContext(servletContext):

  protected WebApplicationContext createRootApplicationContext(ServletContext servletContext) {

  //创建SpringApplicationBuilder,并用其生产出SpringApplication对象

  SpringApplicationBuilder builder = this.createSpringApplicationBuilder();

  builder.main(this.getClass());

  ApplicationContext parent = this.getExistingRootWebApplicationContext(servletContext);

  if(parent != null) {

  this.logger.info("Root context already created (using as parent).");

  servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, (Object)null);

  builder.initializers(new ApplicationContextInitializer[]{new ParentContextApplicationContextInitializer(parent)});

  }

  //初始化并封装SpringApplicationBuilder对象,为SpringApplication对象增加ApplicationContextInitializer和ApplicationListener做准备

  builder.initializers(new ApplicationContextInitializer[]{new ServletContextApplicationContextInitializer(servletContext)});

  builder.listeners(new ApplicationListener[]{new ServletContextApplicationListener(servletContext)});

  //指定创建的ApplicationContext类型

  builder.contextClass(AnnotationConfigEmbeddedWebApplicationContext.class);

  //传递入口类,并构建SpringApplication对象

  //可以通过configure()方法对SpringBootServletInitializer进行扩展

  builder = this.configure(builder);

  SpringApplication application = builder.build();

  if(application.getSources().isEmpty() && AnnotationUtils.findAnnotation(this.getClass(), Configuration.class) != null) {

  application.getSources().add(this.getClass());

  }

  Assert.state(!application.getSources().isEmpty(), "No SpringApplication sources have been defined. Either override the configure method or add an @Configuration annotation");

  if(this.registerErrorPageFilter) {

  application.getSources().add(ErrorPageFilter.class);

  }

  //最后调用SpringApplication的run方法

  return this.run(application);

  }

  说明

  SpringBootServletInitializer的执行过程,简单来说就是通过SpringApplicationBuilder构建并封装SpringApplication对象,并最终调用SpringApplication的run方法的过程。

  扩展SpringBootServletInitializer

  与扩展SpringApplication类似,ApplicationContextInitializer和ApplicationListener可以基于SpringApplicationBuilder提供的public方法进行扩展

  public class ServletInitializer extends SpringBootServletInitializer {

  @Override

  protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {

  application.initializers(MyApplicationContextInitializer1,MyApplicationContextInitializer2);

  application.listeners(MyApplicationListener1,MyApplicationListener2)

  return application.sources(DemoWarApplication.class);

  }

  }