分類  >  Web前端 >

spring mvc 源碼(1)web容器創建

tags:    時間:2013-12-09 23:19:34
spring mvc 源碼(一)web容器創建

spring mvc是一個mvc開源框架,由於與spring是父子關係,所以無縫兼容

 

spring mvc入口:

 

    <!-- spring MVC -->     <servlet>         <servlet-name>spring-mvc</servlet-name>         <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>         <init-param>             <param-name>contextConfigLocation</param-name>             <param-value>classpath*:/spring-mvc-servlet.xml</param-value>         </init-param>         <load-on-startup>1</load-on-startup>     </servlet>

 spring mvc在web.xml中的配置如上,這是一個標準的servlet的配置,入口為:DispatcherServlet;

 

DispatcherServlet源碼為:

 

public class DispatcherServlet extends FrameworkServlet { //省略實現代碼... }  public abstract class FrameworkServlet extends HttpServletBean { //省略實現代碼...s }  public abstract class HttpServletBean extends HttpServlet 		implements EnvironmentCapable, EnvironmentAware { //省略實現代碼 }

 由上面可知DispatcherServlet實現了HttpServlet介面,所以配置也使用servlet配置;

 

 

一個servlet的初始化時通過init()方法完成,那我們先從最頂層的類HttpServletBean看起,下面為HttpServletBean的init()方法的實現:

 

