SpringBoot如何实现Tomcat自动配置

目录准备工作启动接着往下看

目录

准备工作

我们知道SpringBoot的自动装配的秘密在 org.springframework.boot.autoconfigure 包下的 spring.factories 文件中,而嵌入Tomcat的原理就在这个文件中加载的一个配置类: org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration

@Configuration@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 {@Beanpublic 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);}/** * Registers a {@link WebServerFactoryCustomizerBeanPostProcessor}. Registered via * {@link ImportBeanDefinitionRegistrar} for early registration. */public static class BeanPostProcessorsRegistrarimplements ImportBeanDefinitionRegistrar, BeanFactoryAware {private ConfigurableListableBeanFactory beanFactory;@Overridepublic void setBeanFactory(BeanFactory beanFactory) throws BeansException {if (beanFactory instanceof ConfigurableListableBeanFactory) {this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;}}@Overridepublic 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);}}}}

首先看一下上方的几个注解

@AutoConfigureOrder 这个注解是决定配置类的加载顺序的,当注解里的值越小越先加载,而 Ordered.HIGHEST_PRECEDENCE 的值是 Integer.MIN_VALUE 也就是说这个类肯定是最先加载的那一批 @ConditionalOnXXX 在之前的文章中已经无数次提到了,就不再阐述了 @EnableConfigurationProperties 开启 ServerProperties 类的属性值配置。而这个类里面包含的就是Web服务的配置

@ConfigurationProperties(prefix = "server", ignoreUnknownFields = true)public class ServerProperties {private Integer port;private InetAddress address;@NestedConfigurationPropertyprivate final ErrorProperties error = new ErrorProperties();private Boolean useForwardHeaders;private String serverHeader;private int maxHttpHeaderSize = 0; // bytesprivate Duration connectionTimeout;@NestedConfigurationPropertyprivate Ssl ssl;@NestedConfigurationPropertyprivate final Compression compression = new Compression();@NestedConfigurationPropertyprivate final Http2 http2 = new Http2();private final Servlet servlet = new Servlet();private final Tomcat tomcat = new Tomcat();private final Jetty jetty = new Jetty();private final Undertow undertow = new Undertow();}

这个类的代码太多了,这里就不一一贴出来了,我们平常在 application.properties 中配置的server.xxx就是这个类中属性

@ImportBeanPostProcessorsRegistrarpublic static class BeanPostProcessorsRegistrarimplements ImportBeanDefinitionRegistrar, BeanFactoryAware {@Overridepublic 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);}}}

这个类注册了两个bean: WebServerFactoryCustomizerBeanPostProcessorErrorPageRegistrarBeanPostProcessor 关于这两个bean的作用稍后再详细介绍

EmbeddedTomcat

@Configuration@ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class })@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)public static class EmbeddedTomcat { @Bean public TomcatServletWebServerFactory tomcatServletWebServerFactory() {  return new TomcatServletWebServerFactory(); }}

这个类会在存在Tomcat相关jar包时添加一个 TomcatServletWebServerFactory bean

其他两个相信大家都知道怎么回事了

除了这些这个类还注入了两个类 ServletWebServerFactoryCustomizerTomcatServletWebServerFactoryCustomizer现在前期准备工作已经做好了,看一下这个Tomcat是如何启动的吧

启动

启动入口在 ServletWebServerApplicationContext 中的 onRefresh 方法

protected void onRefresh() {super.onRefresh(); try { createWebServer(); }catch (Throwable ex) {throw new ApplicationContextException("Unable to start web server", ex);}}

Tomcat的启动就在 createWebServer 方法里面了

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();}

首先看一下 getWebServerFactory

protected ServletWebServerFactory getWebServerFactory() { // 这里获取的beanname就是上方注册的tomcatServletWebServerFactory了 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);}

准备环境里注册的bean现在出来一个了。注意,上方还注册了一个后置处理器 EmbeddedServletContainerCustomizerBeanPostProcessor ,获取bean tomcatServletWebServerFactory 的时候就会执行后置处理器的 postProcessBeforeInitialization 方法

public Object postProcessBeforeInitialization(Object bean, String beanName)throws BeansException {if (bean instanceof WebServerFactory) {postProcessBeforeInitialization((WebServerFactory) bean);}return bean;}private void postProcessBeforeInitialization(WebServerFactory webServerFactory) { LambdaSafe  .callbacks(WebServerFactoryCustomizer.class, getCustomizers(),     webServerFactory)  .withLogger(WebServerFactoryCustomizerBeanPostProcessor.class)  .invoke((customizer) -> customizer.customize(webServerFactory));}private Collection<WebServerFactoryCustomizer<?>> getCustomizers() { if (this.customizers == null) {  // Look up does not include the parent context  this.customizers = new ArrayList<>(getWebServerFactoryCustomizerBeans());  this.customizers.sort(AnnotationAwareOrderComparator.INSTANCE);  this.customizers = Collections.unmodifiableList(this.customizers); } return this.customizers;}@SuppressWarnings({ "unchecked", "rawtypes" })private Collection<WebServerFactoryCustomizer<?>> getWebServerFactoryCustomizerBeans() { return (Collection) this.beanFactory  .getBeansOfType(WebServerFactoryCustomizer.class, false, false).values();}

这个处理器的作用是获得所有定制器,然后执行定制器的方法

接着往下看

这个时候就可以启动Tomcat了

public WebServer getWebServer(ServletContextInitializer... initializers) { Tomcat tomcat = new Tomcat(); File baseDir = (this.baseDirectory != null ? this.baseDirectory     : createTempDir("tomcat")); tomcat.setBaseDir(baseDir.getAbsolutePath()); Connector connector = new Connector(this.protocol); 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);}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();}private void initialize() throws WebServerException { TomcatWebServer.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())) {     // Remove service connectors so that protocol binding doesn't     // happen when the service is started.     removeServiceConnectors();    }   });   // Start the server to trigger initialization listeners   this.tomcat.start();   // We can re-throw failure exception directly in the main thread   rethrowDeferredStartupExceptions();   try {    ContextBindings.bindClassLoader(context, context.getNamingToken(),            getClass().getClassLoader());   }   catch (NamingException ex) {    // Naming is not enabled. Continue   }   // Unlike Jetty, all Tomcat threads are daemon threads. We create a   // blocking non-daemon to stop immediate shutdown   startDaemonAwaitThread();  }  catch (Exception ex) {   throw new WebServerException("Unable to start embedded Tomcat", ex);  } }}

以上就是SpringBoot如何实现Tomcat自动配置的详细内容,更多关于SpringBoot实现Tomcat自动配置的资料请关注其它相关文章!

人生就是一次充满未知的旅行,在乎的是沿途的风景,

SpringBoot如何实现Tomcat自动配置

相关文章:

你感兴趣的文章:

标签云: