4_7.springboot2.x嵌入式servlet容器自動配置原理


概述

Spring Boot對所支持的Servlet Web服務器實現做了建模抽象:

Servlet容器類型  WebServer模型接口 WebServer工廠實現類
Tomcat    TomcatWebServer     TomcatServletWebServerFactory
Jetty    JettyWebServer    JettyServletWebServerFactory
Undertow  UndertowWebServer  UndertowServletWebServerFactory

基於此模型概念,在一個Servlet Web應用中,Spring Boot會使用上表中所說的WebServer工廠組件生成相應的WebServer實例。而這里的WebServer工廠組件又是從哪里來的呢 ? 這就是自動配置類ServletWebServerFactoryAutoConfiguration的任務了。

自動配置類ServletWebServerFactoryAutoConfiguration首先通過注解聲明自己的生效條件:類 ServletRequest 存在於 classpath 上時才生效,也就是要求javax.servlet-api包必須被引用;當前應用必須是Spring MVC應用才生效;在以上條件被滿足時,ServletWebServerFactoryAutoConfiguration引入了如下三個配置類 :

EmbeddedTomcat

EmbeddedJetty

EmbeddedUndertow

這三個配置類是ServletWebServerFactoryConfiguration的嵌套配置類,它們會分別檢測classpath上存在的類,從而判斷當前應用使用的是Tomcat/Jetty/Undertow中的哪一個Servlet Web服務器,從而決定定義相應的工廠組件bean : TomcatServletWebServerFactory/JettyServletWebServerFactory/UndertowServletWebServerFactory。

詳解:

ServletWebServerFactoryAutoConfiguration

/**
* EnableAutoConfiguration Auto-configuration for servlet web servers.
*
*/
@Configuration
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
// 僅在類 ServletRequest 存在於 classpath 上時才生效
@ConditionalOnClass(ServletRequest.class)
// 僅在當前應用是 Servlet Web 應用時才生效
@ConditionalOnWebApplication(type = Type.SERVLET)
// 確保前綴為 server 的配置參數加載到 bean ServerProperties
@EnableConfigurationProperties(ServerProperties.class)
// 1. 導入 ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar 以注冊
// BeanPostProcessor : WebServerFactoryCustomizerBeanPostProcessor 和
// ErrorPageRegistrarBeanPostProcessor
// 2. 導入 EmbeddedTomcat/EmbeddedJetty/EmbeddedUndertow 這三個屬於
// ServletWebServerFactoryConfiguration 的嵌套配置類,這三個配置類會分別檢測
// classpath上存在的類,從而判斷當前應用使用的是 Tomcat/Jetty/Undertow,
// 從而決定定義哪一個 Servlet Web服務器的工廠 bean :
// TomcatServletWebServerFactory/JettyServletWebServerFactory/UndertowServletWebServerFactory
@Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,
        ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,
        ServletWebServerFactoryConfiguration.EmbeddedJetty.class,
        ServletWebServerFactoryConfiguration.EmbeddedUndertow.class })
public class ServletWebServerFactoryAutoConfiguration {


    // 定義 bean ServletWebServerFactoryCustomizer,這里與properties中serverProperties配置聯系,解釋了為什么配置文件也可修改servlet容器配置
    @Bean
    public ServletWebServerFactoryCustomizer servletWebServerFactoryCustomizer(
            ServerProperties serverProperties) {
        return new ServletWebServerFactoryCustomizer(serverProperties);
    }


    // 針對當前Servlet容器是Tomcat時定義該 bean,用於定制化 TomcatServletWebServerFactory
    @Bean
    // 僅在類 org.apache.catalina.startup.Tomcat 存在於 classpath 上時才生效
    @ConditionalOnClass(name = "org.apache.catalina.startup.Tomcat")
    public TomcatServletWebServerFactoryCustomizer tomcatServletWebServerFactoryCustomizer(
            ServerProperties serverProperties) {
        return new TomcatServletWebServerFactoryCustomizer(serverProperties);
    }


