SpringBoot源码分析-内嵌Tomcat
嵌入式Tomcat简介 SpringBoot框架是当前比较流行的java后端开发框架,与maven结合大大简化了开发人员项目搭建的步骤,我们知道SpringBoot的启动类启动后,tomcat容器、SpringMVC、spring事务等第三方依赖也已经自动启动。那么SpringBoot是如何启动这些第三方依赖的呢?下面以SpringBoot拉起tomcat容器为例,进行源码分析。
我们知道,在使用Spring boot搭建web工程时,我们不需要自己搭建一个tomcat服务器,只需要引入spring-boot-starter-web,在应用启动时会自动启动嵌入版的tomcat作为应用服务器,下面我们来分析这个过程。
Tomcat自动配置
首先是进行SpringBootApplication注解的解析然后是EnableAutoConfiguration注解的解析
导入类AutoConfigurationImportSelector,进行自动配置类加载注册,加载EnableAutoConfiguration类型的注解,其中有一个是关于Tomcat的自动配置类ServletWebServerFactoryAutoConfiguration
进入该类,里面也通过@Import注解将EmbeddedTomcat、EmbeddedJetty、EmbeddedUndertow等嵌入式容器类加载进来了,springboot默认是启动嵌入式tomcat容器,如果要改变启动jetty或者undertow容器,需在pom文件中去设置。
之前我们分析了Spring boot的启动流程和自动配置流程,而嵌入tomcat的启动同样需要依赖一个配置ServletWebServerFactoryAutoConfiguration,我们来看一下这个配置的内容:
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 68 69 70 71 72 @Configuration(proxyBeanMethods = false) @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE) @ConditionalOnClass(ServletRequest.class) @ConditionalOnWebApplication(type = Type.SERVLET) @EnableConfigurationProperties(ServerProperties.class) @Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class, ServletWebServerFactoryConfiguration.EmbeddedTomcat.class, ServletWebServerFactoryConfiguration.EmbeddedJetty.class, ServletWebServerFactoryConfiguration.EmbeddedUndertow.class }) public class ServletWebServerFactoryAutoConfiguration { @Bean public ServletWebServerFactoryCustomizer servletWebServerFactoryCustomizer (ServerProperties serverProperties) { return new ServletWebServerFactoryCustomizer (serverProperties); } @Bean @ConditionalOnClass(name = "org.apache.catalina.startup.Tomcat") public TomcatServletWebServerFactoryCustomizer tomcatServletWebServerFactoryCustomizer ( ServerProperties serverProperties) { return new TomcatServletWebServerFactoryCustomizer (serverProperties); } @Bean @ConditionalOnMissingFilterBean(ForwardedHeaderFilter.class) @ConditionalOnProperty(value = "server.forward-headers-strategy", havingValue = "framework") public FilterRegistrationBean<ForwardedHeaderFilter> forwardedHeaderFilter () { ForwardedHeaderFilter filter = new ForwardedHeaderFilter (); FilterRegistrationBean<ForwardedHeaderFilter> registration = new FilterRegistrationBean <>(filter); registration.setDispatcherTypes(DispatcherType.REQUEST, DispatcherType.ASYNC, DispatcherType.ERROR); registration.setOrder(Ordered.HIGHEST_PRECEDENCE); return registration; } public static class BeanPostProcessorsRegistrar implements ImportBeanDefinitionRegistrar , BeanFactoryAware { private ConfigurableListableBeanFactory beanFactory; @Override public void setBeanFactory (BeanFactory beanFactory) throws BeansException { if (beanFactory instanceof ConfigurableListableBeanFactory) { this .beanFactory = (ConfigurableListableBeanFactory) beanFactory; } } @Override public void registerBeanDefinitions (AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { if (this .beanFactory == null ) { return ; } registerSyntheticBeanIfMissing(registry, "webServerFactoryCustomizerBeanPostProcessor" , WebServerFactoryCustomizerBeanPostProcessor.class); registerSyntheticBeanIfMissing(registry, "errorPageRegistrarBeanPostProcessor" , ErrorPageRegistrarBeanPostProcessor.class); } private void registerSyntheticBeanIfMissing (BeanDefinitionRegistry registry, String name, Class<?> beanClass) { if (ObjectUtils.isEmpty(this .beanFactory.getBeanNamesForType(beanClass, true , false ))) { RootBeanDefinition beanDefinition = new RootBeanDefinition (beanClass); beanDefinition.setSynthetic(true ); registry.registerBeanDefinition(name, beanDefinition); } } } }
配置中主要做这么几件事,导入了内部类BeanPostProcessorsRegistrar,它实现了ImportBeanDefinitionRegistrar,作用我们在之前的文章中已经介绍过,我们可以实现ImportBeanDefinitionRegistrar来注册额外的BeanDefinition。导入了ServletWebServerFactoryConfiguration.EmbeddedTomcat配置(我们主要关注tomcat相关的配置)。注册了ServletWebServerFactoryCustomizer、TomcatServletWebServerFactoryCustomizer两个WebServerFactoryCustomizer类型的bean。
注册BeanPostProcessors 我们来分别看一下上面提到的这些类的作用,首先来看内部类BeanPostProcessorsRegistrar:
1 2 3 4 5 6 7 8 9 10 11 @Override public void registerBeanDefinitions (AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { if (this .beanFactory == null ) { return ; } registerSyntheticBeanIfMissing(registry, "webServerFactoryCustomizerBeanPostProcessor" , WebServerFactoryCustomizerBeanPostProcessor.class); registerSyntheticBeanIfMissing(registry, "errorPageRegistrarBeanPostProcessor" , ErrorPageRegistrarBeanPostProcessor.class); }
这里注册了两个BeanDefinition,WebServerFactoryCustomizerBeanPostProcessor和ErrorPageRegistrarBeanPostProcessor,它们都实现了BeanPostProcessor,后面我们会介绍它们的作用。
注册嵌入式Tomcat
这个配置很简单,就是注册了一个TomcatServletWebServerFactory类型的bean。
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 @Configuration(proxyBeanMethods = false) @ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class }) @ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT) public static class EmbeddedTomcat { @Bean public TomcatServletWebServerFactory tomcatServletWebServerFactory ( ObjectProvider<TomcatConnectorCustomizer> connectorCustomizers, ObjectProvider<TomcatContextCustomizer> contextCustomizers, ObjectProvider<TomcatProtocolHandlerCustomizer<?>> protocolHandlerCustomizers) { TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory (); factory.getTomcatConnectorCustomizers() .addAll(connectorCustomizers.orderedStream().collect(Collectors.toList())); factory.getTomcatContextCustomizers() .addAll(contextCustomizers.orderedStream().collect(Collectors.toList())); factory.getTomcatProtocolHandlerCustomizers() .addAll(protocolHandlerCustomizers.orderedStream().collect(Collectors.toList())); return factory; } } ....... }
最后剩下两个WebServerFactoryCustomizer类型的bean,任何WebServerFactoryCustomizer类型的bean都可以收到WebServerFactory的回调,所以我们可以实现WebServerFactoryCustomizer来定义WebServerFactory。ServletWebServerFactoryCustomizer的作用是将ServerProperties的属性配置设置到ConfigurableServletWebServerFactory的对应属性上,这里主要设置的是一些通用的属性,上面注册的TomcatServletWebServerFactory就实现了ConfigurableServletWebServerFactory。而TomcatServletWebServerFactoryCustomizer则是为TomcatServletWebServerFactory设置了一些它需要的特有属性。
创建Tomcat
当自动装配功能完成之后会接着执行onRefresh的方法
执行流程是 run->refreshContext->refresh->AbstractApplicationContext#refresh->ServletWebServerApplicationContext#onRefresh
1 2 3 4 5 6 7 8 9 10 11 @Override protected void onRefresh () { super .onRefresh(); try { createWebServer(); } catch (Throwable ex) { throw new ApplicationContextException ("Unable to start web server" , ex); } }
创建WEB服务
创建web服务,默认获取的是tomcat的web容器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 private void createWebServer () { WebServer webServer = this .webServer; ServletContext servletContext = getServletContext(); if (webServer == null && servletContext == null ) { ServletWebServerFactory factory = getWebServerFactory(); this .webServer = factory.getWebServer(getSelfInitializer()); } else if (servletContext != null ) { try { getSelfInitializer().onStartup(servletContext); } catch (ServletException ex) { throw new ApplicationContextException ("Cannot initialize servlet context" , ex); } } initPropertySources(); }
获取servletWebServerFactory
因为我们上文在Tomcat自动配置中默认注册了TomcatServletWebServerFactory ,所以这个地方就会从BeanFactory中获取到TomcatServletWebServerFactory 。
1 2 3 4 5 6 7 8 9 10 11 12 13 protected ServletWebServerFactory getWebServerFactory () { String[] beanNames = getBeanFactory().getBeanNamesForType(ServletWebServerFactory.class); if (beanNames.length == 0 ) { throw new ApplicationContextException ("Unable to start ServletWebServerApplicationContext due to missing " + "ServletWebServerFactory bean." ); } if (beanNames.length > 1 ) { throw new ApplicationContextException ("Unable to start ServletWebServerApplicationContext due to multiple " + "ServletWebServerFactory beans : " + StringUtils.arrayToCommaDelimitedString(beanNames)); } return getBeanFactory().getBean(beanNames[0 ], ServletWebServerFactory.class); }
Tomcat对象的初始化 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 private org.springframework.boot.web.servlet.ServletContextInitializer getSelfInitializer () { return this ::selfInitialize; } private void selfInitialize (ServletContext servletContext) throws ServletException { prepareWebApplicationContext(servletContext); registerApplicationScope(servletContext); WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(), servletContext); for (ServletContextInitializer beans : getServletContextInitializerBeans()) { beans.onStartup(servletContext); } }
完成内嵌Tomcat的api调用
factory.getWebServer(getSelfInitializer());
就会调用TomcatServletWebServerFactory 的getWebServer方法完成Tomcat的启动。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 @Override public WebServer getWebServer (ServletContextInitializer... initializers) { if (this .disableMBeanRegistry) { Registry.disableRegistry(); } Tomcat tomcat = new Tomcat (); File baseDir = (this .baseDirectory != null ) ? this .baseDirectory : createTempDir("tomcat" ); tomcat.setBaseDir(baseDir.getAbsolutePath()); Connector connector = new Connector (this .protocol); connector.setThrowOnFailure(true ); tomcat.getService().addConnector(connector); customizeConnector(connector); tomcat.setConnector(connector); tomcat.getHost().setAutoDeploy(false ); configureEngine(tomcat.getEngine()); for (Connector additionalConnector : this .additionalTomcatConnectors) { tomcat.getService().addConnector(additionalConnector); } prepareContext(tomcat.getHost(), initializers); return getTomcatWebServer(tomcat); }
获取tomcat服务 1 2 3 4 5 6 7 8 9 10 11 protected TomcatWebServer getTomcatWebServer (Tomcat tomcat) { return new TomcatWebServer (tomcat, getPort() >= 0 ); } public TomcatWebServer (Tomcat tomcat, boolean autoStart) { Assert.notNull(tomcat, "Tomcat Server must not be null" ); this .tomcat = tomcat; this .autoStart = autoStart; initialize(); }
完成tomcat的初始化 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 private void initialize () throws WebServerException { logger.info("Tomcat initialized with port(s): " + getPortsDescription(false )); synchronized (this .monitor) { try { addInstanceIdToEngineName(); Context context = findContext(); context.addLifecycleListener((event) -> { if (context.equals(event.getSource()) && Lifecycle.START_EVENT.equals(event.getType())) { removeServiceConnectors(); } }); this .tomcat.start(); rethrowDeferredStartupExceptions(); try { ContextBindings.bindClassLoader(context, context.getNamingToken(), getClass().getClassLoader()); } catch (NamingException ex) { } startDaemonAwaitThread(); } catch (Exception ex) { stopSilently(); destroySilently(); throw new WebServerException ("Unable to start embedded Tomcat" , ex); } } }
删除Connectors
删除Connectors,以便再启动服务时不发生协议绑定
1 2 3 4 5 6 7 8 9 10 11 private void removeServiceConnectors () { for (Service service : this .tomcat.getServer().findServices()) { Connector[] connectors = service.findConnectors().clone(); this .serviceConnectors.put(service, connectors); for (Connector connector : connectors) { service.removeConnector(connector); } } }
启动Tomcat
除了refresh方法之外,在finishRefresh()方法中也对tomcat做了相关的处理
调用ServletWebServerApplicationContext 的finishRefresh方法。
1 2 3 4 5 6 7 8 9 10 11 @Override protected void finishRefresh () { super .finishRefresh(); WebServer webServer = startWebServer(); if (webServer != null ) { publishEvent(new ServletWebServerInitializedEvent (webServer, this )); } }
启动服务 1 2 3 4 5 6 7 8 private WebServer startWebServer () { WebServer webServer = this .webServer; if (webServer != null ) { webServer.start(); } return webServer; }
调用start方法开始启动
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 @Override public void start () throws WebServerException { synchronized (this .monitor) { if (this .started) { return ; } try { addPreviouslyRemovedConnectors(); Connector connector = this .tomcat.getConnector(); if (connector != null && this .autoStart) { performDeferredLoadOnStartup(); } checkThatConnectorsHaveStarted(); this .started = true ; logger.info("Tomcat started on port(s): " + getPortsDescription(true ) + " with context path '" + getContextPath() + "'" ); } catch (ConnectorStartFailedException ex) { stopSilently(); throw ex; } catch (Exception ex) { if (findBindException(ex) != null ) { throw new PortInUseException (this .tomcat.getConnector().getPort()); } throw new WebServerException ("Unable to start embedded Tomcat server" , ex); } finally { Context context = findContext(); ContextBindings.unbindClassLoader(context, context.getNamingToken(), getClass().getClassLoader()); } } }
addPreviouslyRemovedConnectors
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 private void addPreviouslyRemovedConnectors () { Service[] services = this .tomcat.getServer().findServices(); for (Service service : services) { Connector[] connectors = this .serviceConnectors.get(service); if (connectors != null ) { for (Connector connector : connectors) { service.addConnector(connector); if (!this .autoStart) { stopProtocolHandler(connector); } } this .serviceConnectors.remove(service); } } }
performDeferredLoadOnStartup
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 private void performDeferredLoadOnStartup () { try { for (Container child : this .tomcat.getHost().findChildren()) { if (child instanceof TomcatEmbeddedContext) { ((TomcatEmbeddedContext) child).deferredLoadOnStartup(); } } } catch (Exception ex) { if (ex instanceof WebServerException) { throw (WebServerException) ex; } throw new WebServerException ("Unable to start embedded Tomcat connectors" , ex); } }
deferredLoadOnStartup
1 2 3 4 void deferredLoadOnStartup () throws LifecycleException { doWithThreadContextClassLoader(getLoader().getClassLoader(), () -> getLoadOnStartupWrappers(findChildren()).forEach(this ::load)); }
关闭Tomcat
在refreshContext中注册一个关闭的钩子函数,而钩子函数可以完成关闭的功能
当服务关闭就会触发ServletWebServerApplicationContext 的onClose方法
1 2 3 4 5 @Override protected void onClose () { super .onClose(); stopAndReleaseWebServer(); }
调用stopAndReleaseWebServer方法关闭Tomcat
1 2 3 4 5 6 7 8 9 10 11 private void stopAndReleaseWebServer () { WebServer webServer = this .webServer; if (webServer != null ) { try { webServer.stop(); this .webServer = null ; } catch (Exception ex) { throw new IllegalStateException (ex); } } }
调用webServer.stop()关闭服务
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 @Override public void stop () throws WebServerException { synchronized (this .monitor) { boolean wasStarted = this .started; try { this .started = false ; try { stopTomcat(); this .tomcat.destroy(); } catch (LifecycleException ex) { } } catch (Exception ex) { throw new WebServerException ("Unable to stop embedded Tomcat" , ex); } finally { if (wasStarted) { containerCounter.decrementAndGet(); } } } }
到这里,整个Spring boot内嵌tomcat的流程就分析完了。