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