摘要
看完本文你将掌握如下知识点:
SpringApplication的作用及运行过程
SpringBootServletInitializer的作用及运行过程
PS:本节内容略显枯燥,如果对SpringBoot的启动过程不感兴趣,可以略过。
SpringBoot系列 :Spring Boot学习笔记
深入了解SpringApplication
1 2 3 4 5 6 @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 2 3 4 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对象
1 2 3 4 5 6 7 8 9 10 public SpringApplication (Object... sources) { this .bannerMode = Mode.CONSOLE; this .logStartupInfo = true ; this .addCommandLineProperties = true ; this .headless = true ; this .registerShutdownHook = true ; this .additionalProfiles = new HashSet (); this .initialize(sources); }
PS:Headless参考资料:在 Java SE 平台上使用 Headless 模式
3.初始化相关对象和属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 private void initialize (Object[] sources) { if (sources != null && sources.length > 0 ) { this .sources.addAll(Arrays.asList(sources)); } this .webEnvironment = this .deduceWebEnvironment(); this .setInitializers(this .getSpringFactoriesInstances(ApplicationContextInitializer.class)); this .setListeners(this .getSpringFactoriesInstances(ApplicationListener.class)); this .mainApplicationClass = this .deduceMainApplicationClass(); }
3.1 判断是否是web运行环境
如果classpath中是否含有WEB_ENVIRONMENT_CLASSES 指定的全部类,则返回true,用于创建指定类型的ApplicationContext对象。
1 2 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做进一步的设置和处理。
1 2 3 public interface ApplicationContextInitializer <C extends ConfigurableApplicationContext > { void initialize (C var1) ; }
spring-boot-1.4.2.RELEASE.jar 中的META-INF/spring.factories 包含的ApplicationContextInitializer
1 2 3 4 5 6 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
1 2 3 4 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 负责接收。
1 2 3 public interface ApplicationListener <E extends ApplicationEvent > extends EventListener { void onApplicationEvent (E var1) ; }
SpringBoot只提供了一个SpringApplicationRunListener 的实现类,就是EventPublishingRunListener ,起作用就是在SpringBoot启动过程中,负责注册ApplicationListener 监听器,在不同的时点发布不同的事件类型,如果有哪些ApplicationListener 的实现类监听了这些事件,则可以接收并处理。
1 2 3 4 5 6 7 8 9 10 11 12 public interface SpringApplicationRunListener { void started () ; void environmentPrepared (ConfigurableEnvironment var1) ; void contextPrepared (ConfigurableApplicationContext var1) ; void contextLoaded (ConfigurableApplicationContext var1) ; void finished (ConfigurableApplicationContext var1, Throwable var2) ; }
spring-boot-1.4.2.RELEASE.jar 中的META-INF/spring.factories 包含的ApplicationListener
1 2 3 4 5 6 7 8 9 10 11 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
1 2 3 org.springframework.context.ApplicationListener=\ org.springframework.boot.autoconfigure.BackgroundPreinitializer
spring-boot-1.4.2.RELEASE.jar 中的META-INF/spring.factories 包含的SpringApplicationRunListener
1 2 3 org.springframework.boot.SpringApplicationRunListener=\ org.springframework.boot.context.event.EventPublishingRunListener
3.4 获得当前执行main方法的类对象,这里就是SpringBootWebDemoApplication的实例。
4.核心方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 public ConfigurableApplicationContext run (String... args) { StopWatch stopWatch = new StopWatch (); stopWatch.start(); ConfigurableApplicationContext context = null ; Object analyzers = null ; this .configureHeadlessProperty(); SpringApplicationRunListeners listeners = this .getRunListeners(args); listeners.started(); try { DefaultApplicationArguments ex = new DefaultApplicationArguments (args); ConfigurableEnvironment environment = this .prepareEnvironment(listeners, ex); Banner printedBanner = this .printBanner(environment); context = this .createApplicationContext(); new FailureAnalyzers (context); this .prepareContext(context, environment, listeners, ex, printedBanner); this .refreshContext(context); this .afterRefresh(context, ex); listeners.finished(context, (Throwable)null ); stopWatch.stop(); if (this .logStartupInfo) { (new StartupInfoLogger (this .mainApplicationClass)).logStarted(this .getApplicationLog(), stopWatch); } return context; } catch (Throwable var9) { this .handleRunFailure(context, listeners, (FailureAnalyzers)analyzers, var9); throw new IllegalStateException (var9); } }
spring-boot-1.4.2.RELEASE.jar 中的META-INF/spring.factories 包含的FailureAnalyzer 和FailureAnalysisReporters
1 2 3 4 5 6 7 8 9 10 11 12 13 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 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
1 2 3 4 5 6 7 8 9 10 11 12 13 @SpringBootApplication public class SpringBootWebDemoApplication { public static void main (String[] 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
1 2 3 4 5 6 7 8 org.springframework.context.ApplicationContextInitializer=\ xxx.xxx.MyApplicationContextInitializer1,\ xxx.xxx.MyApplicationContextInitializer2 org.springframework.context.ApplicationListener=\ xxx.xxx.MyApplicationListener1,\ xxx.xxx.MyApplicationListener2
3.至于ApplicationRunner和CommandLineRunner,只需要在其实现类上加上**@Component**注解或者在@Configuration配置类中通过@Bean注解注入。
深入了解SpringBootServletInitializer
熟悉了SpringApplication 的原理之后,我们再来了解SpringBootServletInitializer 的原理就比较容易了。
1 2 3 4 5 6 7 public class ServletInitializer extends SpringBootServletInitializer { @Override protected SpringApplicationBuilder configure (SpringApplicationBuilder application) { return application.sources(DemoWarApplication.class); } }
SpringBootServletInitializer就是一个org.springframework.web.WebApplicationInitializer ,容器启动时会调用其onStartup(ServletContext servletContext)方法,接下来我么就来看一下这个方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 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):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 protected WebApplicationContext createRootApplicationContext (ServletContext servletContext) { 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)}); } builder.initializers(new ApplicationContextInitializer []{new ServletContextApplicationContextInitializer (servletContext)}); builder.listeners(new ApplicationListener []{new ServletContextApplicationListener (servletContext)}); builder.contextClass(AnnotationConfigEmbeddedWebApplicationContext.class); 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); } return this .run(application); }
说明
SpringBootServletInitializer 的执行过程,简单来说就是通过SpringApplicationBuilder 构建并封装SpringApplication对象,并最终调用SpringApplication的run方法的过程。
扩展SpringBootServletInitializer
与扩展SpringApplication 类似,ApplicationContextInitializer 和ApplicationListener 可以基于SpringApplicationBuilder 提供的public方法进行扩展
1 2 3 4 5 6 7 8 9 10 public class ServletInitializer extends SpringBootServletInitializer { @Override protected SpringApplicationBuilder configure (SpringApplicationBuilder application) { application.initializers(MyApplicationContextInitializer1,MyApplicationContextInitializer2); application.listeners(MyApplicationListener1,MyApplicationListener2) return application.sources(DemoWarApplication.class); } }