SpringBoot如何实现Tomcat自动配置

网友投稿 202 2023-01-26

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: WebServerFactoryCustomizerBeanPostProcessor 和 ErrorPageRegistrarBeanPostProcessor 关于这两个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

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

除了这些这个类还注入了两个类 ServletWebServerFactoryCustomizer 和 TomcatServletWebServerFactoryCustomizer

现在前期准备工作已经做好了,看一下这个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 的时候http://就会执行后置处理器的 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> 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> 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自动配置的资料请关注我们其它相关文章!

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:详解SpringBoot健康检查的实现原理
下一篇:如何使用开放api接口(开放api接口什么意思)
相关文章

 发表评论

暂时没有评论,来抢沙发吧~