SpringBoot 默认使用的嵌入式 Servlet 容器为 Tomcat,经过依赖关系就能够看到:html
问题:java
在普通 web 程序中咱们若是须要修改 Tomcat 配置则可经过 Tomcat 目录下 conf/server.xml 修改,而在 SpringBoot 中咱们只须要在项目配置文件中经过 server 节下的相关属性便可修改容器相关配置,如:web
# 通用 Servlet 容器配置 server.port=8080 server.context-path=/ # Tomcat 配置 server.tomcat.uri-encoding=utf-8
除了上述配置的方式,SpringBoot 还为咱们提供了嵌入式 Servlet 容器的定制器来定制相关配置,例:spring
@Bean public EmbeddedServletContainerCustomizer embeddedServletContainerCustomizer(){ return new EmbeddedServletContainerCustomizer() { // 定制嵌入式 Servlet 容器相关规则 @Override public void customize(ConfigurableEmbeddedServletContainer container) { // container 即为容器对象 container.setPort(8088); } }; }
配置文件中 server 节对应的配置属性类内部也是经过该方式定义容器配置的。apache
若是是在一个标准的 web 工程中,咱们能够经过 WEB-INF/web.xml 来注册三大组件,即 Servlet、Filter、Listener。而 SpringBoot 项目默认是以 jar 包的方式经过嵌入式的 Servlet 容器来启动 web 应用,没有 web.xml,注册三大组件则须要按照如下方式:tomcat
@Bean public ServletRegistrationBean myServlet(){ return new ServletRegistrationBean(new MyServlet(), "/servlet/hello"); }
@Bean public FilterRegistrationBean myFilter(){ FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(); filterRegistrationBean.setFilter(new MyFilter()); filterRegistrationBean.setUrlPatterns(Arrays.asList("/servlet/hello","/login")); return filterRegistrationBean; }
@Bean public ServletListenerRegistrationBean myListener(){ return new ServletListenerRegistrationBean(new MyListener()); }
SpringMVC 的核心 Servlet 在 SpringBoot 中就是以此种方式注册,以下:springboot
1 @Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME) 2 public DispatcherServlet dispatcherServlet() { 3 DispatcherServlet dispatcherServlet = new DispatcherServlet(); 4 dispatcherServlet.setDispatchOptionsRequest( 5 this.webMvcProperties.isDispatchOptionsRequest()); 6 dispatcherServlet.setDispatchTraceRequest( 7 this.webMvcProperties.isDispatchTraceRequest()); 8 dispatcherServlet.setThrowExceptionIfNoHandlerFound( 9 this.webMvcProperties.isThrowExceptionIfNoHandlerFound()); 10 return dispatcherServlet; 11 } 12 13 @Configuration 14 @Conditional(DispatcherServletRegistrationCondition.class) 15 @ConditionalOnClass(ServletRegistration.class) 16 @EnableConfigurationProperties(WebMvcProperties.class) 17 @Import(DispatcherServletConfiguration.class) 18 protected static class DispatcherServletRegistrationConfiguration { 19 20 private final ServerProperties serverProperties; 21 22 private final WebMvcProperties webMvcProperties; 23 24 private final MultipartConfigElement multipartConfig; 25 26 public DispatcherServletRegistrationConfiguration( 27 ServerProperties serverProperties, WebMvcProperties webMvcProperties, 28 ObjectProvider<MultipartConfigElement> multipartConfigProvider) { 29 this.serverProperties = serverProperties; 30 this.webMvcProperties = webMvcProperties; 31 this.multipartConfig = multipartConfigProvider.getIfAvailable(); 32 } 33 34 @Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME) 35 @ConditionalOnBean(value = DispatcherServlet.class, name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME) 36 public ServletRegistrationBean dispatcherServletRegistration( 37 DispatcherServlet dispatcherServlet) { 38 ServletRegistrationBean registration = new ServletRegistrationBean( 39 dispatcherServlet, this.serverProperties.getServletMapping()); 40 registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME); 41 registration.setLoadOnStartup( 42 this.webMvcProperties.getServlet().getLoadOnStartup()); 43 if (this.multipartConfig != null) { 44 registration.setMultipartConfig(this.multipartConfig); 45 } 46 return registration; 47 } 48 49 }
咱们知道,SpringBoot 默认使用的嵌入式容器为 Tomcat,SpringBoot 默认还支持将其切换为 Jetty(对长链接有更好的支持) 或 Undertow(不支持 JSP)。服务器
其实经过容器接口的实现类咱们就能够看到其它两个容器工厂的实现:app
首先咱们要排除默认依赖的 Tomcat 场景启动器:less
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <exclusions> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> </exclusion> </exclusions> </dependency>
再引入 Jetty 的场景启动器便可:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jetty</artifactId> </dependency>
同 Jetty 先排除 Tomcat 的场景启动器,接着引入 Undertow 的场景启动器:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-undertow</artifactId> </dependency>
依旧是从嵌入式 Servlet 容器的自动配置类看起:
1 @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE) 2 @Configuration 3 @ConditionalOnWebApplication 4 @Import(BeanPostProcessorsRegistrar.class) 5 public class EmbeddedServletContainerAutoConfiguration { 6 @Configuration 7 @ConditionalOnClass({ Servlet.class, Tomcat.class }) // 判断当前环境是否引入了嵌入式 Tomcat 依赖 8 @ConditionalOnMissingBean(value = EmbeddedServletContainerFactory.class, search = SearchStrategy.CURRENT) // 当 IoC 容器中没有自定义的嵌入式 Servlet 容器工厂下方代码才生效 9 public static class EmbeddedTomcat { 10 @Bean 11 public TomcatEmbeddedServletContainerFactory tomcatEmbeddedServletContainerFactory() { 12 return new TomcatEmbeddedServletContainerFactory(); 13 } 14 } 15 16 @Configuration 17 @ConditionalOnClass({ Servlet.class, Server.class, Loader.class, 18 WebAppContext.class }) // 判断当前环境是否引入了嵌入式 Jetty 依赖 19 @ConditionalOnMissingBean(value = EmbeddedServletContainerFactory.class, search = SearchStrategy.CURRENT) // 当 IoC 容器中没有自定义的嵌入式 Servlet 容器工厂下方代码才生效 20 public static class EmbeddedJetty { 21 @Bean 22 public JettyEmbeddedServletContainerFactory jettyEmbeddedServletContainerFactory() { 23 return new JettyEmbeddedServletContainerFactory(); 24 } 25 } 26 27 @Configuration 28 @ConditionalOnClass({ Servlet.class, Undertow.class, SslClientAuthMode.class }) // 判断当前环境是否引入了嵌入式 Undertow 依赖 29 @ConditionalOnMissingBean(value = EmbeddedServletContainerFactory.class, search = SearchStrategy.CURRENT) // 当 IoC 容器中没有自定义的嵌入式 Servlet 容器工厂下方代码才生效 30 public static class EmbeddedUndertow { 31 @Bean 32 public UndertowEmbeddedServletContainerFactory undertowEmbeddedServletContainerFactory() { 33 return new UndertowEmbeddedServletContainerFactory(); 34 } 35 } 36 37 public static class BeanPostProcessorsRegistrar 38 implements ImportBeanDefinitionRegistrar, BeanFactoryAware { 39 40 private ConfigurableListableBeanFactory beanFactory; 41 42 @Override 43 public void setBeanFactory(BeanFactory beanFactory) throws BeansException { 44 if (beanFactory instanceof ConfigurableListableBeanFactory) { 45 this.beanFactory = (ConfigurableListableBeanFactory) beanFactory; 46 } 47 } 48 49 @Override 50 public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, 51 BeanDefinitionRegistry registry) { 52 if (this.beanFactory == null) { 53 return; 54 } 55 registerSyntheticBeanIfMissing(registry, 56 "embeddedServletContainerCustomizerBeanPostProcessor", 57 EmbeddedServletContainerCustomizerBeanPostProcessor.class); 58 registerSyntheticBeanIfMissing(registry, 59 "errorPageRegistrarBeanPostProcessor", 60 ErrorPageRegistrarBeanPostProcessor.class); 61 } 62 63 private void registerSyntheticBeanIfMissing(BeanDefinitionRegistry registry, 64 String name, Class<?> beanClass) { 65 if (ObjectUtils.isEmpty( 66 this.beanFactory.getBeanNamesForType(beanClass, true, false))) { 67 RootBeanDefinition beanDefinition = new RootBeanDefinition(beanClass); 68 beanDefinition.setSynthetic(true); 69 registry.registerBeanDefinition(name, beanDefinition); 70 } 71 } 72 73 } 74 }
很明了,自动配置类中使用三个静态内部类来分别注册不一样的嵌入式的 Servlet 容器工厂,按顺序从上到下分别为 Tomcat、Jetty、Undertow。具体要注册哪一个 Servlet 容器工厂须要根据当前环境的依赖来决定,若是当前环境只引入了 Tomcat 场景依赖,那么就仅仅会注册 Tomcat 的容器工厂,其它两个 Servlet 容器工厂就不会被注册。这就是咱们引入依赖便能切换 Servlet 容器的缘由。
如今能够知道的是,在自动配置类中只是注册了一个嵌入式 Servlet 容器的工厂 bean,而并非注册了嵌入式 Servlet 容器的实例 bean,顾名思义,嵌入式 Servlet 容器的工厂确定是用来建立嵌入式 Servlet 容器的实例,那么这个嵌入式 Servlet 容器的实例是在什么时候被建立和启动的呢?
以默认的 Tomcat 容器为例,当咱们启动一个 SpringBoot 程序时,咱们会发现嵌入式 Tomcat 会随之启动,咱们直接从 SpringBoot 程序的入口 run 方法开始看起:
1 public static ConfigurableApplicationContext run(Object source, String... args) { 2 return run(new Object[] { source }, args); 3 } 4 5 public static ConfigurableApplicationContext run(Object[] sources, String[] args) { 6 return new SpringApplication(sources).run(args); 7 } 8 9 public ConfigurableApplicationContext run(String... args) { 10 StopWatch stopWatch = new StopWatch(); 11 stopWatch.start(); 12 ConfigurableApplicationContext context = null; 13 FailureAnalyzers analyzers = null; 14 configureHeadlessProperty(); 15 SpringApplicationRunListeners listeners = getRunListeners(args); 16 listeners.starting(); 17 try { 18 ApplicationArguments applicationArguments = new DefaultApplicationArguments( 19 args); 20 ConfigurableEnvironment environment = prepareEnvironment(listeners, 21 applicationArguments); 22 Banner printedBanner = printBanner(environment); 23 context = createApplicationContext(); 24 analyzers = new FailureAnalyzers(context); 25 prepareContext(context, environment, listeners, applicationArguments, 26 printedBanner); 27 refreshContext(context); 28 afterRefresh(context, applicationArguments); 29 listeners.finished(context, null); 30 stopWatch.stop(); 31 if (this.logStartupInfo) { 32 new StartupInfoLogger(this.mainApplicationClass) 33 .logStarted(getApplicationLog(), stopWatch); 34 } 35 return context; 36 } 37 catch (Throwable ex) { 38 handleRunFailure(context, listeners, analyzers, ex); 39 throw new IllegalStateException(ex); 40 } 41 }
依次执行这几个 run 方法,接着进到 27 行的 refreshContext(context) 方法:
1 private void refreshContext(ConfigurableApplicationContext context) { 2 refresh(context); 3 if (this.registerShutdownHook) { 4 try { 5 context.registerShutdownHook(); 6 } 7 catch (AccessControlException ex) { 8 // Not allowed in some environments. 9 } 10 } 11 }
接着执行第 2 行的 refresh(context) 方法:
1 protected void refresh(ApplicationContext applicationContext) { 2 Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext); 3 ((AbstractApplicationContext) applicationContext).refresh(); 4 }
继续到第 3 行的 ((AbstractApplicationContext) applicationContext).refresh() 方法:
1 @Override 2 public final void refresh() throws BeansException, IllegalStateException { 3 try { 4 super.refresh(); 5 } 6 catch (RuntimeException ex) { 7 stopAndReleaseEmbeddedServletContainer(); 8 throw ex; 9 } 10 }
调用父类的 refresh() 方法:
1 public void refresh() throws BeansException, IllegalStateException { 2 synchronized(this.startupShutdownMonitor) { 3 this.prepareRefresh(); 4 ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory(); 5 this.prepareBeanFactory(beanFactory); 6 7 try { 8 this.postProcessBeanFactory(beanFactory); 9 this.invokeBeanFactoryPostProcessors(beanFactory); 10 this.registerBeanPostProcessors(beanFactory); 11 this.initMessageSource(); 12 this.initApplicationEventMulticaster(); 13 this.onRefresh(); 14 this.registerListeners(); 15 this.finishBeanFactoryInitialization(beanFactory); 16 this.finishRefresh(); 17 } catch (BeansException var9) { 18 if (this.logger.isWarnEnabled()) { 19 this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var9); 20 } 21 22 this.destroyBeans(); 23 this.cancelRefresh(var9); 24 throw var9; 25 } finally { 26 this.resetCommonCaches(); 27 } 28 29 } 30 }
在第 13 行又调用了子类的 onRefresh() 方法:
1 @Override 2 protected void onRefresh() { 3 super.onRefresh(); 4 try { 5 createEmbeddedServletContainer(); 6 } 7 catch (Throwable ex) { 8 throw new ApplicationContextException("Unable to start embedded container", 9 ex); 10 } 11 }
根据方法名能够看出,接着会经过 createEmbeddedServletContainer() 方法建立嵌入式 Servlet 容器:
1 private void createEmbeddedServletContainer() { 2 EmbeddedServletContainer localContainer = this.embeddedServletContainer; 3 ServletContext localServletContext = getServletContext(); 4 if (localContainer == null && localServletContext == null) { 5 EmbeddedServletContainerFactory containerFactory = getEmbeddedServletContainerFactory(); 6 this.embeddedServletContainer = containerFactory 7 .getEmbeddedServletContainer(getSelfInitializer()); 8 } 9 else if (localServletContext != null) { 10 try { 11 getSelfInitializer().onStartup(localServletContext); 12 } 13 catch (ServletException ex) { 14 throw new ApplicationContextException("Cannot initialize servlet context", 15 ex); 16 } 17 } 18 initPropertySources(); 19 }
在第 5 行经过 getEmbeddedServletContainerFactory() 获取到嵌入式 Servlet 容器工厂 bean,该 bean 在嵌入式 Servlet 容器自动配置类中就已经被注册,此时为 Tomcat 的工厂即: org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory ,而后在第 7 行经过 containerFactory.getEmbeddedServletContainer(getSelfInitializer()) 获取嵌入式 Servlet 容器的实例即 Tomcat 容器实例:
1 @Override 2 public EmbeddedServletContainer getEmbeddedServletContainer( 3 ServletContextInitializer... initializers) { 4 // 建立一个 Tomcat 实例 5 Tomcat tomcat = new Tomcat(); 6 // Tomcat 的基本配置 7 File baseDir = (this.baseDirectory != null) ? this.baseDirectory 8 : createTempDir("tomcat"); 9 tomcat.setBaseDir(baseDir.getAbsolutePath()); 10 Connector connector = new Connector(this.protocol); 11 tomcat.getService().addConnector(connector); 12 customizeConnector(connector); 13 tomcat.setConnector(connector); 14 tomcat.getHost().setAutoDeploy(false); 15 configureEngine(tomcat.getEngine()); 16 for (Connector additionalConnector : this.additionalTomcatConnectors) { 17 tomcat.getService().addConnector(additionalConnector); 18 } 19 prepareContext(tomcat.getHost(), initializers); 20 // 传入配置完成的 Tomcat 实例 21 return getTomcatEmbeddedServletContainer(tomcat); 22 }
在第 21 行将完成基本配置的 Tomcat 实例传递给了 getTomcatEmbeddedServletContainer(tomcat) 方法:
1 protected TomcatEmbeddedServletContainer getTomcatEmbeddedServletContainer( 2 Tomcat tomcat) { 3 return new TomcatEmbeddedServletContainer(tomcat, getPort() >= 0); 4 }
接着经过第 3 行将指定的端口不小于 0 的判断结果做为是否自动启动的参数传递给了 Tomcat 容器类的构造方法,建立 Tomcat 容器实例:
1 public TomcatEmbeddedServletContainer(Tomcat tomcat, boolean autoStart) { 2 Assert.notNull(tomcat, "Tomcat Server must not be null"); 3 this.tomcat = tomcat; 4 this.autoStart = autoStart; 5 initialize(); 6 }
又在第 5 行执行了 initialize() :
1 private void initialize() throws EmbeddedServletContainerException { 2 TomcatEmbeddedServletContainer.logger 3 .info("Tomcat initialized with port(s): " + getPortsDescription(false)); 4 synchronized (this.monitor) { 5 try { 6 addInstanceIdToEngineName(); 7 try { 8 final Context context = findContext(); 9 context.addLifecycleListener(new LifecycleListener() { 10 11 @Override 12 public void lifecycleEvent(LifecycleEvent event) { 13 if (context.equals(event.getSource()) 14 && Lifecycle.START_EVENT.equals(event.getType())) { 15 removeServiceConnectors(); 16 } 17 } 18 19 }); 20 21 this.tomcat.start(); 22 23 rethrowDeferredStartupExceptions(); 24 25 try { 26 ContextBindings.bindClassLoader(context, getNamingToken(context), 27 getClass().getClassLoader()); 28 } 29 catch (NamingException ex) { 30 } 31 32 startDaemonAwaitThread(); 33 } 34 catch (Exception ex) { 35 containerCounter.decrementAndGet(); 36 throw ex; 37 } 38 } 39 catch (Exception ex) { 40 stopSilently(); 41 throw new EmbeddedServletContainerException( 42 "Unable to start embedded Tomcat", ex); 43 } 44 } 45 }
在第 21 行经过 Tomcat 实例的 start() 方法启动了 Tomcat。
至此咱们能够得出结论,随着 SpringBoot 程序的启动,SpringBoot 会使用注册的嵌入式 Servlet 容器工厂 bean 来建立嵌入式 Servlet 容器,接着会随着容器的建立来启动嵌入式 Servlet 容器。
经过上面的学习咱们已经知道,能够经过修改配置文件及编写容器的定制器来修改嵌入式 Servlet 容器的配置,这些配置是如何生效的呢?
回到嵌入式 Servlet 容器的自动配置类中,咱们会发如今该类上有一个 @Import(BeanPostProcessorsRegistrar.class) 注解,该注解是用来快速注册指定组件的,具体使用可参考【Import-快速注册】。查看该类的 registerBeanDefinitions 方法:
1 @Override 2 public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, 3 BeanDefinitionRegistry registry) { 4 if (this.beanFactory == null) { 5 return; 6 } 7 registerSyntheticBeanIfMissing(registry, 8 "embeddedServletContainerCustomizerBeanPostProcessor", 9 EmbeddedServletContainerCustomizerBeanPostProcessor.class); 10 registerSyntheticBeanIfMissing(registry, 11 "errorPageRegistrarBeanPostProcessor", 12 ErrorPageRegistrarBeanPostProcessor.class); 13 }
在第 7 行能够看到注册了一个 embeddedServletContainerCustomizerBeanPostProcessor 即后置处理器组件,后置处理器使用可参考:【实现BeanPostProcessor接口】,查看该组件:
1 package org.springframework.boot.context.embedded; 2 3 public class EmbeddedServletContainerCustomizerBeanPostProcessor 4 implements BeanPostProcessor, BeanFactoryAware { 5 6 private ListableBeanFactory beanFactory; 7 8 private List<EmbeddedServletContainerCustomizer> customizers; 9 10 @Override 11 public void setBeanFactory(BeanFactory beanFactory) { 12 Assert.isInstanceOf(ListableBeanFactory.class, beanFactory, 13 "EmbeddedServletContainerCustomizerBeanPostProcessor can only be used " 14 + "with a ListableBeanFactory"); 15 this.beanFactory = (ListableBeanFactory) beanFactory; 16 } 17 18 // 初始化以前执行 19 @Override 20 public Object postProcessBeforeInitialization(Object bean, String beanName) 21 throws BeansException { 22 if (bean instanceof ConfigurableEmbeddedServletContainer) { 23 postProcessBeforeInitialization((ConfigurableEmbeddedServletContainer) bean); 24 } 25 return bean; 26 } 27 28 @Override 29 public Object postProcessAfterInitialization(Object bean, String beanName) 30 throws BeansException { 31 return bean; 32 } 33 34 private void postProcessBeforeInitialization( 35 ConfigurableEmbeddedServletContainer bean) { 36 // 遍历全部的容器定制器 37 for (EmbeddedServletContainerCustomizer customizer : getCustomizers()) { 38 customizer.customize(bean); 39 } 40 } 41 // 获取全部的容器定制器 42 private Collection<EmbeddedServletContainerCustomizer> getCustomizers() { 43 if (this.customizers == null) { 44 // Look up does not include the parent context 45 this.customizers = new ArrayList<EmbeddedServletContainerCustomizer>( 46 this.beanFactory 47 .getBeansOfType(EmbeddedServletContainerCustomizer.class, 48 false, false) 49 .values()); 50 Collections.sort(this.customizers, AnnotationAwareOrderComparator.INSTANCE); 51 this.customizers = Collections.unmodifiableList(this.customizers); 52 } 53 return this.customizers; 54 } 55 56 }
查看第 20-26 行的 postProcessBeforeInitialization 方法,该方法会在 IoC 容器建立一个 bean 实例后、初始化以前执行。在该方法中判断了当前建立的 bean 是否是嵌入式 Servlet 容器,若是是,则经过 34 行的 postProcessBeforeInitialization 方法,在该方法中遍历全部的容器定制器,经过容器定制器的 customize 方法来配置当前建立的 bean 即当前建立的嵌入式 Servlet 容器。由于在配置文件中作容器相关配置实际也是经过容器定制器来配置容器,因此修改配置文件及编写容器的定制器来修改容器配置会对当前 IoC 容器中全部的嵌入式 Servlet 容器生效。
这里以使用本地的 Tomcat 为例。
一、建立一个 SpringBoot 项目,打包方式为 war。
二、将嵌入式 Tomcat 依赖的 scope 指定为 provided:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> <scope>provided</scope> </dependency>
三、编写一个类继承 org.springframework.boot.web.support.SpringBootServletInitializer ,重写 configure 方法,例如:
package com.springboot.webdev3; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.boot.web.support.SpringBootServletInitializer; public class ServletInitializer extends SpringBootServletInitializer { @Override protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { return application.sources(Webdev3Application.class); } }
四、建立 web 资源目录:
五、最终目录结构以下:
六、将其部署到本地 Tomcat 容器,启动 Tomcat,SpringBoot 项目会随之启动:
以前咱们启动打包方式为 jar 的 SpringBoot 项目时,首先是执行 SpringBoot 入口类的 main 方法,随之启动了 IoC 容器,嵌入式 Servlet 容器也随之建立并启动了。
而咱们如今是启动打包方式为 war 的Spring项目,直接启动服务器,SpringBoot 应用就随之启动了。
问题来了,为何 SpringBoot 程序会随着外部 Servlet 容器启动而启动?
Servlet 3.0后有一个新规范:
在 SpringBoot 的 web 场景启动器依赖中就有一个 javax.servlet.ServletContainerInitializer 文件:
org.springframework.web.SpringServletContainerInitializer
查看该类:
1 package org.springframework.web; 2 3 import java.lang.reflect.Modifier; 4 import java.util.LinkedList; 5 import java.util.List; 6 import java.util.ServiceLoader; 7 import java.util.Set; 8 import javax.servlet.ServletContainerInitializer; 9 import javax.servlet.ServletContext; 10 import javax.servlet.ServletException; 11 import javax.servlet.annotation.HandlesTypes; 12 13 import org.springframework.core.annotation.AnnotationAwareOrderComparator; 14 15 @HandlesTypes(WebApplicationInitializer.class) 16 public class SpringServletContainerInitializer implements ServletContainerInitializer { 17 18 @Override 19 public void onStartup(Set<Class<?>> webAppInitializerClasses, ServletContext servletContext) 20 throws ServletException { 21 22 List<WebApplicationInitializer> initializers = new LinkedList<WebApplicationInitializer>(); 23 24 if (webAppInitializerClasses != null) { 25 for (Class<?> waiClass : webAppInitializerClasses) { 26 if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) && 27 WebApplicationInitializer.class.isAssignableFrom(waiClass)) { 28 try { 29 initializers.add((WebApplicationInitializer) waiClass.newInstance()); 30 } 31 catch (Throwable ex) { 32 throw new ServletException("Failed to instantiate WebApplicationInitializer class", ex); 33 } 34 } 35 } 36 } 37 38 if (initializers.isEmpty()) { 39 servletContext.log("No Spring WebApplicationInitializer types detected on classpath"); 40 return; 41 } 42 43 AnnotationAwareOrderComparator.sort(initializers); 44 servletContext.log("Spring WebApplicationInitializers detected on classpath: " + initializers); 45 46 for (WebApplicationInitializer initializer : initializers) { 47 initializer.onStartup(servletContext); 48 } 49 } 50 51 }
看到源码就很清晰了,应用在启动时会建立该类实例执行它的 onStartup 方法,而在该类上经过标注 @HandlesTypes(WebApplicationInitializer.class) 将当前程序中的全部 WebApplicationInitializer 的实现类的字节码对象经过构造方法注入,而 SpringBootServletInitializer 类就是 WebApplicationInitializer 的一个实现类,因此咱们本身编写的 ServletInitializer 的字节码对象将会被注入,而且在第 29 行建立实例,在第 47 行执行了咱们本身编写的 ServletInitializer 类对象的 onStartup 方法:
1 @Override 2 public void onStartup(ServletContext servletContext) throws ServletException { 3 this.logger = LogFactory.getLog(getClass()); 4 WebApplicationContext rootAppContext = createRootApplicationContext( 5 servletContext); 6 if (rootAppContext != null) { 7 servletContext.addListener(new ContextLoaderListener(rootAppContext) { 8 @Override 9 public void contextInitialized(ServletContextEvent event) { 10 } 11 }); 12 } 13 else { 14 this.logger.debug("No ContextLoaderListener registered, as " 15 + "createRootApplicationContext() did not " 16 + "return an application context"); 17 } 18 }
接着执行 createRootApplicationContext(servletContext) 方法:
1 protected WebApplicationContext createRootApplicationContext( 2 ServletContext servletContext) { 3 SpringApplicationBuilder builder = createSpringApplicationBuilder(); 4 builder.main(getClass()); 5 ApplicationContext parent = getExistingRootWebApplicationContext(servletContext); 6 if (parent != null) { 7 this.logger.info("Root context already created (using as parent)."); 8 servletContext.setAttribute( 9 WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, null); 10 builder.initializers(new ParentContextApplicationContextInitializer(parent)); 11 } 12 builder.initializers( 13 new ServletContextApplicationContextInitializer(servletContext)); 14 builder.contextClass(AnnotationConfigEmbeddedWebApplicationContext.class); 15 builder = configure(builder); 16 builder.listeners(new WebEnvironmentPropertySourceInitializer(servletContext)); 17 SpringApplication application = builder.build(); 18 if (application.getSources().isEmpty() && AnnotationUtils 19 .findAnnotation(getClass(), Configuration.class) != null) { 20 application.getSources().add(getClass()); 21 } 22 Assert.state(!application.getSources().isEmpty(), 23 "No SpringApplication sources have been defined. Either override the " 24 + "configure method or add an @Configuration annotation"); 25 if (this.registerErrorPageFilter) { 26 application.getSources().add(ErrorPageFilterConfiguration.class); 27 } 28 return run(application); 29 }
接着在第 15 行又执行了 configure 方法,而这个 configure 方法正是咱们本身编写的继承 SpringBootServletInitializer 类重写的 configure 方法:
1 @Override 2 protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { 3 return application.sources(Webdev3Application.class); 4 }
在该方法中经过 application.sources(Webdev3Application.class) 传入了当前 SpringBoot 项目的入口类,返回一个 Spring 程序构建器。回到上一步,在 15 行拿到该构建器,在第 17 行建立了 Spring 程序实例,最后在第 28 行执行了 Spring 程序实例的 run 方法,即 SpringBoot 程序随服务器的启动而启动了。