软件编程
位置:首页>> 软件编程>> java编程>> SpringBoot如何实现Tomcat自动配置

SpringBoot如何实现Tomcat自动配置

作者:筱进  发布时间:2022-04-28 02:44:10 

标签: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 {

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

来源:https://www.cicoding.cn/springboot/tomcat-auto-configuration-in-springboot/

0
投稿

猜你喜欢

手机版 软件编程 asp之家 www.aspxhome.com