官方文档

https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-embedded-container

辅助

有的时候调试web相关的内容,除了用debug去看的话,打开这个日志也可以看到一些有用的信息

  1. logging:
  2. level:
  3. web: debug

大致步骤

springboot流程
image.png
步骤:
1.WebApplicationType.deduceFromClasspath();判断REACTIVE/NONE/SERVLET
5.ApplicationContextFactory根据webApplicationType来创建得到AnnotationConfigServletWebServerApplicationContext
7.刷新上下文的时候利用TomcatServletWebServerFactory创建tomcat服务器,并且设置好,然后启动
为什么得到是tomcat工厂呢,这个就要看自动配置类
因为导入了jar包,存在了类,所以注册了tomcat的工厂
如果引入了Jetty,Undertow的包,就会导入其他web服务器的工厂,后续的流程就变了
自动配置类导入配置类

  1. @Configuration(proxyBeanMethods = false)
  2. @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
  3. @ConditionalOnClass(ServletRequest.class)
  4. @ConditionalOnWebApplication(type = Type.SERVLET)
  5. @EnableConfigurationProperties(ServerProperties.class)
  6. //导入tomcat等配置类
  7. @Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,
  8. ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,
  9. ServletWebServerFactoryConfiguration.EmbeddedJetty.class,
  10. ServletWebServerFactoryConfiguration.EmbeddedUndertow.class })
  11. public class ServletWebServerFactoryAutoConfiguration {
  12. //把配置文件转为三大容器的通用属性
  13. @Bean
  14. public ServletWebServerFactoryCustomizer servletWebServerFactoryCustomizer(ServerProperties serverProperties,
  15. ObjectProvider<WebListenerRegistrar> webListenerRegistrars) {
  16. return new ServletWebServerFactoryCustomizer(serverProperties,
  17. webListenerRegistrars.orderedStream().collect(Collectors.toList()));
  18. }
  19. }

导入的配资类

  1. @Configuration(proxyBeanMethods = false)
  2. class ServletWebServerFactoryConfiguration {
  3. @Configuration(proxyBeanMethods = false)
  4. @ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class })
  5. //根据导包来判断是否生效,生效了才能往容器注册Bean
  6. @ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
  7. static class EmbeddedTomcat {
  8. @Bean
  9. TomcatServletWebServerFactory tomcatServletWebServerFactory(
  10. ObjectProvider<TomcatConnectorCustomizer> connectorCustomizers,
  11. ObjectProvider<TomcatContextCustomizer> contextCustomizers,
  12. ObjectProvider<TomcatProtocolHandlerCustomizer<?>> protocolHandlerCustomizers) {
  13. TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
  14. factory.getTomcatConnectorCustomizers()
  15. .addAll(connectorCustomizers.orderedStream().collect(Collectors.toList()));
  16. factory.getTomcatContextCustomizers()
  17. .addAll(contextCustomizers.orderedStream().collect(Collectors.toList()));
  18. factory.getTomcatProtocolHandlerCustomizers()
  19. .addAll(protocolHandlerCustomizers.orderedStream().collect(Collectors.toList()));
  20. return factory;
  21. }
  22. //同样的还有JettyServletWebServerFactory/UndertowServletWebServerFactory
  23. }
  24. }

步骤1核心代码

  1. static WebApplicationType deduceFromClasspath() {
  2. if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
  3. && !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
  4. return WebApplicationType.REACTIVE;
  5. }
  6. for (String className : SERVLET_INDICATOR_CLASSES) {
  7. if (!ClassUtils.isPresent(className, null)) {
  8. return WebApplicationType.NONE;
  9. }
  10. }
  11. return WebApplicationType.SERVLET;
  12. }
  13. 原理判断是否导包,存在类

步骤5核心代码1

  1. public class SpringApplication {
  2. private ApplicationContextFactory applicationContextFactory = ApplicationContextFactory.DEFAULT;
  3. }