	@Override 	public final void init() throws ServletException { 		if (logger.isDebugEnabled()) { 			logger.debug("Initializing servlet '" + getServletName() + "'"); 		}  		// Set bean properties from init parameters. 		try { 			PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties); 			BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this); 			ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext()); 			bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment())); 			initBeanWrapper(bw); 			bw.setPropertyValues(pvs, true); 		} 		catch (BeansException ex) { 			logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex); 			throw ex; 		}  		// Let subclasses do whatever initialization they like.                 //留給子類進行擴展 		initServletBean();  		if (logger.isDebugEnabled()) { 			logger.debug("Servlet '" + getServletName() + "' configured successfully"); 		} 	}  

 由上面的代碼可知,方法前半部分做了一些初始化配置,initServletBean()被FrameworkServlet重寫,下面是FrameworkServlet中initServletBean()的實現:

 

	@Override 	protected final void initServletBean() throws ServletException { 		getServletContext().log("Initializing Spring FrameworkServlet '" + getServletName() + "'"); 		if (this.logger.isInfoEnabled()) { 			this.logger.info("FrameworkServlet '" + getServletName() + "': initialization started"); 		} 		long startTime = System.currentTimeMillis();  		try {                         //創建上下文,創建容器 			this.webApplicationContext = initWebApplicationContext(); 			initFrameworkServlet(); 		} 		catch (ServletException ex) { 			this.logger.error("Context initialization failed", ex); 			throw ex; 		} 		catch (RuntimeException ex) { 			this.logger.error("Context initialization failed", ex); 			throw ex; 		}  		if (this.logger.isInfoEnabled()) { 			long elapsedTime = System.currentTimeMillis() - startTime; 			this.logger.info("FrameworkServlet '" + getServletName() + "': initialization completed in " + 					elapsedTime + " ms"); 		} 	}          //被調用方法實現,創建spring mvc容器的實現 	protected WebApplicationContext initWebApplicationContext() {                 //獲取父容器,即spring的容器 		WebApplicationContext rootContext = 				WebApplicationContextUtils.getWebApplicationContext(getServletContext()); 		WebApplicationContext wac = null;  		if (this.webApplicationContext != null) { 			// A context instance was injected at construction time -> use it 			wac = this.webApplicationContext; 			if (wac instanceof ConfigurableWebApplicationContext) { 				ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac; 				if (!cwac.isActive()) { 					// The context has not yet been refreshed -> provide services such as 					// setting the parent context, setting the application context id, etc 					if (cwac.getParent() == null) { 						// The context instance was injected without an explicit parent -> set 						// the root application context (if any; may be null) as the parent                                                 //設置父容器,父容器為spring的容器 						cwac.setParent(rootContext); 					} 					configureAndRefreshWebApplicationContext(cwac); 				} 			} 		} 		if (wac == null) { 			// No context instance was injected at construction time -> see if one 			// has been registered in the servlet context. If one exists, it is assumed 			// that the parent context (if any) has already been set and that the 			// user has performed any initialization such as setting the context id 			wac = findWebApplicationContext(); 		} 		if (wac == null) { 			// No context instance is defined for this servlet -> create a local one 			wac = createWebApplicationContext(rootContext); 		}  		if (!this.refreshEventReceived) { 			// Either the context is not a ConfigurableApplicationContext with refresh 			// support or the context injected at construction time had already been 			// refreshed -> trigger initial onRefresh manually here. 			onRefresh(wac); 		}  		if (this.publishContext) { 			// Publish the context as a servlet context attribute. 			String attrName = getServletContextAttributeName(); 			getServletContext().setAttribute(attrName, wac); 			if (this.logger.isDebugEnabled()) { 				this.logger.debug("Published WebApplicationContext of servlet '" + getServletName() + 						"' as ServletContext attribute with name [" + attrName + "]"); 			} 		}  		return wac; 	}

有上面代碼可知:spring mvc上下文和spring 上下文的關係,spring mvc容器為子容器, spring的容器為父容器,所以spring mvc里在配置controller時,可以直接引用在spring中配置的service類;所以在web.xml配置中,要先配置spring的配置,然後配置spring mvc的配置,否則spring mvc的子容器無法使用父容器中的資源

 

在FrameworkServlet類中onRefresh()方法是一個空方法,並沒有具體實現,而是被子類重寫了,在子類DispatcherServlet中onRefresh()方法實現如下:

 

	@Override 	protected void onRefresh(ApplicationContext context) { 		initStrategies(context); 	}  	/** 	 * Initialize the strategy objects that this servlet uses. 	 * <p>May be overridden in subclasses in order to initialize further strategy objects. 	 */ 	protected void initStrategies(ApplicationContext context) { 		initMultipartResolver(context); 		initLocaleResolver(context); 		initThemeResolver(context); 		initHandlerMappings(context); 		initHandlerAdapters(context); 		initHandlerExceptionResolvers(context); 		initRequestToViewNameTranslator(context); 		initViewResolvers(context); 		initFlashMapManager(context); 	}

由上面的代碼可知在DispatcherServlet中刷新容器的時候,做了大量的初始化工作,初始化了Resolver,HandlerMapping,Adapter

 

初始化Resolver:

 

	private void initLocaleResolver(ApplicationContext context) { 		try {                         //先從上下文里查找   			this.localeResolver = context.getBean(LOCALE_RESOLVER_BEAN_NAME, LocaleResolver.class); 			if (logger.isDebugEnabled()) { 				logger.debug("Using LocaleResolver [" + this.localeResolver + "]"); 			} 		} 		catch (NoSuchBeanDefinitionException ex) { 			// We need to use the default.                         //上下文里沒有則使用默認的 			this.localeResolver = getDefaultStrategy(context, LocaleResolver.class); 			if (logger.isDebugEnabled()) { 				logger.debug("Unable to locate LocaleResolver with name '" + LOCALE_RESOLVER_BEAN_NAME + 						"': using default [" + this.localeResolver + "]"); 			} 		} 	}

 關於默認的resolver:在DispatcherServlet剛開始有一段靜態初始化的代碼:

 

 

	static { 		// Load default strategy implementations from properties file. 		// This is currently strictly internal and not meant to be customized 		// by application developers. 		try { 			ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, DispatcherServlet.class); 			defaultStrategies = PropertiesLoaderUtils.loadProperties(resource); 		} 		catch (IOException ex) { 			throw new IllegalStateException("Could not load 'DispatcherServlet.properties': " + ex.getMessage()); 		} 	}

 默認載入一個配置文件:與DispatcherServlet在同一個路徑下DEFAULT_STRATEGIES_PATH;即DispatcherServlet.properties

# Default implementation classes for DispatcherServlet's strategy interfaces. # Used as fallback when no matching beans are found in the DispatcherServlet context. # Not meant to be customized by application developers.  org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver  org.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolver  org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\ 	org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping  org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\ 	org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\ 	org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter  org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerExceptionResolver,\ 	org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\ 	org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver  org.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator  org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver  org.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager

 配置文件中配置了默認的Resolver, HandlerMapping, HandlerAdapter,  onRefresh()方法中要初始化的組件都有一個默認的配置。

 

上面的組件初始化基本都遵從了一個主要邏輯:先從容器或者資源里找,找不到就使用配置文件中默認配置的。

 

通過上面的代碼spring mvc的初始化工作就完成了,上下文容器也就建立了!

 

 

推薦閱讀文章

Bookmark the permalink ,來源:互聯網