SpringBoot如何实现Tomcat自动配置

 更新时间:2021年03月12日 09:30:40   作者:筱进  
这篇文章主要介绍了SpringBoot如何实现Tomcat自动配置,帮助大家更好的理解和学习使用SpringBoot框架,感兴趣的朋友可以了解下

目录

准备工作

我们知道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 {

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

	/**
	 * Registers a {@link WebServerFactoryCustomizerBeanPostProcessor}. Registered via
	 * {@link ImportBeanDefinitionRegistrar} for early 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);
			}
		}

	}

}

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

  • @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;

	@NestedConfigurationProperty
	private final ErrorProperties error = new ErrorProperties();

	private Boolean useForwardHeaders;

	private String serverHeader;

	private int maxHttpHeaderSize = 0; // bytes

	private Duration connectionTimeout;

	@NestedConfigurationProperty
	private Ssl ssl;

	@NestedConfigurationProperty
	private final Compression compression = new Compression();

	@NestedConfigurationProperty
	private 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就是这个类中属性

@Import
BeanPostProcessorsRegistrar
public static class BeanPostProcessorsRegistrar
			implements ImportBeanDefinitionRegistrar, BeanFactoryAware {
		@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);
			}
		}

	}

这个类注册了两个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项目中访问HTML页面的实现示例

    SpringBoot项目中访问HTML页面的实现示例

    本文主要介绍了SpringBoot项目中访问HTML页面的实现示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2024-08-08
  • SpringBoot集成Nacos的详细教程

    SpringBoot集成Nacos的详细教程

    这篇文章主要介绍了SpringBoot集成Nacos的详细教程,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-09-09
  • JWT Token实现方法及步骤详解

    JWT Token实现方法及步骤详解

    这篇文章主要介绍了JWT Token实现方法及步骤详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-09-09
  • Java微信公众号开发之通过微信公众号获取用户信息

    Java微信公众号开发之通过微信公众号获取用户信息

    这篇文章主要介绍了Java微信公众号开发之通过微信公众号获取用户信息,需要的朋友可以参考下
    2017-05-05
  • Java多态性抽象类与接口细致详解

    Java多态性抽象类与接口细致详解

    这篇文章主要给大家介绍了关于Java中方法使用的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-08-08
  • Spring Mvc下实现以文件流方式下载文件的方法示例

    Spring Mvc下实现以文件流方式下载文件的方法示例

    这篇文章主要介绍了Spring Mvc下实现以文件流方式下载文件的方法示例,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2019-05-05
  • Spring中ThreadLocal的解析

    Spring中ThreadLocal的解析

    这篇文章主要介绍了Spring中ThreadLocal的解析,Spring通过各种DAO模板类降低了开发者使用各种数据持久技术的难度。这些模板类都是线程安全的,也就是说,多个DAO可以复用同一个模板实例而不会发生冲突,下面一起进入文章学子详细内容吧
    2022-01-01
  • SpringMVC Restful api接口实现的代码

    SpringMVC Restful api接口实现的代码

    本篇文章主要介绍了SpringMVC Restful api接口实现的代码,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-09-09
  • 详解Spring如何整合Mybatis

    详解Spring如何整合Mybatis

    今天给大家带来的是关于Java的相关知识,文章围绕着Spring如何整合Mybatis展开,文中有非常详细的介绍及代码示例,需要的朋友可以参考下
    2021-06-06
  • java 线程池的实现原理、优点与风险、以及4种线程池实现

    java 线程池的实现原理、优点与风险、以及4种线程池实现

    这篇文章主要介绍了java 线程池的实现原理、优点与风险、以及4种线程池实现包括了:配置线程池大小配置,线程池的实现原理等,需要的朋友可以参考下
    2023-02-02

最新评论