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