一、1.x 版本的嵌入式Servlet自动配置原理
【1】嵌入式Servlet容器的自动配置类(EmbeddedServletContainerAutoConfiguration):
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@Configuration
@ConditionalOnWebApplication //表示在web应用环境下该自动配置才生效
//作用:给容器中导入一些组件
//导入了(后置处理器)EmbeddedServletContainerCustomizerBeanPostProcessor组件。作用:在Bean初始化前后(刚创建完前后还未属性赋值)执行前置/后置的逻辑
@Import(BeanPostProcessorsRegistrar.class)
public class EmbeddedServletContainerAutoConfiguration {
@Configuration
@ConditionalOnClass({ Servlet.class, Tomcat.class })//判断当前是否引入了tomcat依赖
//判断当前容器没有用户自己定义的嵌入式servlet容器工厂(EmbeddedServletContainerFactory)。作用:servlet容器工厂用来创建嵌入式的servlet容器
@ConditionalOnMissingBean(value = EmbeddedServletContainerFactory.class, search = SearchStrategy.CURRENT)
public static class EmbeddedTomcat {
@Bean
public TomcatEmbeddedServletContainerFactory tomcatEmbeddedServletContainerFactory() {
return new TomcatEmbeddedServletContainerFactory();
}
}
//Jetty
//Undertow
}
【2】EmbeddedServletContainerFactory 嵌入式servlet容器工厂
public interface EmbeddedServletContainerFactory {
//获取嵌入式的servlet容器
EmbeddedServletContainer getEmbeddedServletContainer(ServletContextInitializer... var1);
}
(1)EmbeddedServletContainerFactory 中的三种嵌入式的servlet容器工厂:
(2)对应的三种嵌入式servlet容器:
【3】以 TomcatEmbeddedServletContainerFactory 为例
@Override
public EmbeddedServletContainer getEmbeddedServletContainer(
ServletContextInitializer... initializers) {
//1.创建一个Tomcat
Tomcat tomcat = new Tomcat();
//2.配置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);
//3.将配置好的Tomcat放到servlet容器中,然后返回一个EmbeddedServletContainer,启动Tomcat服务器
return getTomcatEmbeddedServletContainer(tomcat);
}
protected TomcatEmbeddedServletContainer getTomcatEmbeddedServletContainer(
Tomcat tomcat) {
return new TomcatEmbeddedServletContainer(tomcat, getPort() >= 0);
}
当tomcat, getPort() >= 0
成立,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 {
// Remove service connectors to that protocol binding doesn't happen
// yet
removeServiceConnectors();
// Start the server to trigger initialization listeners
// 启动Tomcat服务器
this.tomcat.start();
// We can re-throw failure exception directly in the main thread
rethrowDeferredStartupExceptions();
Context context = findContext();
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) {
throw new EmbeddedServletContainerException(
"Unable to start embedded Tomcat", ex);
}
}
}
(1)对嵌入式容器的配置是如何生效的?
- 修改
ServerProperties
里的属性。 - 自定义一个嵌入式servlet容器定制器
EmbeddedServletContainerCustomizer
。
(2)EmbeddedServletContainerCustomizer
servlet容器定制器如何帮助我们修改的servlet容器的配置?
① 容器中导入了EmbeddedServletContainerCustomizerBeanPostProcessor
(后置处理器)
//在初始化之前(对象创建好但是还未赋值)
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
//如果当前初始化的是ConfigurableEmbeddedServletContainer类型的组件
if (bean instanceof ConfigurableEmbeddedServletContainer) {
//就调用postProcessBeforeInitialization方法
postProcessBeforeInitialization((ConfigurableEmbeddedServletContainer) bean);
}
return bean;
}
private void postProcessBeforeInitialization(
ConfigurableEmbeddedServletContainer bean) {
//获取所有的定制器,调用每一个定制器的customize方法来给servlet容器进行属性赋值
for (EmbeddedServletContainerCustomizer customizer : getCustomizers()) {
customizer.customize(bean);
}
}
//核心就是利用IOC容器获取EmbeddedServletContainerCustomizer类型的组件
//结论:如果我们想要定制servlet容器,就要给容器添加EmbeddedServletContainerCustomizer类型的组件
private Collection<EmbeddedServletContainerCustomizer> getCustomizers() {
if (this.customizers == null) {
// Look up does not include the parent context
this.customizers = new ArrayList<EmbeddedServletContainerCustomizer>(
this.beanFactory
.getBeansOfType(EmbeddedServletContainerCustomizer.class,
false, false)
.values());
Collections.sort(this.customizers, AnnotationAwareOrderComparator.INSTANCE);
this.customizers = Collections.unmodifiableList(this.customizers);
}
return this.customizers;
}
在容器初始化之后进行配置的修改:
@Override
public Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException {
return bean;
}
② ServerProperties也是EmbeddedServletContainerCustomizer
【4】总结步骤
- SpringBoot根据导入的依赖情况,给容器中添加相应的
EmbeddedServletContainerFactory【TomcatEmbeddedServletContainerFactory】
- 容器中某个组件要创建对象就会惊动后置处理器
EmbeddedServletContainerCustomizerBeanPostProcessor
。只要是嵌入式的Servlet容器工厂,后置处理器就工作; - 后置处理器从容器中获取所有的
EmbeddedServletContainerCustomizer
,调用定制器的定制方法