SpringMVC源碼解析(1)-啓動過程

xml方式

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0">
    <display-name>CtrTimeOut</display-name>
    <welcome-file-list>
        <welcome-file>index.jsp</welcome-file>
    </welcome-file-list>
    <context-param>
        <param-name>contextConfigLocation</param-name>
      	# spring的配置
        <param-value>classpath:config/spring/applicationContext.xml</param-value>
    </context-param>
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    <servlet>
        <servlet-name>controller</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
          	# springmvc的配置
            <param-value>classpath:config/spring/spring-controller.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>controller</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
</web-app>
複製代碼

能夠看到xml配置方式藉助了了ContextLoaderListener來啓動php

看下ContextLoaderListener的類定義java

public class ContextLoaderListener extends ContextLoader implements ServletContextListener 複製代碼

ContextLoaderListener實現了ServletContextListenerweb

public interface ServletContextListener extends EventListener {
  //容器初始化完成
    public void contextInitialized(ServletContextEvent sce);
  //容器中止
    public void contextDestroyed(ServletContextEvent sce);
}
複製代碼

servlet標準可知ServletContextListener是容器的生命週期方法,springmvc就藉助其啓動與中止spring

ContextLoadListener調用了initWebApplicationContext方法,建立WebApplicationContext做爲spring的容器上下文spring-mvc

#org.springframework.web.context.ContextLoader
/**
 * 根據xml配置建立applicationContext
 */
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
	...
	try {
		// Store context in local instance variable, to guarantee that
		// it is available on ServletContext shutdown.
		if (this.context == null) {//判空 (以註解方式配置時非空)
			this.context = createWebApplicationContext(servletContext);
		}
		if (this.context instanceof ConfigurableWebApplicationContext) {
			ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
			if (!cwac.isActive()) {
				...
				//讀取contextConfigLocation配置並refresh()
				configureAndRefreshWebApplicationContext(cwac, servletContext);
			}
		}
        //將applicationContext設置到servletContext中
   	servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
		...
		return this.context;
	}
}
複製代碼

而DispatcherServlet建立WebApplicationContext做爲springmvc的上下文 並將ContextLoadListener建立的上下文設置爲自身的parentbash

DispatcherServlet extends FrameworkServlet 
#org.springframework.web.servlet.FrameworkServlet
	@Override
	protected final void initServletBean() throws ServletException {
		...
		try {
			this.webApplicationContext = initWebApplicationContext();
			initFrameworkServlet();
		}
		...
	}
	protected WebApplicationContext initWebApplicationContext() {
		WebApplicationContext rootContext =
				WebApplicationContextUtils.getWebApplicationContext(getServletContext());
		WebApplicationContext wac = null;
		...
		if (wac == null) {
			//建立applicationContext
			wac = createWebApplicationContext(rootContext);
		}
		...
		return wac;
	}
	protected WebApplicationContext createWebApplicationContext(ApplicationContext parent) {
		//XmlWebApplicationContext 
		Class<?> contextClass = getContextClass();
		...
		//建立applicationContext
		ConfigurableWebApplicationContext wac =
				(ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
		wac.setEnvironment(getEnvironment());
		//設置parent(ContextLoadListener中建立的applicationContext)
		wac.setParent(parent);
		//讀取contextConfigLocation配置
		wac.setConfigLocation(getContextConfigLocation());
		//refresh()
		configureAndRefreshWebApplicationContext(wac);
		return wac;
	}
複製代碼

springmvc的applicationContext會去讀取配置文件 咱們來看一個最簡單的配置文件mvc

<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd" default-autowire="byName">
   #springmvc容器掃描路徑
   <context:component-scan base-package="com.iflytek.ossp.ctrtimeout.controller"></context:component-scan>
   #spring4新增的標籤 主要是添加了默認的HandleMappin,ViewResolver,HandleAdapter
   <mvc:annotation-driven />
</beans>
複製代碼

springmvc標籤解析

根據spring的自定義schema解析機制 咱們找到 在下圖位置app

http\://www.springframework.org/schema/mvc=org.springframework.web.servlet.config.MvcNamespaceHandler
複製代碼

mvc標籤解析定義

能夠看到mvc全部的標籤解析器都定義在此jsp

public class MvcNamespaceHandler extends NamespaceHandlerSupport {
	@Override
	public void init() {
		registerBeanDefinitionParser("annotation-driven", new AnnotationDrivenBeanDefinitionParser());
		registerBeanDefinitionParser("default-servlet-handler", new DefaultServletHandlerBeanDefinitionParser());
		registerBeanDefinitionParser("interceptors", new InterceptorsBeanDefinitionParser());
		registerBeanDefinitionParser("resources", new ResourcesBeanDefinitionParser());
		registerBeanDefinitionParser("view-controller", new ViewControllerBeanDefinitionParser());
		registerBeanDefinitionParser("redirect-view-controller", new ViewControllerBeanDefinitionParser());
		registerBeanDefinitionParser("status-controller", new ViewControllerBeanDefinitionParser());
		registerBeanDefinitionParser("view-resolvers", new ViewResolversBeanDefinitionParser());
		registerBeanDefinitionParser("tiles-configurer", new TilesConfigurerBeanDefinitionParser());
		registerBeanDefinitionParser("freemarker-configurer", new FreeMarkerConfigurerBeanDefinitionParser());
		registerBeanDefinitionParser("velocity-configurer", new VelocityConfigurerBeanDefinitionParser());
		registerBeanDefinitionParser("groovy-configurer", new GroovyMarkupConfigurerBeanDefinitionParser());
	}
}

複製代碼

來看一下AnnotationDrivenBeanDefinitionParser解析器作了什麼ide

解析過程較爲複雜 經過註釋咱們能夠得知如下對象將被裝載

annotation-driven

java方式

public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer{
	//加載spring配置 建立spring上下文
    @Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class<?>[] {RootConfig.class};
    }
    //加載springMvc配置 建立springMVC上下文
    @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class<?>[] {WebConfig.class}; // 指定配置類
    }
    //DiapatchServlet映射路徑
    @Override
    protected String[] getServletMappings() {
        return new String[] {"/"}; // 將dispatcherServlet映射到「/」
    }
}
複製代碼