步骤5核心代码2

  1. @FunctionalInterface
  2. public interface ApplicationContextFactory {
  3. ApplicationContextFactory DEFAULT = (webApplicationType) -> {
  4. try {
  5. switch (webApplicationType) {
  6. case SERVLET:
  7. return new AnnotationConfigServletWebServerApplicationContext();
  8. case REACTIVE:
  9. return new AnnotationConfigReactiveWebServerApplicationContext();
  10. default:
  11. return new AnnotationConfigApplicationContext();
  12. }
  13. }
  14. catch (Exception ex) {
  15. throw new IllegalStateException("Unable create a default ApplicationContext instance, "
  16. + "you may need a custom ApplicationContextFactory", ex);
  17. }
  18. };
  19. }

代码入口

  1. public class SpringApplication {
  2. public ConfigurableApplicationContext run(String... args) {
  3. StopWatch stopWatch = new StopWatch();
  4. stopWatch.start();
  5. DefaultBootstrapContext bootstrapContext = createBootstrapContext();
  6. ConfigurableApplicationContext context = null;
  7. configureHeadlessProperty();
  8. SpringApplicationRunListeners listeners = getRunListeners(args);
  9. listeners.starting(bootstrapContext, this.mainApplicationClass);
  10. try {
  11. ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
  12. ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
  13. configureIgnoreBeanInfo(environment);
  14. Banner printedBanner = printBanner(environment);
  15. context = createApplicationContext();
  16. context.setApplicationStartup(this.applicationStartup);
  17. prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
  18. //代码入口,刷新上下文
  19. refreshContext(context);
  20. afterRefresh(context, applicationArguments);
  21. stopWatch.stop();
  22. if (this.logStartupInfo) {
  23. new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
  24. }
  25. listeners.started(context);
  26. callRunners(context, applicationArguments);
  27. }
  28. catch (Throwable ex) {
  29. handleRunFailure(context, ex, listeners);
  30. throw new IllegalStateException(ex);
  31. }
  32. try {
  33. listeners.running(context);
  34. }
  35. catch (Throwable ex) {
  36. handleRunFailure(context, ex, null);
  37. throw new IllegalStateException(ex);
  38. }
  39. return context;
  40. }
  41. private void refreshContext(ConfigurableApplicationContext context) {
  42. if (this.registerShutdownHook) {
  43. try {
  44. context.registerShutdownHook();
  45. }
  46. catch (AccessControlException ex) {
  47. // Not allowed in some environments.
  48. }
  49. }
  50. //刷新上下文
  51. refresh((ApplicationContext) context);
  52. }
  53. }

