javascript
Spring MVC 实现原理
為什么80%的碼農都做不了架構師?>>> ??
<context-param><param-name>contextConfigLocation</param-name><param-value>classpath:spring*.xml</param-value> </context-param> <listener><listener>org.springframework.web.context.ContextLoaderListener</listener> </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>從這個配置里面看見,一個配置上下文的監聽器,這個監聽器負責整個 IOC 容器 在 Web 環境中的啟動工作,另外一個是配置 SpringMVC 的分發請求器。他們共同構成了 Spring 和 Web 容器的接口操作,他們與 Web容器的 耦合 是通過ServletContext 初始化的,而DispatcherServlet就負責 Web 請求轉發的建立,完成 http 的請求響應。
?
在這個啟動過程中可以看見系統加載是依賴 web 容器的 ?ServletContextListener 來觸發的,這個Listener的觸發會在如下時候:
當 Servlet 容器啟動或終止 Web應用時,會觸發 ServletContextEvent 事件,該事件由ServletContextListener 來處理。在 ServletContextListener 接口定義了處理 ServletContextEvent 事件的兩個方法。
在 Spring 會在 Web容器啟動的過程中,借助這個監聽器來創建它的上下文,而這個上下文其實就是 IOC 容器的初始化過程。而這個上下文初始化的過程同時也會把這個 ServletContext 給設置上,以供后面的 WebApplicationContext 來獲取 Web容器級別的全局屬性。
先看下這個創建的 XmlWebApplicationContext 上下文的類繼承關系:
?
它的 refresh 過程其實是在 AbstractApplicationContext 完成的,如下代碼:
@Overridepublic 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) {logger.warn("Exception encountered during context initialization - cancelling refresh attempt", ex);// Destroy already created singletons to avoid dangling resources.destroyBeans();// Reset 'active' flag.cancelRefresh(ex);// Propagate exception to caller.throw ex;}}}這個就是之前 spring ioc 中已經講到過的容器啟動過程,在 XmlWebApplicationContext它重寫了 beanDefinition 的加載,如下代碼
/*** Loads the bean definitions via an XmlBeanDefinitionReader.* @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader* @see #initBeanDefinitionReader* @see #loadBeanDefinitions*/@Overrideprotected 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(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/";/** Default suffix for building a config location for a namespace */public static final String DEFAULT_CONFIG_LOCATION_SUFFIX = ".xml";這三個靜態的定義就是專門為 web 環境來設置的,相對于配置資源的獲取從以下這段重寫的代碼可以看出:
/*** The default location for the root context is "/WEB-INF/applicationContext.xml",* and "/WEB-INF/test-servlet.xml" for a context with the namespace "test-servlet"* (like for a DispatcherServlet instance with the servlet-name "test").*/@Overrideprotected 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 創建過程,這個 createWebApplcationContext 的如下實現:
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);}在創建 WebApplicationContext 的時候這里面有一個選擇上下文 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 默認上下文就是 XmlApplicationContext ,驗證了對這個類的分析。
轉載于:https://my.oschina.net/exit/blog/812688
總結
以上是生活随笔為你收集整理的Spring MVC 实现原理的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 微信JS-SDK之图像接口开发详解
- 下一篇: callable object与新增的f