Spring3.2 MVC 分析
Spring3.2 MVC 分析:
SpringMVC現在應該用得很廣泛了,其配置清晰,靈活度,定制能力等都是很強的,相比Struts2也是勝過一籌,還是從源碼來分析一下,SpringMVC為我們做了什么。
- 先從配置文件開始,看web.xml,用過SpringMVC的同學應該都很熟悉:
<web-app> <listener> <listener-class> org.springframework.web.context.ContextLoaderListener<!-- Spring根容器在這里被建立起來 --> </listener-class> </listener> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath*:configs/beans.xml</param-value> </context-param> <!-- SpringMVC子容器會在DispatherServlet初始化過程中被建立起來 --> <servlet> <servlet-name>example</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>example</servlet-name> <url-pattern>/*</url-pattern> </servlet-mapping> </web-app>
看看SpringMVC官方的請求處理流程圖,DispacherServlet就是那個FrontController, 根據用戶請求的url,匹配到我們自定義的控制器,然后進行業務處理,處理完成,將數據model返回給FrontController,然后它將數據+視圖模版(如jsp, freemarker等)進行渲染,然后返回到用戶瀏覽器:
那到底要做些什么工作,才能達到上面這樣的流程呢,往下說。
看看DispatcherServlet怎么來的:
先從HttpServletBean類入手,人類都知道Servlet被Servlet容器創建時,會調用一次其init方法,我們就看看HttpServletBean的init方法,其主要工作就是將Servlet配置參數映射到我們的Servlet Bean里面,然后再初始化子類(initServletBean):
@Override public final void init() throws ServletException { // Set bean properties from init parameters. 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); // Let subclasses do whatever initialization they like. initServletBean(); ... }接著看initServletBean方法,該方法由FrameworkServlet, 從其名稱就知道,它時Spring Web框架的基礎Servlet, Spring這樣描述它的工作:
* Manages a WebApplicationContext instance per servlet. The servlet's configuration is determined by beans in the servlet's namespace. * Publishes events on request processing, whether or not a request is successfully handled.
它如何實現initServletBean, 它做的事很單一,就是初始化WebApplicationContext對象,initFrameworkServlet是個空方法,留給子類去實現:
protected final void initServletBean() throws ServletException { ... this.webApplicationContext = initWebApplicationContext(); initFrameworkServlet(); ... }
看看FrameworkServlet怎么初始化WebApplicationContext的,其實類似于初始化IoC容器(如之前Spring Ioc文章講XmlClasspathApplicationContext):
protected WebApplicationContext initWebApplicationContext() { // 通過ServletContext對象獲取到ContextLoaderListener建立的根容器,它到底怎么被建立起來的,后面再講 WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(getServletContext()); WebApplicationContext wac = null; if (this.webApplicationContext != null) { //若通過構造參數傳遞了webApplicationContext // 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 cwac.setParent(rootContext); } configureAndRefreshWebApplicationContext(cwac); } } } if (wac == null) { // 如果配置了contextAttribute屬性,通過其來獲取WebApplicationContext對象 wac = findWebApplicationContext(); } if (wac == null) { // 這里創建FrameworkServlet的上下文實例,默認實現是XmlWebApplicationContext, 且以根上下文為父上下文 wac = createWebApplicationContext(rootContext); } //觸發refresh事件, 會調用DispatcherServlet的onRefresh方法 if (!this.refreshEventReceived) { } //默認會將FrameworkServlet對象的上下文對象存到ServletContext中 if (this.publishContext) { // Publish the context as a servlet context attribute. String attrName = getServletContextAttributeName(); getServletContext().setAttribute(attrName, wac); } return wac; }
我們可以看看createWebApplicationContext()怎么建立其XmlWebApplicationContext上下文對象的:
protected WebApplicationContext createWebApplicationContext(ApplicationContext parent) { Class<?> contextClass = getContextClass(); //沒有配置,則默認XmlWebApplicationContext.class ConfigurableWebApplicationContext wac = (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass); wac.setEnvironment(getEnvironment()); wac.setParent(parent); //將根上下文作為父上下文 wac.setConfigLocation(getContextConfigLocation());//是否再xml中配置了contextConfigLocation屬性 configureAndRefreshWebApplicationContext(wac); //如同之前文章將的Spring IoC就是初始化Ioc容器 return wac; }那XmlWebApplicationContext的初始化與普通容器如XmlClassPathApplicationContext有什么不同呢?其實現是很簡潔的:
public class XmlWebApplicationContext extends AbstractRefreshableWebApplicationContext { public static final String DEFAULT_CONFIG_LOCATION = "/WEB-INF/applicationContext.xml"; public static final String DEFAULT_CONFIG_LOCATION_PREFIX = "/WEB-INF/"; public static final String DEFAULT_CONFIG_LOCATION_SUFFIX = ".xml"; @Override //這個方法就是再AbstractApplicationContext中的模版方法refresh中調用,和一般IoC容器初始化一樣 protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException { XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory); beanDefinitionReader.setEnvironment(this.getEnvironment()); beanDefinitionReader.setResourceLoader(this); beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this)); initBeanDefinitionReader(beanDefinitionReader); loadBeanDefinitions(beanDefinitionReader); } protected void initBeanDefinitionReader(XmlBeanDefinitionReader beanDefinitionReader) { } protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws IOException { String[] configLocations = getConfigLocations(); if (configLocations != null) { for (String configLocation : configLocations) { reader.loadBeanDefinitions(configLocation); } } } @Override protected String[] getDefaultConfigLocations() { if (getNamespace() != null) { return new String[] {DEFAULT_CONFIG_LOCATION_PREFIX + getNamespace() + DEFAULT_CONFIG_LOCATION_SUFFIX};//就是web.xml中配置的DispatcherServlet的servlet-name,對應我們就會有/WEB-INF/example-servlet.xml } else { return new String[] {DEFAULT_CONFIG_LOCATION}; } } }
簡單就分析完FrameworkServlet的上下文對象的構建過程,繼續看DispatcherServlet怎么構建自己的,就從FrameworkServlet中initWebApplicationContext的onRefresh中,對應DispatcherServlet中的OnRefresh方法:
@Override protected void onRefresh(ApplicationContext context) { initStrategies(context); } protected void initStrategies(ApplicationContext context) { initMultipartResolver(context); //初始化multipartResolver bean, 用于文件上傳處理 initLocaleResolver(context); //初始化localeResolver bean, 用于解析用戶端Locale信息,默認實現AcceptHeaderLocaleResolver initThemeResolver(context); //初始化themeResolver bean, 默認實現FixedThemeResolver initHandlerMappings(context); //初始化handlerMappings集,將用戶請求映射到我們定義的Controller中 initHandlerAdapters(context); //初始化handlerAdapters initHandlerExceptionResolvers(context); //初始化異常處理解析器 initRequestToViewNameTranslator(context); //初始化請求到視圖的翻譯器 initViewResolvers(context); //初始化視圖解析器,如jsp, freemarker等 initFlashMapManager(context); //初始化FlashMap管理器,實現再不同請求中共享參數,如重定向。 }
上面這些組件都有默認實現,spring將默認實現放在和DispatcherServlet.java同包下的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重點看看handlerMappdings怎么建立起來的:
private void initHandlerMappings(ApplicationContext context) { this.handlerMappings = null; //默認會從父上下文遞歸地去獲取HandlerMapping bean if (this.detectAllHandlerMappings) { // Find all HandlerMappings in the ApplicationContext, including ancestor contexts. Map<String, HandlerMapping> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false); if (!matchingBeans.isEmpty()) { this.handlerMappings = new ArrayList<HandlerMapping>(matchingBeans.values()); // We keep HandlerMappings in sorted order. OrderComparator.sort(this.handlerMappings); } }else { //僅從當前上下文獲取HandlerMapping bean HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class); this.handlerMappings = Collections.singletonList(hm); } if (this.handlerMappings == null) { //若沒有配置,根據上面的屬性文件提供默認實現 this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class); } }
看看HandlerMapping接口的定義,十分簡潔,就一個getHandler接口:
public interface HandlerMapping { ... //根據當前request對象,獲取HandlerExecutionChain對象,其包括一個handler對象(我們定義的Controller)和一些定義的攔截器 HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception; }其繼承樹:
HandlerExecutionChain確實保存了handler和interceptors:
public class HandlerExecutionChain { private final Object handler; private HandlerInterceptor[] interceptors; private List<HandlerInterceptor> interceptorList; private int interceptorIndex = -1; ... }
看一個HandlerMapping的簡單實現SimpleUrlHandlerMapping, 我們需要配置urlMap屬性,通過registerHandlers方法將urlMap的屬性注冊到父類的handlerMap中,其保存了url請求與handler bean的對應關系:
public class SimpleUrlHandlerMapping extends AbstractUrlHandlerMapping { private final Map<String, Object> urlMap = new HashMap<String, Object>(); protected void registerHandlers(Map<String, Object> urlMap) throws BeansException { if (urlMap.isEmpty()) { logger.warn("Neither 'urlMap' nor 'mappings' set on SimpleUrlHandlerMapping"); } else { for (Map.Entry<String, Object> entry : urlMap.entrySet()) { String url = entry.getKey(); Object handler = entry.getValue(); // Prepend with slash if not already present. if (!url.startsWith("/")) { url = "/" + url; } // Remove whitespace from handler bean name. if (handler instanceof String) { handler = ((String) handler).trim(); } registerHandler(url, handler); //由父類實現 } } }繼續看AbstractUrlHandlerMapping怎么實現registerHandler()方法:
protected void registerHandler(String urlPath, Object handler) throws BeansException, IllegalStateException { Object resolvedHandler = handler; // Eagerly resolve handler if referencing singleton via name. if (!this.lazyInitHandlers && handler instanceof String) {//若為非懶加載且是單例bean,則從容器中獲取 String handlerName = (String) handler; if (getApplicationContext().isSingleton(handlerName)) { resolvedHandler = getApplicationContext().getBean(handlerName); } } Object mappedHandler = this.handlerMap.get(urlPath); if (mappedHandler != null) { //一個url只能有一個對應的handler if (mappedHandler != resolvedHandler) { throw new IllegalStateException( "Cannot map " + getHandlerDescription(handler) + " to URL path [" + urlPath + "]: There is already " + getHandlerDescription(mappedHandler) + " mapped."); } } else { if (urlPath.equals("/")) { setRootHandler(resolvedHandler); } else if (urlPath.equals("/*")) { setDefaultHandler(resolvedHandler); } else { this.handlerMap.put(urlPath, resolvedHandler); } } }這上面就是HandlerMapping bean怎么被注冊起來的,下面是HandlerMapping Bean怎么完成請求的映射處理的。
看看AbstractHandlerMapping的getHandler(), 根據當前請求來獲取對應的HandlerExecuteChain:
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { Object handler = getHandlerInternal(request); // 獲取handler對象 if (handler == null) { handler = getDefaultHandler(); //默認Handler對象,就是上面注冊的"/"的handler對象 } if (handler == null) { return null; } // Bean name or resolved handler? if (handler instanceof String) { //如果是bean name就從容器中獲取 String handlerName = (String) handler; handler = getApplicationContext().getBean(handlerName); } return getHandlerExecutionChain(handler, request); //獲取handlerExecutionChain }接著看getHandlerInternal怎么獲得handler對象(AbstractUrlHanlderMapping):
protected Object getHandlerInternal(HttpServletRequest request) throws Exception { String lookupPath = getUrlPathHelper().getLookupPathForRequest(request); //提取請求url Object handler = lookupHandler(lookupPath, request); //根據url查找handler if (handler == null) { // We need to care for the default handler directly, since we need to // expose the PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE for it as well. Object rawHandler = null; if ("/".equals(lookupPath)) { rawHandler = getRootHandler(); } if (rawHandler == null) { rawHandler = getDefaultHandler(); } if (rawHandler != null) { // Bean name or resolved handler? if (rawHandler instanceof String) { String handlerName = (String) rawHandler; rawHandler = getApplicationContext().getBean(handlerName); } validateHandler(rawHandler, request); handler = buildPathExposingHandler(rawHandler, lookupPath, lookupPath, null); } } return handler; }
由lookupHandler(url,request)查找handler對象:
protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception { // Direct match? Object handler = this.handlerMap.get(urlPath); //精確匹配獲取handler if (handler != null) { // Bean name or resolved handler? if (handler instanceof String) { String handlerName = (String) handler; handler = getApplicationContext().getBean(handlerName); } validateHandler(handler, request); //驗證handler,由子類實現 return buildPathExposingHandler(handler, urlPath, urlPath, null); } //正則匹配獲取handler List<String> matchingPatterns = new ArrayList<String>(); for (String registeredPattern : this.handlerMap.keySet()) { if (getPathMatcher().match(registeredPattern, urlPath)) { matchingPatterns.add(registeredPattern); } } String bestPatternMatch = null; Comparator<String> patternComparator = getPathMatcher().getPatternComparator(urlPath); if (!matchingPatterns.isEmpty()) { Collections.sort(matchingPatterns, patternComparator); bestPatternMatch = matchingPatterns.get(0); } if (bestPatternMatch != null) { handler = this.handlerMap.get(bestPatternMatch); // Bean name or resolved handler? if (handler instanceof String) { String handlerName = (String) handler; handler = getApplicationContext().getBean(handlerName); } validateHandler(handler, request); String pathWithinMapping = getPathMatcher().extractPathWithinPattern(bestPatternMatch, urlPath); // There might be multiple 'best patterns', let's make sure we have the correct URI template variables // for all of them Map<String, String> uriTemplateVariables = new LinkedHashMap<String, String>(); for (String matchingPattern : matchingPatterns) { if (patternComparator.compare(bestPatternMatch, matchingPattern) == 0) { Map<String, String> vars = getPathMatcher().extractUriTemplateVariables(matchingPattern, urlPath); Map<String, String> decodedVars = getUrlPathHelper().decodePathVariables(request, vars); uriTemplateVariables.putAll(decodedVars); } } return buildPathExposingHandler(handler, bestPatternMatch, pathWithinMapping, uriTemplateVariables); } // No handler found... return null; }上面就明白了根據請求獲取handler對象的過程, 下面講講請求是怎么被DispatcherServlet分發處理的.
DispatcherServlet分發處理主要方法為doServlce方法開始:
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception { // 如果request請求是include請求,則對請求屬性作一個快照 Map<String, Object> attributesSnapshot = null; if (WebUtils.isIncludeRequest(request)) { attributesSnapshot = new HashMap<String, Object>(); Enumeration<?> attrNames = request.getAttributeNames(); while (attrNames.hasMoreElements()) { String attrName = (String) attrNames.nextElement(); if (this.cleanupAfterInclude || attrName.startsWith("org.springframework.web.servlet")) { attributesSnapshot.put(attrName, request.getAttribute(attrName)); } } } //設置一些涉及MVC框架的屬性,以方便handler或view等引用. request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext()); request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver); request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver); request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource()); //更新FlashMap屬性,該屬性是基于Session實現,主要是在不同request請求間傳遞參數,典型的就是Redirect這種情況 FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response); if (inputFlashMap != null) { request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap)); } request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap()); request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager); try { doDispatch(request, response); //真正實現將請求進行轉發 } finally { if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) { return; } // Restore the original attribute snapshot, in case of an include. if (attributesSnapshot != null) { restoreAttributesAfterInclude(request, attributesSnapshot); } } }
看doDispatch():
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { HttpServletRequest processedRequest = request; HandlerExecutionChain mappedHandler = null; boolean multipartRequestParsed = false; WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); ModelAndView mv = null; Exception dispatchException = null; processedRequest = checkMultipart(request);//檢查request是否Multipart, 即是否上傳文件 multipartRequestParsed = processedRequest != request; //獲取當前請求對應的HandlerExecutionChain對象 mappedHandler = getHandler(processedRequest, false); if (mappedHandler == null || mappedHandler.getHandler() == null) { noHandlerFound(processedRequest, response); return; } // Determine handler adapter for the current request. HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); // Process last-modified header, if supported by the handler. String method = request.getMethod(); boolean isGet = "GET".equals(method); if (isGet || "HEAD".equals(method)) { long lastModified = ha.getLastModified(request, mappedHandler.getHandler()); if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) { return; } } if (!mappedHandler.applyPreHandle(processedRequest, response)) {//前置處理,調用chain的攔截器預處理方法preHandle return; } ... //調用handler處理方法,最終會調用Controller的handleRequest方法,并返回ModelAndView對象 mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); ... applyDefaultViewName(request, mv);//解析視圖名稱,就是我們配置的prefix + uri + suffix mappedHandler.applyPostHandle(processedRequest, response, mv);//后置處理,調用chain的攔截器postHandle方法 processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException); //進行視圖渲染 ... }
接著看processDispatchResult()方法:
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response, HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception) throws Exception { boolean errorView = false; if (exception != null) { //如果有異常 if (exception instanceof ModelAndViewDefiningException) { logger.debug("ModelAndViewDefiningException encountered", exception); mv = ((ModelAndViewDefiningException) exception).getModelAndView(); } else { Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null); mv = processHandlerException(request, response, handler, exception); errorView = (mv != null); } } // Did the handler return a view to render? if (mv != null && !mv.wasCleared()) { render(mv, request, response); //渲染視圖的最后一個步驟 if (errorView) { WebUtils.clearErrorRequestAttributes(request); } }else { if (logger.isDebugEnabled()) { logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName() + "': assuming HandlerAdapter completed request handling"); } } if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) { // Concurrent handling started during a forward return; } if (mappedHandler != null) { mappedHandler.triggerAfterCompletion(request, response, null); } }看看render怎么工作的:
protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception { // Determine locale for request and apply it to the response. Locale locale = this.localeResolver.resolveLocale(request); response.setLocale(locale); View view; if (mv.isReference()) { // 若ModalAndView中保存了view的引用 //根據我們配置的ViewReslover bean,解析視圖名稱 view = resolveViewName(mv.getViewName(), mv.getModelInternal(), locale, request); if (view == null) { throw new ServletException( "Could not resolve view with name '" + mv.getViewName() + "' in servlet with name '" + getServletName() + "'"); } } else { //ModalAndView中保存了實際的view對象 view = mv.getView(); if (view == null) { throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a " + "View object in servlet with name '" + getServletName() + "'"); } } view.render(mv.getModelInternal(), request, response); //渲染視圖 }上面講述了DispatcherServlet的請求分發過程及視圖渲染大致流程。接下來得說說視圖渲染得話題,默認DispatcherServlet的默認視圖實是 InternalResourceViewResolver, 也就是jsp視圖,看看jsp視圖是怎么被呈現的。
先看AbstractView的render方法:
public void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception { Map<String, Object> mergedModel = createMergedOutputModel(model, request, response);//合并動態和靜態屬性 prepareResponse(request, response); renderMergedOutputModel(mergedModel, request, response); //用模型渲染視圖 }
看看InternalResourceView的renderMergeOutputModel怎么實現的:
protected void renderMergedOutputModel( Map<String, Object> model, HttpServletRequest request, HttpServletResponse response){ // Determine which request handle to expose to the RequestDispatcher. HttpServletRequest requestToExpose = getRequestToExpose(request); //將model放入到request屬性中 exposeModelAsRequestAttributes(model, requestToExpose); // Expose helpers as request attributes, if any. exposeHelpers(requestToExpose); //獲取request對象dispath的path String dispatcherPath = prepareForRendering(requestToExpose, response); //獲取RequestDispatcher對象 RequestDispatcher rd = getRequestDispatcher(requestToExpose, dispatcherPath); if (rd == null) { throw new ServletException("Could not get RequestDispatcher for [" + getUrl() + "]: Check that the corresponding file exists within your web application archive!"); } // If already included or response already committed, perform include, else forward. if (useInclude(requestToExpose, response)) { response.setContentType(getContentType()); rd.include(requestToExpose, response); }else { // Note: The forwarded resource is supposed to determine the content type itself. exposeForwardRequestAttributes(requestToExpose); rd.forward(requestToExpose, response); } }
這上面就講述了MVC中視圖的工作。
- 關于Spring根容器的建立, 之前說了,Spring會建立根上下文,再建立MVC上下文,我們簡略說下根上下文的建立,也就是web.xml的ContextLoaderListener配置:
<listener> <listener-class> org.springframework.web.context.ContextLoaderListener </listener-class> </listener>看看ContextLoaderListener, 它繼承自ContextLoader, 主要初始化工作還是由ContextLoader來完成
public class ContextLoaderListener extends ContextLoader implements ServletContextListener { ... }看看ContextLoader的initWebApplicationContext方法:
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) { if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) { ... } long startTime = System.currentTimeMillis(); // 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()) { // 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 -> // determine parent for root web application context, if any. ApplicationContext parent = loadParentContext(servletContext); cwac.setParent(parent); } configureAndRefreshWebApplicationContext(cwac, servletContext);//構建上下文,類似IoC容器構建 } } //將創建的根上下文保存到ServletContext, 所以再MVC上下文中可以獲取到 servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context); ClassLoader ccl = Thread.currentThread().getContextClassLoader(); if (ccl == ContextLoader.class.getClassLoader()) { currentContext = this.context; } else if (ccl != null) { currentContextPerThread.put(ccl, this.context); } return this.context; }
protected WebApplicationContext createWebApplicationContext(ServletContext sc) { Class<?> contextClass = determineContextClass(sc);//默認實現為XmlWebApplicationContext 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); }
這就簡略說了Spring根上下文的建立過程。
來自:http://my.oschina.net/indestiny/blog/197061