springboot整合springmvc源码分析(3)--直击内容

  1. springboot整合springmvc源码分析(1)--前言
  2. springboot整合springmvc源码分析(2)--承上启下

通过第一章的猜想和第二章的否认,我们这章直接来分析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的来龙去脉啦

posted @ 2020-04-11 15:19  menco  阅读(31)  评论(0)    收藏  举报