通過第一章的猜想和第二章的否認,我們這章直接來分析springboot整合springmvc核心源碼
由之前的揭密springboot自動裝配(2)--AutoConfigurationImportSelector 系列文章中,我們已經大概知道springboot的套路的,那麼我們就直接從META-INF/spring.factories這個文件開始
打開文件找到org.springframework.boot.autoconfigure.EnableAutoConfiguration爲key的配置類名,你就會發現有許多關於web.servlet相關的配置類
從這些配置類名來看,我們應該可以猜到DispatcherServletAutoConfiguration就是我們要找的springmvc中央控制器DispatcherServlet相關的東西,八九不離十,那我們就打開瞧一瞧:
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass(DispatcherServlet.class)
@AutoConfigureAfter(ServletWebServerFactoryAutoConfiguration.class)
public class DispatcherServletAutoConfiguration {
/*
* The bean name for a DispatcherServlet that will be mapped to the root URL "/"
*/
public static final String DEFAULT_DISPATCHER_SERVLET_BEAN_NAME = "dispatcherServlet";
/*
* The bean name for a ServletRegistrationBean for the DispatcherServlet "/"
*/
public static final String DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME = "dispatcherServletRegistration";
@Configuration(proxyBeanMethods = false)
@Conditional(DefaultDispatcherServletCondition.class)
@ConditionalOnClass(ServletRegistration.class)
@EnableConfigurationProperties({ HttpProperties.class, WebMvcProperties.class })
protected static class DispatcherServletConfiguration {
@Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
public DispatcherServlet dispatcherServlet(HttpProperties httpProperties, WebMvcProperties webMvcProperties) {
DispatcherServlet dispatcherServlet = new DispatcherServlet();
dispatcherServlet.setDispatchOptionsRequest(webMvcProperties.isDispatchOptionsRequest());
dispatcherServlet.setDispatchTraceRequest(webMvcProperties.isDispatchTraceRequest());
dispatcherServlet.setThrowExceptionIfNoHandlerFound(webMvcProperties.isThrowExceptionIfNoHandlerFound());
dispatcherServlet.setPublishEvents(webMvcProperties.isPublishRequestHandledEvents());
dispatcherServlet.setEnableLoggingRequestDetails(httpProperties.isLogRequestDetails());
return dispatcherServlet;
}
@Bean
@ConditionalOnBean(MultipartResolver.class)
@ConditionalOnMissingBean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME)
public MultipartResolver multipartResolver(MultipartResolver resolver) {
// Detect if the user has created a MultipartResolver but named it incorrectly
return resolver;
}
}
@Configuration(proxyBeanMethods = false)
@Conditional(DispatcherServletRegistrationCondition.class)
@ConditionalOnClass(ServletRegistration.class)
@EnableConfigurationProperties(WebMvcProperties.class)
@Import(DispatcherServletConfiguration.class)
protected static class DispatcherServletRegistrationConfiguration {
@Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)
@ConditionalOnBean(value = DispatcherServlet.class, name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
public DispatcherServletRegistrationBean dispatcherServletRegistration(DispatcherServlet dispatcherServlet,
WebMvcProperties webMvcProperties, ObjectProvider<MultipartConfigElement> multipartConfig) {
DispatcherServletRegistrationBean registration = new DispatcherServletRegistrationBean(dispatcherServlet,
webMvcProperties.getServlet().getPath());
registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
registration.setLoadOnStartup(webMvcProperties.getServlet().getLoadOnStartup());
multipartConfig.ifAvailable(registration::setMultipartConfig);
return registration;
}
}
}
不看不知道,一看嚇一跳,果不然,這裏有我們熟悉的 DispatcherServlet的bean的生成
完事之後將DispatcherServlet注入給DispatcherServletRegistrationBean,起名爲dispatcherServlet
我們瞧下DispatcherServletRegistrationBean的繼承實現關係
通過以上方式,springboot將DispatcherServletRegistrationBean對象交由spring 容器當中管理
接着我們應該從我們啓動類開始找找springboot什麼時候創建tomcat以及將DispatcherServlet放進servlet容器當中
那就從我們熟悉的啓動類開始吧
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
我們從run方法中直接進到:AbstractApplicationContext.refresh()中去,該方法我們熟悉不過了
我們直接定位到
// Initialize other special beans in specific context subclasses.
onRefresh();
繼承AbstractApplicationContext的有如下幾個類,其中一個可以大膽猜想ServletWebServerApplicationContext是個不簡單的東西
@Override
protected void onRefresh() {
super.onRefresh();
try {
createWebServer();
}
catch (Throwable ex) {
throw new ApplicationContextException("Unable to start web server", ex);
}
}
ServletWebServerApplicationContext.createWebServer從名字上看應該就是創建一個服務了,我們的tomcat服務或許就在這裏被創建
進入getWebServerFactory()創建了一個TomcatServletWebServerFactory
然後接着調用factory.getWebServer(getSelfInitializer())方法
ServletContextInitializer是一個jdk8函數式接口的編程的玩意
這裏面就有我們的前面定義的DispatcherServletRegistrationBean,調用DispatcherServletRegistrationBean.onStartup方法
接着調用register:
然後進行addRegistration:
到這裏應該很熟悉了吧,將我們的springmvc中央控制器dispatcherServlet放入servlet容器當中
我們在回到這裏:
接着看看TomcatServletWebServerFactory.getWebServer():
這裏是不是就是在創建我們的tomcat服務器 ,這下就全都明白所以然了吧
我們接着進到TomcatServletWebServerFactory.prepareContext方法,下面是部分代碼
protected void prepareContext(Host host, ServletContextInitializer[] initializers) {
File documentRoot = getValidDocumentRoot();
TomcatEmbeddedContext context = new TomcatEmbeddedContext();
.....省略
ServletContextInitializer[] initializersToUse = mergeInitializers(initializers);
host.addChild(context);
configureContext(context, initializersToUse);
}
接着TomcatServletWebServerFactory.configureContext
這裏的javax.servlet.ServletContainerInitializer接口是不是很熟悉,好像在哪見過?我們回想一下
煥然大悟,是不是就是這個接口,前面文章中講到過在META-INF/services/javax.servlet.ServletContainerInitializer配置實現ServletContainerInitializer該接口的類將會在tomcat啓動時被調用到onStartup,而我們這裏雖然不是如第一章所說那樣通過spi調用,但是我們這裏顯示的爲他賦值一個ServletContainerInitializer的實現類TomcatStarter,這樣就完成在tomcat啓動時調用到TomcatStarter.onStartup方法,然後我們看一下圈紅位置,拿到我們傳入的面向接口編程的ServletContextInitializer,然後調用該類(DispatcherServletRegistrationBean)下的onStartup進行dispatcherServlet的注入到ServletContext中
至此,我們就完全弄明白springboot整合springmvc嵌入式tomcat的來龍去脈啦