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的來龍去脈啦

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章