能夠看到,同xml方式相同,java方式啓動也有兩個上下文

WebAppInitializer繼承關係

同xml方式相似,java配置啓動方式也須要藉助於servlet容器的生命週期方法,就是WebApplicationInitializer接口

public interface WebApplicationInitializer {
	//容器啓動時被調用
	void onStartup(ServletContext servletContext) throws ServletException;

}
複製代碼

: WebApplicationInitializer是spring定義的接口,它可以響應容器生命週期的緣由是由於SpringServletContainerInitializer

@HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer implements ServletContainerInitializer{
	//獲得全部WebApplicationInitializer實現 並調用其onStartup()方法
  	@Override
	public void onStartup(Set<Class<?>> webAppInitializerClasses, ServletContext servletContext) throws ServletException {
		List<WebApplicationInitializer> initializers = new LinkedList<WebApplicationInitializer>();
		...
        for (Class<?> waiClass : webAppInitializerClasses) {
        ...
        initializers.add((WebApplicationInitializer) waiClass.newInstance());
        ...	
        }
		...
		AnnotationAwareOrderComparator.sort(initializers);
		for (WebApplicationInitializer initializer : initializers) {
			initializer.onStartup(servletContext);
		}
	}
}

//真正的容器生命週期方法 
//會根據@HandlesTypes註解獲取全部類實現,並做爲onStartup()方法的第一個參數
public interface ServletContainerInitializer {
    public void onStartup(Set<Class<?>> c, ServletContext ctx) throws ServletException; 
}
複製代碼

啓動過程:

  1. AbstractContextLoaderInitializer調用createRootApplicationContext建立spring上下文
  2. AbstractDispatcherServletInitializer調用createServletApplicationContext建立springMVC上下文

通過以上兩步spring容器就啓動起來了

再看xml方式經過mvc標籤來添加默認實現(HandlerMapping,HandlerAdapter等等)

java方式則經過@EnableWebMvc註解來添加默認實現

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(DelegatingWebMvcConfiguration.class)
public @interface EnableWebMvc {
}

@Configuration
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {

	private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite();


	@Autowired(required = false)
	public void setConfigurers(List<WebMvcConfigurer> configurers) {
		if (!CollectionUtils.isEmpty(configurers)) {
			this.configurers.addWebMvcConfigurers(configurers);
		}
	}


	@Override
	protected void configurePathMatch(PathMatchConfigurer configurer) {
		this.configurers.configurePathMatch(configurer);
	}

	@Override
	protected void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
		this.configurers.configureContentNegotiation(configurer);
	}

	@Override
	protected void configureAsyncSupport(AsyncSupportConfigurer configurer) {
		this.configurers.configureAsyncSupport(configurer);
	}

	@Override
	protected void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
		this.configurers.configureDefaultServletHandling(configurer);
	}

	@Override
	protected void addFormatters(FormatterRegistry registry) {
		this.configurers.addFormatters(registry);
	}

	@Override
	protected void addInterceptors(InterceptorRegistry registry) {
		this.configurers.addInterceptors(registry);
	}

	@Override
	protected void addResourceHandlers(ResourceHandlerRegistry registry) {
		this.configurers.addResourceHandlers(registry);
	}

	@Override
	protected void addCorsMappings(CorsRegistry registry) {
		this.configurers.addCorsMappings(registry);
	}

	@Override
	protected void addViewControllers(ViewControllerRegistry registry) {
		this.configurers.addViewControllers(registry);
	}

	@Override
	protected void configureViewResolvers(ViewResolverRegistry registry) {
		this.configurers.configureViewResolvers(registry);
	}

	@Override
	protected void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
		this.configurers.addArgumentResolvers(argumentResolvers);
	}

	@Override
	protected void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> returnValueHandlers) {
		this.configurers.addReturnValueHandlers(returnValueHandlers);
	}

	@Override
	protected void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
		this.configurers.configureMessageConverters(converters);
	}

	@Override
	protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
		this.configurers.extendMessageConverters(converters);
	}

	@Override
	protected void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) {
		this.configurers.configureHandlerExceptionResolvers(exceptionResolvers);
	}

	@Override
	protected void extendHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) {
		this.configurers.extendHandlerExceptionResolvers(exceptionResolvers);
	}

	@Override
	protected Validator getValidator() {
		return this.configurers.getValidator();
	}

	@Override
	protected MessageCodesResolver getMessageCodesResolver() {
		return this.configurers.getMessageCodesResolver();
	}

}
複製代碼

@IMPORT是spring4的註解 用於關聯@Configuration類

DelegatingWebMvcConfiguration:像它的名字同樣只是個delegate,起做用的主要是其父類WebMvcConfigurationSupport.

WebMvcConfigurationSupport:其中註冊了大量的默認實現(如同AnnotationDrivenBeanDefinitionParser同樣),同時它的持有一個WebMvcConfigurerComposite對象.

WebMvcConfigurerComposite:內部聚合了WebMvcConfigurerAdapter的集合.

WebMvcConfigurerAdapter:用於添加自定義HandlerAdapter,HandlerMapping等

springMVC的啓動過程就是這樣了..

相關文章
相關標籤/搜索