需要刷新的就是这玩意,创建的逻辑在第五步,会做很多事情,具体看其他文章
image.png
刷新上下文代码,创建web服务器

  1. public class ServletWebServerApplicationContext extends GenericWebApplicationContext implements ConfigurableWebServerApplicationContext {
  2. private final DefaultListableBeanFactory beanFactory;
  3. @Override
  4. public final void refresh() throws BeansException, IllegalStateException {
  5. try {
  6. super.refresh();
  7. }
  8. catch (RuntimeException ex) {
  9. WebServer webServer = this.webServer;
  10. if (webServer != null) {
  11. webServer.stop();
  12. }
  13. throw ex;
  14. }
  15. }
  16. @Override
  17. protected void onRefresh() {
  18. super.onRefresh();
  19. try {
  20. createWebServer();
  21. }
  22. catch (Throwable ex) {
  23. throw new ApplicationContextException("Unable to start web server", ex);
  24. }
  25. }
  26. //真正逻辑
  27. private void createWebServer() {
  28. WebServer webServer = this.webServer;
  29. ServletContext servletContext = getServletContext();
  30. if (webServer == null && servletContext == null) {
  31. StartupStep createWebServer = this.getApplicationStartup().start("spring.boot.webserver.create");
  32. //得到工厂
  33. ServletWebServerFactory factory = getWebServerFactory();
  34. createWebServer.tag("factory", factory.getClass().toString());
  35. //工厂生产 看具体代码
  36. this.webServer = factory.getWebServer(getSelfInitializer());
  37. createWebServer.end();
  38. getBeanFactory().registerSingleton("webServerGracefulShutdown",
  39. new WebServerGracefulShutdownLifecycle(this.webServer));
  40. getBeanFactory().registerSingleton("webServerStartStop",
  41. new WebServerStartStopLifecycle(this, this.webServer));
  42. }
  43. else if (servletContext != null) {
  44. try {
  45. getSelfInitializer().onStartup(servletContext);
  46. }
  47. catch (ServletException ex) {
  48. throw new ApplicationContextException("Cannot initialize servlet context", ex);
  49. }
  50. }
  51. initPropertySources();
  52. }
  53. protected ServletWebServerFactory getWebServerFactory() {
  54. // Use bean names so that we don't consider the hierarchy,
  55. //得到一个tomcatServletWebServerFactory
  56. String[] beanNames = getBeanFactory().getBeanNamesForType(ServletWebServerFactory.class);
  57. if (beanNames.length == 0) {
  58. throw new ApplicationContextException("Unable to start ServletWebServerApplicationContext due to missing "
  59. + "ServletWebServerFactory bean.");
  60. }
  61. if (beanNames.length > 1) {
  62. throw new ApplicationContextException("Unable to start ServletWebServerApplicationContext due to multiple "
  63. + "ServletWebServerFactory beans : " + StringUtils.arrayToCommaDelimitedString(beanNames));
  64. }
  65. //DefaultListableBeanFactory工厂模式
  66. //最终得到TomcatServletWebServerFactory
  67. return getBeanFactory().getBean(beanNames[0], ServletWebServerFactory.class);
  68. }
  69. }
  70. 父类
  71. public abstract class AbstractApplicationContext extends DefaultResourceLoader implements ConfigurableApplicationContext {
  72. @Override
  73. public void refresh() throws BeansException, IllegalStateException {
  74. synchronized (this.startupShutdownMonitor) {
  75. StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");
  76. // Prepare this context for refreshing.
  77. prepareRefresh();
  78. // Tell the subclass to refresh the internal bean factory.
  79. ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
  80. // Prepare the bean factory for use in this context.
  81. prepareBeanFactory(beanFactory);
  82. try {
  83. // Allows post-processing of the bean factory in context subclasses.
  84. postProcessBeanFactory(beanFactory);
  85. StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
  86. // Invoke factory processors registered as beans in the context.
  87. invokeBeanFactoryPostProcessors(beanFactory);
  88. // Register bean processors that intercept bean creation.
  89. registerBeanPostProcessors(beanFactory);
  90. beanPostProcess.end();
  91. // Initialize message source for this context.
  92. initMessageSource();
  93. // Initialize event multicaster for this context.
  94. initApplicationEventMulticaster();
  95. // Initialize other special beans in specific context subclasses.
  96. //创建嵌入式Servlet容器的逻辑在这里
  97. onRefresh();
  98. // Check for listener beans and register them.
  99. registerListeners();
  100. // Instantiate all remaining (non-lazy-init) singletons.
  101. finishBeanFactoryInitialization(beanFactory);
  102. // Last step: publish corresponding event.
  103. finishRefresh();
  104. }
  105. catch (BeansException ex) {
  106. if (logger.isWarnEnabled()) {
  107. logger.warn("Exception encountered during context initialization - " +
  108. "cancelling refresh attempt: " + ex);
  109. }
  110. // Destroy already created singletons to avoid dangling resources.
  111. destroyBeans();
  112. // Reset 'active' flag.
  113. cancelRefresh(ex);
  114. // Propagate exception to caller.
  115. throw ex;
  116. }
  117. finally {
  118. // Reset common introspection caches in Spring's core, since we
  119. // might not ever need metadata for singleton beans anymore...
  120. resetCommonCaches();
  121. contextRefresh.end();
  122. }
  123. }
  124. }
  125. //看上面子类的onRefresh
  126. protected void onRefresh() throws BeansException {
  127. // For subclasses: do nothing by default.
  128. }
  129. }

