spring MVC的實現原理
阿新 • • 發佈:2019-01-09
1、典型配置
Spring MVC的一個典型配置如下:
從這個配置裡面可以看到,一個是配置上下文的監聽器,這個監聽器負責整個ioc容器在web環境中的啟動工作;另外一個就是配置spring MVC的一個分發請求器。他們共同構成了spring和web容器的介面操作,他們與web容器的耦合是通過ServletContext來實現的。而ServletContext就為spring的IOC容器提供了一個宿主環境,這個宿主環境裡面的ioc容器是通過.ContextLoader初始化的,而DispatcherServlet就負責web請求轉發的建立,完成http的請求響應。<context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring-context.xml</param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <servlet> <servlet-name>test</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath*:config/test-servlet.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>test</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping>
2、上下文的啟動及初始化過程
在這個啟動過程中看到系統載入是依賴web容器的servletContextListener來觸發的,這個listener的觸發會在如下時候:
當Servlet 容器啟動或終止Web 應用時,會觸發ServletContextEvent 事件,該事件由 ServletContextListener 來處理。在 ServletContextListener 介面中定義了處理ServletContextEvent 事件的兩個方法。
1) contextInitialized(ServletContextEvent sce) :當Servlet 容器啟動Web 應用時呼叫該方法。在呼叫完該方法之後,容器再對Filter 初始化,並且對那些在Web 應用啟動時就需要被初始化的Servlet 進行初始化。
2)contextDestroyed(ServletContextEvent sce) :當Servlet 容器終止Web 應用時呼叫該方法。在呼叫該方法之前,容器會先銷燬所有的Servlet 和Filter 過濾器。
所以spring會在web容器啟動的時候藉助這個監聽器來建立它的上下文,而這個上下文的建立其實就是一個ico容器的初始化過程。而這個上下文初始化的過程同時也會把這個ServletContext給設定上,以供後面的WebApplicationContext來獲取web容器級別的全域性屬性。
先看下這個建立的XmlWebApplicationContext上下文的類繼承關係:
它的refresh過程其實是在AbstractApplicationContext完成的,如下程式碼:
這個就是之前spring ioc中已經講到過的容器啟動過程,在XmlWebApplicationContext它重寫了beanDefinition的載入,如下程式碼:public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { // Prepare this context for refreshing. prepareRefresh(); // Tell the subclass to refresh the internal bean factory. ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // Prepare the bean factory for use in this context. prepareBeanFactory(beanFactory); try { // Allows post-processing of the bean factory in context subclasses. postProcessBeanFactory(beanFactory); // Invoke factory processors registered as beans in the context. invokeBeanFactoryPostProcessors(beanFactory); // Register bean processors that intercept bean creation. registerBeanPostProcessors(beanFactory); // Initialize message source for this context. initMessageSource(); // Initialize event multicaster for this context. initApplicationEventMulticaster(); // Initialize other special beans in specific context subclasses. onRefresh(); // Check for listener beans and register them. registerListeners(); // Instantiate all remaining (non-lazy-init) singletons. finishBeanFactoryInitialization(beanFactory); // Last step: publish corresponding event. finishRefresh(); } catch (BeansException ex) { // Destroy already created singletons to avoid dangling resources. destroyBeans(); // Reset 'active' flag. cancelRefresh(ex); // Propagate exception to caller. throw ex; } } }
@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
// Create a new XmlBeanDefinitionReader for the given BeanFactory.
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
// Configure the bean definition reader with this context's
// resource loading environment.
beanDefinitionReader.setEnvironment(this.getEnvironment());
beanDefinitionReader.setResourceLoader(this);
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
// Allow a subclass to provide custom initialization of the reader,
// then proceed with actually loading the bean definitions.
initBeanDefinitionReader(beanDefinitionReader);
loadBeanDefinitions(beanDefinitionReader);
}
對於這個實現類它主要添加了對web環境和xml配置類的定義處理,例如:/** Default config location for the root context */
public static final String DEFAULT_CONFIG_LOCATION = "/WEB-INF/applicationContext.xml";
/** Default prefix for building a config location for a namespace */
public static final String DEFAULT_CONFIG_LOCATION_PREFIX = "/WEB-INF/";
這兩個靜態變數的定義就是專門為web環境來設定的,相對於配置資源的獲取從以下這段重寫的程式碼也可以看出來。
@Override
protected String[] getDefaultConfigLocations() {
if (getNamespace() != null) {
return new String[] {DEFAULT_CONFIG_LOCATION_PREFIX + getNamespace() + DEFAULT_CONFIG_LOCATION_SUFFIX};
}
else {
return new String[] {DEFAULT_CONFIG_LOCATION};
}
}
在上面時序圖中有一個initWebApplicationContext的過程,這個過程裡面有如下一段程式碼可以簡單的說明一下:
if (this.context == null) {
this.context = createWebApplicationContext(servletContext);
}
也就是context的建立過程,這個createWebApplicationContext的實現如下:
protected WebApplicationContext createWebApplicationContext(ServletContext sc) {
Class<?> contextClass = determineContextClass(sc);
if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
throw new ApplicationContextException("Custom context class [" + contextClass.getName() +
"] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]");
}
return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
}
在建立webApplicationXontext的時候這裡面有一個選擇上下文class的方法:
protected Class<?> determineContextClass(ServletContext servletContext) {
String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM);
if (contextClassName != null) {
try {
return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader());
}
catch (ClassNotFoundException ex) {
throw new ApplicationContextException(
"Failed to load custom context class [" + contextClassName + "]", ex);
}
}
else {
contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName());
try {
return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader());
}
catch (ClassNotFoundException ex) {
throw new ApplicationContextException(
"Failed to load default context class [" + contextClassName + "]", ex);
}
}
}
首先會判斷在web容器的上下文全域性屬性裡面有沒有做設定,如果做了設定就使用這個類。如果沒有設定就從一個預設的策略properties裡面去獲取這個配置。這個配置的預設檔案內容如下:# Default WebApplicationContext implementation class for ContextLoader.
# Used as fallback when no explicit context implementation has been specified as context-param.
# Not meant to be customized by application developers.
org.springframework.web.context.WebApplicationContext=org.springframework.web.context.support.XmlWebApplicationContext
也就是說spring的mvc預設上下文就是XmlWebApplicationContext ,驗證了我們上面對這個類的分析。