目录:Springboot源码学习目录上文:11、SpringBoot 启动 刷新应用上下文 自动装配解析(三)前言:好了,终于将刷新应用上下文的自动装配流程的源码读完了,顺便也将Spring的配置类解析过程回忆了一遍,现在我们要回过头,继续回到应用上下文刷新的主流程的下一个重点,Web容器的创建和启动 我们应该还记得09、SpringBoot 启动 - 刷新应用上下文 仅自己可见 这篇文章里,refresh方法的源码中,我们标注过里面调用了一个方法onRefresh() 这个方法里面就隐藏着我们的神秘的SpringBoot和Tomcat之类的Web容器整合过程,让我们一睹芳容
在开始debu源码之前,我们先要提前认识三个重要的配置类,这三个配置类都是在SpringBoot自动装配时注入的ServletWebServerFactoryAutoConfiguration
,ErrorMvcAutoConfiguration
,DispatcherServletAutoConfiguration
以及这三个配置类分别又导入的bean(没有列出导入的所有bean,只列一部分启动时就用到的)
-
ServletWebServerFactoryAutoConfiguration
:导入了EmbeddedTomcat
,BeanPostProcessorsRegistrar
,ServletWebServerFactoryCustomizer
,TomcatServletWebServerFactoryCustomizer
-
EmbeddedTomcat
:主要作用就是向beanFactory中注册一个类型是TomcatServletWebServerFactory
的bean, -
ServletWebServerFactoryCustomizer
:实现了WebServerFactoryCustomizer
接口,对EmbeddedTomcat
注册的TomcatServletWebServerFactory
进行配置 -
TomcatServletWebServerFactoryCustomizer
:实现了WebServerFactoryCustomizer
接口,对EmbeddedTomcat
注册的TomcatServletWebServerFactory
进行配置 -
BeanPostProcessorsRegistrar
:实现了,主要作用是向beanFactory中注入了两个beanPostProcessor,WebServerFactoryCustomizerBeanPostProcessor
和ErrorPageRegistrarBeanPostProcessor
-
WebServerFactoryCustomizerBeanPostProcessor
:所有的WebServerFactoryCustomizer
接口实现类的bean都要被该类增强,上面的ServletWebServerFactoryCustomizer
,TomcatServletWebServerFactoryCustomizer
就实现了WebServerFactoryCustomizer
接口 -
ErrorPageRegistrarBeanPostProcessor
:所有的ErrorPageRegistry
接口实现类的bean都要被该类增强,增强方式是获取所有的ErrorPageRegistrar
接口实现类的bean,ErrorPageRegistry
接口实现类的bean进行增强,上面TomcatServletWebServerFactory
也实现了这个接口ErrorPageRegistry
接口 -
ErrorMvcAutoConfiguration
:主要作用是向beanFactory中注入了类型为ErrorPageCustomizer
的beanErrorPageCustomizer
:实现了ErrorPageRegistrar
接口,ErrorPageRegistrarBeanPostProcessor
正是用ErrorPageCustomizer
对实现了ErrorPageRegistry
接口的bean进行增强 -
DispatcherServletAutoConfiguration
:主要作用是向beanFactory中注入了类型为DispatcherServletRegistrationBean
和DispatcherServlet
的bean -
DispatcherServletRegistrationBean
:依赖DispatcherServlet
,必须等DispatcherServlet
注入beanFactory后才能创建,实现了ServletContextInitializer
接口,很重要,就是靠这个bean将DispatcherServlet
这个bean加入serlvet容器中 -
DispatcherServlet
:不用解释,SpringMVC的核心中的核心
一、onRefresh
debug进入org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext#onRefresh
protected void onRefresh() {
//调用父类的方法,没什么重要的内容我们就不看了
super.onRefresh();
try {
// 看名字我们就知道,这里是重点,创建Web服务
createWebServer();
}
catch (Throwable ex) {
throw new ApplicationContextException("Unable to start web server", ex);
}
}
debug进入createWebServer
方法
private void createWebServer() {
// 获取Web服务器,第一次启动,为null
WebServer webServer = this.webServer;
// 获取Servlet上下文,第一次启动,为null
ServletContext servletContext = getServletContext();
if (webServer == null && servletContext == null) {
// 第一次启动,上面两个都为null,所以进到这个逻辑里,
// 记录时间
StartupStep createWebServer = this.getApplicationStartup().start("spring.boot.webserver.create");
// 获取WebServer工厂,重点见标题三
ServletWebServerFactory factory = getWebServerFactory();
createWebServer.tag("factory", factory.getClass().toString());
// 通过WebServer工厂,获取WebServer,重点见标题四,
// getSelfInitializer()方法获取一个实现了ServletContextInitializer接口的对象,也是我们一会要看的重点,见标题二,
// 之所以把这个getSelfInitializer方法的源码解读放在标题二,是因为这个方法返回的对象实现了ServletContextInitializer接口,
// 是通过lambda表达式创建的,后续Tomcat启动会回调ServletContainerInitializer这个接口的所有实现类,
// 其中有一个实现了TomcatStarter,会获取所有的ServletContainerInitializer接口实现类,然后回调执行
// 后面又会用到这个对象,会重新回到这个方法调用,为了防止混乱我们先放在前面认识一下,至少有一个映像
this.webServer = factory.getWebServer(getSelfInitializer());
createWebServer.end();
// 向bean工厂中注册两个bean,和WebServer生命周期有关,我们这里不关注
getBeanFactory().registerSingleton("webServerGracefulShutdown",
new WebServerGracefulShutdownLifecycle(this.webServer));
getBeanFactory().registerSingleton("webServerStartStop",
new WebServerStartStopLifecycle(this, this.webServer));
}
else if (servletContext != null) {
try {
getSelfInitializer().onStartup(servletContext);
}
catch (ServletException ex) {
throw new ApplicationContextException("Cannot initialize servlet context", ex);
}
}
initPropertySources();
}
二、getSelfInitializer()
debug进入getSelfInitializer()
方法
// 通过lambda表达式,创建一个org.springframework.boot.web.servlet.ServletContextInitializer对象,里面方法onStartup的实现就是,调用当前方法的selfInitialize()
private org.springframework.boot.web.servlet.ServletContextInitializer getSelfInitializer() {
return this::selfInitialize;
}
我们进入selfInitialize方法看看
private void selfInitialize(ServletContext servletContext) throws ServletException {
prepareWebApplicationContext(servletContext);
registerApplicationScope(servletContext);
WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(), servletContext);
// 核心逻辑就在这里,获取所有的ServletContextInitializer接口的实现类,然后遍历执行onStartup方法,
// 文章开篇提到的DispatcherServletRegistrationBean,就是一个ServletContextInitializer,在执行回调时会把DispatchServlet加入ServletContext中
for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
beans.onStartup(servletContext);
}
}
debug进入getServletContextInitializerBeans
protected Collection<ServletContextInitializer> getServletContextInitializerBeans() {
创建ServletContextInitializerBeans对象,本质上是一个集合对象,把当前bean工厂也传进去
return new ServletContextInitializerBeans(getBeanFactory());
}
debug进入ServletContextInitializerBeans方法
public ServletContextInitializerBeans(ListableBeanFactory beanFactory,
Class<? extends ServletContextInitializer>... initializerTypes) {
this.initializers = new LinkedMultiValueMap<>();
// 我们没有传initializerTypes,这里就是ServletContextInitializer
this.initializerTypes = (initializerTypes.length != 0) ? Arrays.asList(initializerTypes)
: Collections.singletonList(ServletContextInitializer.class);
// 核心逻辑,debug进入
addServletContextInitializerBeans(beanFactory);
addAdaptableBeans(beanFactory);
List<ServletContextInitializer> sortedInitializers = this.initializers.values().stream()
.flatMap((value) -> value.stream().sorted(AnnotationAwareOrderComparator.INSTANCE))
.collect(Collectors.toList());
this.sortedList = Collections.unmodifiableList(sortedInitializers);
logMappings(this.initializers);
}
我们在这里面就可以看到,会将各种类型ServletContextInitializer,分类添加到initializers
属性中,最终存储类型有
Servlet,Filter,EventListener,ServletContextInitializer
private void addServletContextInitializerBean(String beanName, ServletContextInitializer initializer,ListableBeanFactory beanFactory) {
if (initializer instanceof ServletRegistrationBean) {
Servlet source = ((ServletRegistrationBean<?>) initializer).getServlet();
addServletContextInitializerBean(Servlet.class, beanName, initializer, beanFactory, source);
}
else if (initializer instanceof FilterRegistrationBean) {
Filter source = ((FilterRegistrationBean<?>) initializer).getFilter();
addServletContextInitializerBean(Filter.class, beanName, initializer, beanFactory, source);
}
else if (initializer instanceof DelegatingFilterProxyRegistrationBean) {
String source = ((DelegatingFilterProxyRegistrationBean) initializer).getTargetBeanName();
addServletContextInitializerBean(Filter.class, beanName, initializer, beanFactory, source);
}
else if (initializer instanceof ServletListenerRegistrationBean) {
EventListener source = ((ServletListenerRegistrationBean<?>) initializer).getListener();
addServletContextInitializerBean(EventListener.class, beanName, initializer, beanFactory, source);
}
else {
addServletContextInitializerBean(ServletContextInitializer.class, beanName, initializer, beanFactory,initializer);
}
}
三、获取WebServer工厂
protected ServletWebServerFactory getWebServerFactory() {
// Use bean names so that we don't consider the hierarchy
// 因为我们使用的是默认的web容器,此处会获取到一个tomcatServletWebServerFactory,就是之前自动装配导入的配置类ServletWebServerFactoryConfiguration中导入的类
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));
}
// 将web容器实例化,就是创建tomcatServletWebServerFactory类型的这个bean
// 过程很复杂,这里简单说一下,tomcatServletWebServerFactory在初始化过程中会被ErrorPageRegistrarBeanPostProcessor这个beanPostProcessor回调执行,
// ErrorPageRegistrarBeanPostProcessor 会先获取到所有实现了ErrorPageRegistrar接口的bean,其中就有ErrorPageCustomizer,
// 而创建ErrorPageCustomizer这个bean的时候,依赖DispatcherServletRegistrationBean,DispatcherServletRegistrationBean又依赖DispatcherServlet
// 所以会先创建DispatcherServlet整个bean,然后是DispatcherServletRegistrationBean,ErrorPageCustomizer,最后tomcatServletWebServerFactory初始化完成
return getBeanFactory().getBean(beanNames[0], ServletWebServerFactory.class);
}
四、通过WebServer工厂获取WebServer
public WebServer getWebServer(ServletContextInitializer... initializers) {
if (this.disableMBeanRegistry) {
Registry.disableRegistry();
}
// 这里我们朝思暮想的Tomcat出现了
Tomcat tomcat = new Tomcat();
File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat");
tomcat.setBaseDir(baseDir.getAbsolutePath());
Connector connector = new Connector(this.protocol);
connector.setThrowOnFailure(true);
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);
}
// 重点见标题五,准备web服务上下文,这里面会创建一个Tomcat上下文,然后将initializers传入
prepareContext(tomcat.getHost(), initializers);
// 重点见标题六,获取Tomcat的webServer,这里tomcat会启动
return getTomcatWebServer(tomcat);
}
五、准备web服务上下文
protected void prepareContext(Host host, ServletContextInitializer[] initializers) {
File documentRoot = getValidDocumentRoot();
// 创建一个内嵌tomcat上下文
TomcatEmbeddedContext context = new TomcatEmbeddedContext();
// 省略中间大量内嵌Tomcat的上下文相关逻辑,和我们这次要看的和Web容器整合关系不大
// 见标题5.1,将传入的ServletContextInitializer和其他ServletContextInitializer合并
ServletContextInitializer[] initializersToUse = mergeInitializers(initializers);
// 将上下文加入主机
host.addChild(context);
// 见标题5.2,配置上下文
configureContext(context, initializersToUse);
postProcessContext(context);
}
5.1、将传入的ServletContextInitializer和其他ServletContextInitializer合并
protected final ServletContextInitializer[] mergeInitializers(ServletContextInitializer... initializers) {
// 合并过程很简单,就是创建了几个新的ServletContextInitializer接口实现类,并且将所有的ServletContextInitializer接口实现类放入一个list
List<ServletContextInitializer> mergedInitializers = new ArrayList<>();
mergedInitializers.add((servletContext) -> this.initParameters.forEach(servletContext::setInitParameter));
mergedInitializers.add(new SessionConfiguringInitializer(this.session));
mergedInitializers.addAll(Arrays.asList(initializers));
mergedInitializers.addAll(this.initializers);
return mergedInitializers.toArray(new ServletContextInitializer[0]);
}
5.2、配置上下文
protected void configureContext(Context context, ServletContextInitializer[] initializers) {
// 这里创建了一个TomcatStarter,然后加入上下文
// 他实现了ServletContainerInitializer接口,这里我们不介绍这个接口,只需要知道,Tomcat启动后获取所有的实现了ServletContainerInitializer接口的类,并且执行
TomcatStarter starter = new TomcatStarter(initializers);
if (context instanceof TomcatEmbeddedContext) {
TomcatEmbeddedContext embeddedContext = (TomcatEmbeddedContext) context;
embeddedContext.setStarter(starter);
embeddedContext.setFailCtxIfServletStartFails(true);
}
context.addServletContainerInitializer(starter, NO_CLASSES);
// 省略中间大量内嵌Tomcat的上下文相关逻辑,和我们这次要看的和Web容器整合关系不大
}
这里我们只是看一下TomcatStarter内部的实现,非常简单
import javax.servlet.ServletContainerInitializer;
import javax.servlet.ServletContext;
import org.springframework.boot.web.servlet.ServletContextInitializer;
class TomcatStarter implements ServletContainerInitializer {
// 所有的ServletContextInitializer实现
private final ServletContextInitializer[] initializers;
TomcatStarter(ServletContextInitializer[] initializers) {
this.initializers = initializers;
}
@Override
public void onStartup(Set<Class<?>> classes, ServletContext servletContext) throws ServletException {
try {
// 遍历ServletContextInitializer接口实现类,回调执行,其中就有我们本文中标题二的内容
for (ServletContextInitializer initializer : this.initializers) {
initializer.onStartup(servletContext);
}
}
catch (Exception ex) {
// 省略部分不重要的异常处理逻辑
}
}
}
六、获取Tomcat的webServer
protected TomcatWebServer getTomcatWebServer(Tomcat tomcat) {
// 创建一个tomcat服务
return new TomcatWebServer(tomcat, getPort() >= 0, getShutdown());
}
进入TomcatWebServer
public TomcatWebServer(Tomcat tomcat, boolean autoStart, Shutdown shutdown) {
Assert.notNull(tomcat, "Tomcat Server must not be null");
this.tomcat = tomcat;
this.autoStart = autoStart;
this.gracefulShutdown = (shutdown == Shutdown.GRACEFUL) ? new GracefulShutdown(tomcat) : null;
// 初始化方法,tomcat启动就在这里
initialize();
}
debug进入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
// tomcat启动
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);
}
}
}
七、总流程