SpringBoot如何實(shí)現(xiàn)Tomcat自動配置
目錄
準(zhǔn)備工作
我們知道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 這個注解是決定配置類的加載順序的,當(dāng)注解里的值越小越先加載,而 Ordered.HIGHEST_PRECEDENCE 的值是 Integer.MIN_VALUE 也就是說這個類肯定是最先加載的那一批
- @ConditionalOnXXX 在之前的文章中已經(jīng)無數(shù)次提到了,就不再闡述了
- @EnableConfigurationProperties 開啟 ServerProperties 類的屬性值配置。而這個類里面包含的就是Web服務(wù)的配置
@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 關(guān)于這兩個bean的作用稍后再詳細(xì)介紹
- 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相關(guān)jar包時添加一個 TomcatServletWebServerFactory bean
其他兩個相信大家都知道怎么回事了
除了這些這個類還注入了兩個類 ServletWebServerFactoryCustomizer 和 TomcatServletWebServerFactoryCustomizer
現(xiàn)在前期準(zhǔn)備工作已經(jīng)做好了,看一下這個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);
}
準(zhǔn)備環(huán)境里注冊的bean現(xiàn)在出來一個了。注意,上方還注冊了一個后置處理器 EmbeddedServletContainerCustomizerBeanPostProcessor ,獲取bean tomcatServletWebServerFactory 的時候就會執(zhí)行后置處理器的 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();
}
這個處理器的作用是獲得所有定制器,然后執(zhí)行定制器的方法
接著往下看
這個時候就可以啟動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如何實(shí)現(xiàn)Tomcat自動配置的詳細(xì)內(nèi)容,更多關(guān)于SpringBoot實(shí)現(xiàn)Tomcat自動配置的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
SpringBoot?AnnotationUtils工具類的使用實(shí)例詳解
這篇文章主要介紹了SpringBoot?AnnotationUtils工具類的使用,使用自定義注解標(biāo)記業(yè)務(wù)方法,原生Java獲取注解及AnnotationUtils工具類獲取方法,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下2022-09-09
SpringBoot中實(shí)現(xiàn)異步調(diào)用@Async詳解
這篇文章主要介紹了SpringBoot中實(shí)現(xiàn)異步調(diào)用@Async詳解,在SpringBoot的日常開發(fā)中,一般都是同步調(diào)用的,但實(shí)際中有很多場景非常適合使用異步來處理,需要的朋友可以參考下2024-01-01
Springboot實(shí)現(xiàn)WebMvcConfigurer接口定制mvc配置詳解
這篇文章主要介紹了Springboot實(shí)現(xiàn)WebMvcConfigurer接口定制mvc配置詳解,spring?boot拋棄了傳統(tǒng)xml配置文件,通過配置類(標(biāo)注@Configuration的類,@Configuration配置類相當(dāng)于一個xml配置文件)以JavaBean形式進(jìn)行相關(guān)配置,需要的朋友可以參考下2023-09-09
關(guān)于java自定義線程池的原理與實(shí)現(xiàn)
本文介紹了如何自定義線程池和阻塞隊列,包括阻塞隊列的實(shí)現(xiàn)方法,線程池的構(gòu)建以及拒絕策略的應(yīng)用,詳細(xì)闡述了線程池中任務(wù)的提交和執(zhí)行流程,以及如何處理任務(wù)超出隊列容量的情況2022-04-04
Springboot+MyBatist實(shí)現(xiàn)前后臺交互登陸功能方式
這篇文章主要介紹了Springboot+MyBatist實(shí)現(xiàn)前后臺交互登陸功能方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-01-01
javaweb實(shí)現(xiàn)投票系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了javaweb實(shí)現(xiàn)投票系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2022-09-09
SpringBoot feign動態(tài)設(shè)置數(shù)據(jù)源(https請求)
這篇文章主要介紹了SpringBoot如何在運(yùn)行時feign動態(tài)添加數(shù)據(jù)源,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2021-08-08

