springboot整合springmvc源码分析(3)--直击内容
通过第一章的猜想和第二章的否认,我们这章直接来分析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的来龙去脉啦


浙公网安备 33010602011771号