SpringMVC如今應該用得很普遍了,其配置清晰,靈活度,定製能力等都是很強的,相比Struts2也是賽過一籌,仍是從源碼來分析一下,SpringMVC爲咱們作了什麼。 java
<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等)進行渲染,而後返回到用戶瀏覽器: web
那到底要作些什麼工做,才能達到上面這樣的流程呢,往下說。 spring
看看DispatcherServlet怎麼來的: 瀏覽器
先從HttpServletBean類入手,人類都知道Servlet被Servlet容器建立時,會調用一次其init方法,咱們就看看HttpServletBean的init方法,其主要工做就是將Servlet配置參數映射到咱們的Servlet Bean裏面,而後再初始化子類(initServletBean): mvc
@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是個空方法,留給子類去實現: app
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上下文對象的: jsp
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方法: async
@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中: ide
# 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中視圖的工做。
<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根上下文的創建過程。
收工。