    /**
     * Registers a WebServerFactoryCustomizerBeanPostProcessor. Registered via
     * ImportBeanDefinitionRegistrar for early registration.
     * 這是一個 ImportBeanDefinitionRegistrar, 它會向容器注入兩個 BeanPostProcessor :
     * 1. WebServerFactoryCustomizerBeanPostProcessor
     * 該 BeanPostProcessor 會搜集容器中所有的 WebServerFactoryCustomizer,對當前應用所采用的
     * WebServerFactory 被初始化前進行定制
     * 2. ErrorPageRegistrarBeanPostProcessor
     * 該 BeanPostProcessor 會搜集容器中所有的 ErrorPageRegistrar,添加到當前應用所采用的
     * ErrorPageRegistry 中,實際上,這里的 ErrorPageRegistry 會是 ConfigurableWebServerFactory,
     * 具體實現上來講,是一個 ConfigurableTomcatWebServerFactory,ConfigurableJettyWebServerFactory
     * 或者 ConfigurableUndertowWebServerFactory,分別對應 Tomcat, Jetty, Undertow 這三種
     * Servlet Web 容器的工廠類
     */
    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);
            }
        }


    }

ServletWebServerFactoryConfiguration

       ServletWebServerFactoryConfiguration是一個針對ServletWebServerFactory進行配置的配置類。它通過檢測應用classpath存在的類,從而判斷當前應用要使用哪個Servlet容器:Tomcat,Jetty還是Undertow。檢測出來之后,定義相應的Servlet Web服務器工廠組件bean 

Servlet容器類型  WebServerFactory實現類
Tomcat  TomcatServletWebServerFactory
Jetty     JettyServletWebServerFactory
Undertow     UndertowServletWebServerFactory

注意,以上三個實現類都繼承自抽象基類AbstractServletWebServerFactory,實現了接口WebServerFactory,ErrorPageRegistry。該特征會被WebServerFactoryCustomizerBeanPostProcessor和ErrorPageRegistrarBeanPostProcessor使用,用於對WebServerFactory進行定制化,以及設置相應的錯誤頁面。

@Configuration
class ServletWebServerFactoryConfiguration {

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

}

/**
* Nested configuration if Jetty is being used.
*/
@Configuration
@ConditionalOnClass({ Servlet.class, Server.class, Loader.class, WebAppContext.class })
@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
public static class EmbeddedJetty {

@Bean
public JettyServletWebServerFactory JettyServletWebServerFactory() {
return new JettyServletWebServerFactory();
}

}

/**
* Nested configuration if Undertow is being used.
*/
@Configuration
@ConditionalOnClass({ Servlet.class, Undertow.class, SslClientAuthMode.class })
@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
public static class EmbeddedUndertow {

@Bean
public UndertowServletWebServerFactory undertowServletWebServerFactory() {
return new UndertowServletWebServerFactory();
}

}

}

查看代碼:

這里以TomcatServletWebServerFactory:

注意這里此方法返回值:getTomcatWebServer(tomcat);查看此方法:傳入兩個參數,只要得到端口號大於0就默認啟動

同時查看:其中TomcatWebServer類構造方法:

這時候tomcat啟動開始。查看initialize();方法

private void initialize() throws WebServerException {
		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) {
				stopSilently();
				destroySilently();
				throw new WebServerException("Unable to start embedded Tomcat", ex);
			}
		}
	}

可以看到有啟動: this.tomcat.start();啟動!

思考

如何對嵌入式容器的配置修改是怎么生效的

兩種方法:1、修改properties 

2、配置servlet容器定制器

@Bean
public WebServerFactoryCustomizer<TomcatServletWebServerFactory> myWebServerFactoryCustomizer() {
    return new WebServerFactoryCustomizer<TomcatServletWebServerFactory>() {
        public void customize(TomcatServletWebServerFactory factory) {
            //設置相關配置
            factory.setPort(8080);
        }
    };
}

