Spring Boot 嵌入式 Servlet 容器自動配置原理
Spring Boot 做 嵌入式 Servlet 容器自動配置的原始碼包:EmbeddedServletContainerAutoConfiguration
原始碼如下:
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE) @Configuration @ConditionalOnWebApplication @Import(BeanPostProcessorsRegistrar.class) public class EmbeddedServletContainerAutoConfiguration { /** * Nested configuration if Tomcat is being used. */ @Configuration //判斷當前專案是否引入了 Tomcat 依賴, 如果引入了 Tomcat 依賴,就建立 Tomcat 工廠 @ConditionalOnClass({ Servlet.class, Tomcat.class }) //判斷當前容器中沒有 EmbeddedServletContainerFactory(嵌入式的容器工廠, 作用就是建立 Servlet 容器) 元件就自己建立 @ConditionalOnMissingBean(value = EmbeddedServletContainerFactory.class, search = SearchStrategy.CURRENT) public static class EmbeddedTomcat { @Bean public TomcatEmbeddedServletContainerFactory tomcatEmbeddedServletContainerFactory() { return new TomcatEmbeddedServletContainerFactory(); } } /** * Nested configuration if Jetty is being used. */ @Configuration //判斷當前專案是否引入了 Jetty 依賴, 如果引入了 Jetty 依賴,就建立 Jetty 工廠 @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(); } } /** * Nested configuration if Undertow is being used. */ @Configuration //判斷當前專案是否引入了 Undertow 依賴, 如果引入了 Undertow 依賴,就建立 Undertow 工廠 @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(); } } /** * Registers a {@link EmbeddedServletContainerCustomizerBeanPostProcessor}. 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, "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); } } } }
EmbeddedServletContainerFactory(嵌入式的容器工廠, 作用就是建立 Servlet 容器) 原始碼:
public interface EmbeddedServletContainerFactory { /** * Gets a new fully configured but paused {@link EmbeddedServletContainer} instance. * Clients should not be able to connect to the returned server until * {@link EmbeddedServletContainer#start()} is called (which happens when the * {@link ApplicationContext} has been fully refreshed). * @param initializers {@link ServletContextInitializer}s that should be applied as * the container starts * @return a fully configured and started {@link EmbeddedServletContainer} * @see EmbeddedServletContainer#stop() */ //獲取嵌入式的 Servlet 容器 EmbeddedServletContainer getEmbeddedServletContainer(ServletContextInitializer... initializers); }
EmbeddedServletContainer(獲取嵌入式的 Servlet 容器):
Toncat 嵌入式容器工廠(TomcatEmbeddedServletContainerFactory) 部分原始碼:
@Override public EmbeddedServletContainer getEmbeddedServletContainer(ServletContextInitializer... initializers) { //建立一個 Toncat Tomcat tomcat = new Tomcat(); /**** Start 配置 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); /**** End 配置 Tomcat 工作的基本環節 ***/ //將配置好的 Tomcat 傳入進去,返回一個嵌入式的 Servlet 容器, 並且啟動 Tomcat 伺服器 return getTomcatEmbeddedServletContainer(tomcat); }
getTomcatEmbeddedServletContainer(tomcat) 方法執行原始碼:
public TomcatEmbeddedServletContainer(Tomcat tomcat, boolean autoStart) {
Assert.notNull(tomcat, "Tomcat Server must not be null");
this.tomcat = tomcat;
this.autoStart = autoStart;
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);
}
}
}
自己對嵌入式容器配置的修改是怎麼生效的?
第一種配置檔案的方式,第二種嵌入式容器定製器(EmbeddedServletContainerCustomizer)
兩種方式實際上都用的是EmbeddedServletContainerCustomizer,那麼肯定就是 EmbeddedServletContainerCustomizer幫我們修改了嵌入式容器的配置。
幫我們修改嵌入式容器的配置原理: 此文最開頭
//匯入 BeanPostProcessorsRegistrar,作用: 給容器中匯入一些元件
//匯入了 EmbeddedServletContainerCustomizerBeanPostProcessor 後置處理器
//後置處理器: 在 bean 初始化前後(剛建立完物件,還沒屬性賦值)執行初始化工作
@Import(BeanPostProcessorsRegistrar.class)
public class EmbeddedServletContainerAutoConfiguration {
BeanPostProcessorsRegistrar 原始碼如下
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,
"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);
}
}
}
EmbeddedServletContainerCustomizerBeanPostProcessor 原始碼如下:
public class EmbeddedServletContainerCustomizerBeanPostProcessor implements BeanPostProcessor, BeanFactoryAware {
private ListableBeanFactory beanFactory;
private List<EmbeddedServletContainerCustomizer> customizers;
@Override
public void setBeanFactory(BeanFactory beanFactory) {
Assert.isInstanceOf(ListableBeanFactory.class, beanFactory,
"EmbeddedServletContainerCustomizerBeanPostProcessor can only be used "
+ "with a ListableBeanFactory");
this.beanFactory = (ListableBeanFactory) beanFactory;
}
//初始化之前
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
//如果當前初始化的是一個 ConfigurableEmbeddedServletContainer 型別的元件, 就呼叫 postProcessBeforeInitialization方法
if (bean instanceof ConfigurableEmbeddedServletContainer) {
postProcessBeforeInitialization((ConfigurableEmbeddedServletContainer) bean);
}
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException {
return bean;
}
private void postProcessBeforeInitialization(ConfigurableEmbeddedServletContainer bean) {
//獲取所有的定製器, 呼叫每一個 定製器的 customizer 方法給Servlet 容器屬性賦值
for (EmbeddedServletContainerCustomizer customizer : getCustomizers()) {
customizer.customize(bean);
}
}
private Collection<EmbeddedServletContainerCustomizer> getCustomizers() {
if (this.customizers == null) {
// Look up does not include the parent context
this.customizers = new ArrayList<EmbeddedServletContainerCustomizer>(
//從容器中獲取所有的這個型別(EmbeddedServletContainerCustomizer)的元件
//定製 Servlet 容器, 給容器中可以新增一個 EmbeddedServletContainerCustomizer 型別的元件
this.beanFactory
.getBeansOfType(EmbeddedServletContainerCustomizer.class,
false, false)
.values());
Collections.sort(this.customizers, AnnotationAwareOrderComparator.INSTANCE);
this.customizers = Collections.unmodifiableList(this.customizers);
}
return this.customizers;
}
}
總結: 1)、Spring Boot 根據匯入的依賴情況, 給容器中新增響應的嵌入式容器工廠(EmbeddedServletContainerFactory:TomcatEmbeddedServletContainerFactory)
2)、容器中某個元件要建立物件就會呼叫 後置處理器: EmbeddedServletContainerCustomizerBeanPostProcessor
只要是嵌入式的 Servlet 容器工廠,後置處理器就工作
3)、後置處理器,從容器中獲取的 EmbeddedServletContainerCustomizer,呼叫定製器的定製方法