工厂代码

  1. public class TomcatServletWebServerFactory extends AbstractServletWebServerFactory implements ConfigurableTomcatWebServerFactory, ResourceLoaderAware {
  2. @Override
  3. public WebServer getWebServer(ServletContextInitializer... initializers) {
  4. if (this.disableMBeanRegistry) {
  5. Registry.disableRegistry();
  6. }
  7. Tomcat tomcat = new Tomcat();
  8. File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat");
  9. tomcat.setBaseDir(baseDir.getAbsolutePath());
  10. Connector connector = new Connector(this.protocol);
  11. connector.setThrowOnFailure(true);
  12. tomcat.getService().addConnector(connector);
  13. customizeConnector(connector);
  14. tomcat.setConnector(connector);
  15. tomcat.getHost().setAutoDeploy(false);
  16. configureEngine(tomcat.getEngine());
  17. for (Connector additionalConnector : this.additionalTomcatConnectors) {
  18. tomcat.getService().addConnector(additionalConnector);
  19. }
  20. //上下文准备和设置
  21. prepareContext(tomcat.getHost(), initializers);
  22. return getTomcatWebServer(tomcat);
  23. }
  24. protected TomcatWebServer getTomcatWebServer(Tomcat tomcat) {
  25. return new TomcatWebServer(tomcat, getPort() >= 0, getShutdown());
  26. }
  27. public TomcatWebServer(Tomcat tomcat, boolean autoStart, Shutdown shutdown) {
  28. Assert.notNull(tomcat, "Tomcat Server must not be null");
  29. this.tomcat = tomcat;
  30. this.autoStart = autoStart;
  31. this.gracefulShutdown = (shutdown == Shutdown.GRACEFUL) ? new GracefulShutdown(tomcat) : null;
  32. initialize();
  33. }
  34. //启动tomcat容器
  35. private void initialize() throws WebServerException {
  36. logger.info("Tomcat initialized with port(s): " + getPortsDescription(false));
  37. synchronized (this.monitor) {
  38. try {
  39. addInstanceIdToEngineName();
  40. Context context = findContext();
  41. context.addLifecycleListener((event) -> {
  42. if (context.equals(event.getSource()) && Lifecycle.START_EVENT.equals(event.getType())) {
  43. // Remove service connectors so that protocol binding doesn't
  44. // happen when the service is started.
  45. removeServiceConnectors();
  46. }
  47. });
  48. // Start the server to trigger initialization listeners
  49. //启动了!!
  50. this.tomcat.start();
  51. // We can re-throw failure exception directly in the main thread
  52. rethrowDeferredStartupExceptions();
  53. try {
  54. ContextBindings.bindClassLoader(context, context.getNamingToken(), getClass().getClassLoader());
  55. }
  56. catch (NamingException ex) {
  57. // Naming is not enabled. Continue
  58. }
  59. // Unlike Jetty, all Tomcat threads are daemon threads. We create a
  60. // blocking non-daemon to stop immediate shutdown
  61. startDaemonAwaitThread();
  62. }
  63. catch (Exception ex) {
  64. stopSilently();
  65. destroySilently();
  66. throw new WebServerException("Unable to start embedded Tomcat", ex);
  67. }
  68. }
  69. }
  70. }

扩展点

0.常用的在配置文件都可以改了
1.实现WebServerFactoryCustomizer 接口

  1. @Component
  2. public class CustomizationBean implements WebServerFactoryCustomizer<ConfigurableServletWebServerFactory> {
  3. @Override
  4. public void customize(ConfigurableServletWebServerFactory server) {
  5. server.setPort(9000);
  6. }
  7. }
  8. @Component
  9. public class TomcatServerCustomizerExample implements WebServerFactoryCustomizer<TomcatServletWebServerFactory> {
  10. @Override
  11. public void customize(TomcatServletWebServerFactory server) {
  12. server.addConnectorCustomizers(
  13. (tomcatConnector) -> tomcatConnector.setAsyncTimeout(Duration.ofSeconds(20).toMillis()));
  14. }
  15. }

2.extend ServletWebServerFactory
注意:springboot的自动配置有已经有了一些WebServerFactoryCustomizer 的实现类,自动增强,也会作用在自定义的这个

容器上下文

Embedded servlet containers do not directly execute the Servlet 3.0+ javax.servlet.ServletContainerInitializer interface or Spring’s org.springframework.web.WebApplicationInitializer interface.
This is an intentional design decision intended to reduce the risk that third party libraries designed to run inside a war may break Spring Boot applications.

定制

If you need to perform servlet context initialization in a Spring Boot application, you should register a bean that implements the org.springframework.boot.web.servlet.ServletContextInitializer interface. The single onStartup method provides access to the ServletContext and, if necessary, can easily be used as an adapter to an existing WebApplicationInitializer.

  1. ServletWebServerFactoryCustomizer继承重写,放入容器
  2. 修改配置文件
  3. 自定义实现ServletWebServerFactory/ConfigurableServletWebServerFactory