【Spring Boot】(19)、Spring Boot嵌入式Servlet容器自動配置原理
EmbeddedServletContainerAutoConfiguration是嵌入式Servlet容器的自動配置類,該類在spring-boot-autoconfigure-xxx.jar中的web模組可以找到。
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE) @Configuration @ConditionalOnWebApplication @Import(BeanPostProcessorsRegistrar.class) public class EmbeddedServletContainerAutoConfiguration { @Configuration @ConditionalOnClass({ Servlet.class, Tomcat.class }) @ConditionalOnMissingBean(value = EmbeddedServletContainerFactory.class, search = SearchStrategy.CURRENT) public static class EmbeddedTomcat { @Bean public TomcatEmbeddedServletContainerFactory tomcatEmbeddedServletContainerFactory() { return new TomcatEmbeddedServletContainerFactory(); } } @Configuration @ConditionalOnClass({ Servlet.class, Server.class, Loader.class, WebAppContext.class }) @ConditionalOnMissingBean(value = EmbeddedServletContainerFactory.class, search = SearchStrategy.CURRENT) public static class EmbeddedJetty { @Bean public JettyEmbeddedServletContainerFactory jettyEmbeddedServletContainerFactory() { return new JettyEmbeddedServletContainerFactory(); } } @Configuration @ConditionalOnClass({ Servlet.class, Undertow.class, SslClientAuthMode.class }) @ConditionalOnMissingBean(value = EmbeddedServletContainerFactory.class, search = SearchStrategy.CURRENT) public static class EmbeddedUndertow { @Bean public UndertowEmbeddedServletContainerFactory undertowEmbeddedServletContainerFactory() { return new UndertowEmbeddedServletContainerFactory(); } } //other code... }
TomcatEmbeddedServletContainerFactory
JettyEmbeddedServletContainerFactory
UndertowEmbeddedServletContainerFactory
這裡以大家熟悉的Tomcat為例,首先Spring Boot會判斷當前環境中是否引入了Servlet和Tomcat依賴,並且當前容器中沒有自定義的EmbeddedServletContainerFactory的情況下,則建立Tomcat容器工廠。其他Servlet容器工廠也是同樣的道理。
EmbeddedServletContainerFactory:嵌入式Servlet容器工廠
public interface EmbeddedServletContainerFactory {
EmbeddedServletContainer getEmbeddedServletContainer(
ServletContextInitializer... initializers);
}
該工廠介面主要有三個實現類,分別對應三種嵌入式Servlet容器的工廠類,如圖所示:
EmbeddedServletContainer
同樣道理,對應三種嵌入式Servlet容器,如圖所示:
TomcatEmbeddedServletContainerFactory類為例:
public class TomcatEmbeddedServletContainerFactory
extends AbstractEmbeddedServletContainerFactory implements ResourceLoaderAware {
//other code...
@Override
public EmbeddedServletContainer getEmbeddedServletContainer(
ServletContextInitializer... initializers) {
//建立一個Tomcat
Tomcat tomcat = new Tomcat();
//配置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);
//包裝tomcat物件,返回一個嵌入式Tomcat容器,內部會啟動該tomcat容器
return getTomcatEmbeddedServletContainer(tomcat);
}
}
TomcatEmbeddedServletContainerFactory#getTomcatEmbeddedServletContainer函式:
protected TomcatEmbeddedServletContainer getTomcatEmbeddedServletContainer(
Tomcat tomcat) {
return new TomcatEmbeddedServletContainer(tomcat, getPort() >= 0);
}
該函式很簡單,就是來建立Tomcat容器並返回。
TomcatEmbeddedServletContainer類定義:
public class TomcatEmbeddedServletContainer implements EmbeddedServletContainer {
public TomcatEmbeddedServletContainer(Tomcat tomcat, boolean autoStart) {
Assert.notNull(tomcat, "Tomcat Server must not be null");
this.tomcat = tomcat;
this.autoStart = autoStart;
//初始化嵌入式Tomcat容器,並啟動Tomcat
initialize();
}
private void initialize() throws EmbeddedServletContainerException {
TomcatEmbeddedServletContainer.logger
.info("Tomcat initialized with port(s): " + getPortsDescription(false));
synchronized (this.monitor) {
try {
addInstanceIdToEngineName();
try {
final Context context = findContext();
context.addLifecycleListener(new LifecycleListener() {
@Override
public void lifecycleEvent(LifecycleEvent 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
//啟動tomcat
this.tomcat.start();
// We can re-throw failure exception directly in the main thread
rethrowDeferredStartupExceptions();
try {
ContextBindings.bindClassLoader(context, getNamingToken(context),
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) {
containerCounter.decrementAndGet();
throw ex;
}
}
catch (Exception ex) {
stopSilently();
throw new EmbeddedServletContainerException(
"Unable to start embedded Tomcat", ex);
}
}
}
}
那麼問題來了,我們對嵌入式容器的修改配置是如何生效的?
之前講過可以通過修改ServerProperties相關配置或者自定義EmbeddedServletContainerCustomizer定製器兩種方式來修改預設配置。而ServerProperties其實就是EmbeddedServletContainerCustomizer的子類,所以說到底還是EmbeddedServletContainerCustomizer起了修改的作用。
其實在EmbeddedServletContainerAutoConfiguration類上匯入了一個BeanPostProcessorsRegistrar類:
@Import(BeanPostProcessorsRegistrar.class)
該類主要用於給容器匯入元件,看定義:
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;
}
//註冊了一個EmbeddedServletContainerCustomizerBeanPostProcessor後置處理器的Bean
registerSyntheticBeanIfMissing(registry,
"embeddedServletContainerCustomizerBeanPostProcessor",
EmbeddedServletContainerCustomizerBeanPostProcessor.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初始化前(建立完成,還未屬性賦值),會執行初始化工作。
registerBeanDefinitions方法中向容器中匯入了EmbeddedServletContainerCustomizerBeanPostProcessor型別的Bean:
public class EmbeddedServletContainerCustomizerBeanPostProcessor
implements BeanPostProcessor, BeanFactoryAware {
//初始化之前
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
if (bean instanceof ConfigurableEmbeddedServletContainer) {
postProcessBeforeInitialization((ConfigurableEmbeddedServletContainer) bean);
}
return bean;
}
private void postProcessBeforeInitialization(
ConfigurableEmbeddedServletContainer bean) {
//獲取所有的定製器,呼叫每個定製器的customize方法,給Servlet容器進行屬性賦值
for (EmbeddedServletContainerCustomizer customizer : getCustomizers()) {
customizer.customize(bean);
}
}
//從IOC容器中獲取所有型別為EmbeddedServletContainerCustomizer的定製器
private Collection<EmbeddedServletContainerCustomizer> getCustomizers() {
if (this.customizers == null) {
// Look up does not include the parent context
this.customizers = new ArrayList<EmbeddedServletContainerCustomizer>(
//型別為EmbeddedServletContainerCustomizer的Bean
this.beanFactory
.getBeansOfType(EmbeddedServletContainerCustomizer.class,
false, false)
.values());
Collections.sort(this.customizers, AnnotationAwareOrderComparator.INSTANCE);
this.customizers = Collections.unmodifiableList(this.customizers);
}
return this.customizers;
}
//other code...
}
EmbeddedServletContainerCustomizer型別的元件,用於自定義屬性配置,然後在匯入的後置處理器中獲取到該元件,並呼叫該自定義元件的customize方法,來修改預設的屬性配置。
總結:
(1)、Spring Boot根據匯入容器型別的依賴情況,會給容器中新增相應的EmbeddedServletContainerFactory嵌入式Servlet容器工廠;
(2)、應用程式一旦匯入了嵌入式Servlet容器依賴,就會觸動後置處理器EmbeddedServletContainerCustomizerBeanPostProcessor;
====================打個廣告,歡迎關注====================
QQ: |
412425870 |
微信公眾號:Cay課堂 |
|
csdn部落格: |
http://blog.csdn.net/caychen |
碼雲: |
https://gitee.com/caychen/ |
github: |
https://github.com/caychen |
點選群號或者掃描二維碼即可加入QQ群: |
|
|