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根上下文的建立过程。
收工。
来源:oschina
链接:https://my.oschina.net/u/222173/blog/197061