思考:怎么修改的原理?

springboot版本對照:

1.5.x:-->

//導入后置處理器的注冊器,作用給容器導入組件

@Import(BeanPostProcessorsRegistrar.class

@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@Configuration
@ConditionalOnWebApplication
//導入后置處理器的注冊器
@Import(BeanPostProcessorsRegistrar.class)
public class EmbeddedServletContainerAutoConfiguration {.....}

2.x:將其作為內部類導入組件

@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 {
    .....
    public static class BeanPostProcessorsRegistrar implements ImportBeanDefinitionRegistrar, BeanFactoryAware {
    ...

        @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,BeanDefinitionRegistry registry) {
       if (this.beanFactory == null) {
      return;
           }
        //導入servlet定制器的后置處理器    
       registerSyntheticBeanIfMissing(registry, "webServerFactoryCustomizerBeanPostProcessor",
       WebServerFactoryCustomizerBeanPostProcessor.class);
       registerSyntheticBeanIfMissing(registry, "errorPageRegistrarBeanPostProcessor",
         ErrorPageRegistrarBeanPostProcessor.class);
            }
     }


}

觀察方法:BeanPostProcessorsRegistrar,導入servlet定制器的后置處理器 

作用:后置處理器:bean初始化前后(創建完對象,還沒賦值賦值)執行初始化工作

WebServerFactoryCustomizerBeanPostProcessor

@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
   if (bean instanceof WebServerFactory) {
      postProcessBeforeInitialization((WebServerFactory) bean);
   }
   return bean;
}

初始化之前:判斷如果當前初始化組件是WebServerFactory 的一個組件,就調用postProcessBeforeInitialization此方法,

private void postProcessBeforeInitialization(WebServerFactory webServerFactory) {
LambdaSafe.callbacks(WebServerFactoryCustomizer.class, getCustomizers(), webServerFactory)
.withLogger(WebServerFactoryCustomizerBeanPostProcessor.class)
.invoke((customizer) -> customizer.customize(webServerFactory));
}

此方法:獲取所有的定制器,調用每一個定制器的customize方法,來給servlet容器進行屬性賦值

getCustomizers()方法以上調用:


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

this.customizers = new ArrayList<>(getWebServerFactoryCustomizerBeans());此方法點進去:
@SuppressWarnings({ "unchecked", "rawtypes" })
private Collection<WebServerFactoryCustomizer<?>> getWebServerFactoryCustomizerBeans() {
return (Collection) this.beanFactory.getBeansOfType(WebServerFactoryCustomizer.class, false, false).values();
}

作用:從容器中獲取所有該類型的組件:WebServerFactoryCustomizer這里該組件我們可以定制。

思考2?在properties中是怎樣定制servlet容器的??

@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 {......}

2.x:中直接在servelt容器工廠的自動配置類:@EnableConfigurationProperties(ServerProperties.class)注解,向容器中添加ServerProperties組件,在SpringBoot中xxxProperties類都是和配置文件映射的類,用戶可以通過配置文件配置類中屬性。例如:用戶可以通過server.port配置容器端口號。

1.5.x:serverProperties中實現方法。

嵌入式servlet容器自動配置原理的步驟

1)、SpringBoot根據導入的依賴情況,容器中導入的ServletWebServerFactoryConfiguration配置類,給容器添加相應的TomcatServletWebServerFactory,通過TomcatServletWebServerFactory的getWebServer()啟動servelt容器

2)、容器中某個組件要創建對象就會驚動后置處理器;@Import({ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,

只要是嵌入式的Servlet容器工廠,后置處理器就工作,用與定制嵌入式容器的配置修改,

3)、后置處理器,執行postProcessBeforeInitialization()方法,調用定制器的定制方法;

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM