- //這裏是對DispatcherServlet的初始化方法,根據名字我們很方面的看到對各個Spring MVC主要元素的初始化
- protected void initFrameworkServlet() throws ServletException, BeansException {
- initMultipartResolver();
- initLocaleResolver();
- initThemeResolver();
- initHandlerMappings();
- initHandlerAdapters();
- initHandlerExceptionResolvers();
- initRequestToViewNameTranslator();
- initViewResolvers();
- }
//這裏是對DispatcherServlet的初始化方法,根據名字我們很方面的看到對各個Spring MVC主要元素的初始化
protected void initFrameworkServlet() throws ServletException, BeansException {
initMultipartResolver();
initLocaleResolver();
initThemeResolver();
initHandlerMappings();
initHandlerAdapters();
initHandlerExceptionResolvers();
initRequestToViewNameTranslator();
initViewResolvers();
}
看到註解我們知道,這是DispatcherSerlvet的初始化過程,它是在WebApplicationContext已經存在的情況下進行的,也就意味着在初始化它的時候,IOC容器應該已經工作了,這也是我們在web.xml中配置Spring的時候,需要把DispatcherServlet的 load-on-startup的屬性配置爲2的原因。
對於具體的初始化過程,很容易理解,我們拿initHandlerMappings()來看看:
- private void initHandlerMappings() throws BeansException {
- if (this.detectAllHandlerMappings) {
- // 這裏找到所有在上下文中定義的HandlerMapping,同時把他們排序
- // 因爲在同一個上下文中可以有不止一個handlerMapping,所以我們把他們都載入到一個鏈裏進行維護和管理
- Map matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(
- getWebApplicationContext(), HandlerMapping.class, true, false);
- if (!matchingBeans.isEmpty()) {
- this.handlerMappings = new ArrayList(matchingBeans.values());
- // 這裏通過order屬性來對handlerMapping來在list中排序
- Collections.sort(this.handlerMappings, new OrderComparator());
- }
- }
- else {
- try {
- Object hm = getWebApplicationContext().getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
- this.handlerMappings = Collections.singletonList(hm);
- }
- catch (NoSuchBeanDefinitionException ex) {
- // Ignore, we'll add a default HandlerMapping later.
- }
- }
- //如果在上下文中沒有定義的話,那麼我們使用默認的BeanNameUrlHandlerMapping
- if (this.handlerMappings == null) {
- this.handlerMappings = getDefaultStrategies(HandlerMapping.class);
- ........
- }
- }
private void initHandlerMappings() throws BeansException {
if (this.detectAllHandlerMappings) {
// 這裏找到所有在上下文中定義的HandlerMapping,同時把他們排序
// 因爲在同一個上下文中可以有不止一個handlerMapping,所以我們把他們都載入到一個鏈裏進行維護和管理
Map matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(
getWebApplicationContext(), HandlerMapping.class, true, false);
if (!matchingBeans.isEmpty()) {
this.handlerMappings = new ArrayList(matchingBeans.values());
// 這裏通過order屬性來對handlerMapping來在list中排序
Collections.sort(this.handlerMappings, new OrderComparator());
}
}
else {
try {
Object hm = getWebApplicationContext().getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
this.handlerMappings = Collections.singletonList(hm);
}
catch (NoSuchBeanDefinitionException ex) {
// Ignore, we'll add a default HandlerMapping later.
}
}
//如果在上下文中沒有定義的話,那麼我們使用默認的BeanNameUrlHandlerMapping
if (this.handlerMappings == null) {
this.handlerMappings = getDefaultStrategies(HandlerMapping.class);
........
}
}
怎樣獲得上下文環境,可以參見我們前面的對IOC容器在web環境中加載的分析。 DispatcherServlet把定義了的所有HandlerMapping都加載了放在一個List裏待以後進行使用,這個鏈的每一個元素都是一個handlerMapping的配置,而一般每一個handlerMapping可以持有一系列從URL請求到 Spring Controller的映射,比如SimpleUrl
HandlerMaaping中就定義了一個map來持有這一系列的映射關係。
DisptcherServlet通過HandlerMapping使得Web應用程序確定一個執行路徑,就像我們在HanderMapping中看到的那樣,HandlerMapping只是一個藉口:
- public interface HandlerMapping {
- public static final String PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE =
- Conventions.getQualifiedAttributeName(HandlerMapping.class, "pathWithinHandlerMapping");
- //實際上維護一個HandlerExecutionChain,這是典型的Command的模式的使用,這個執行鏈裏面維護handler和攔截器
- HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
- }
public interface HandlerMapping {
public static final String PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE =
Conventions.getQualifiedAttributeName(HandlerMapping.class, "pathWithinHandlerMapping");
//實際上維護一個HandlerExecutionChain,這是典型的Command的模式的使用,這個執行鏈裏面維護handler和攔截器
HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
}
他的具體實現只需要實現一個接口方法,而這個接口方法返回的是一個HandlerExecutionChain,實際上就是一個執行鏈,就像在Command模式描述的那樣,這個類很簡單,就是一個持有一個Interceptor鏈和一個Controller:
- public class HandlerExecutionChain {
- private Object handler;
- private HandlerInterceptor[] interceptors;
- ........
- }
public class HandlerExecutionChain {
private Object handler;
private HandlerInterceptor[] interceptors;
........
}
而這些Handler和Interceptor需要我們定義HandlerMapping的時候配置好,比如對具體的 SimpleURLHandlerMapping,他要做的就是根據URL映射的方式註冊Handler和Interceptor,自己維護一個放映映射的handlerMap,當需要匹配Http請求的時候需要使用這個表裏的信息來得到執行鏈。這個註冊的過程在IOC容器初始化 SimpleUrlHandlerMapping的時候就被完成了,這樣以後的解析纔可以用到map裏的映射信息,這裏的信息和bean文件的信息是等價的,下面是具體的註冊過程:
- protected void registerHandlers(Map urlMap) throws BeansException {
- if (urlMap.isEmpty()) {
- logger.warn("Neither 'urlMap' nor 'mappings' set on SimpleUrlHandlerMapping");
- }
- else {
- //這裏迭代在SimpleUrlHandlerMapping中定義的所有映射元素
- Iterator it = urlMap.keySet().iterator();
- while (it.hasNext()) {
- //這裏取得配置的url
- String url = (String) it.next();
- //這裏根據url在bean定義中取得對應的handler
- Object handler = urlMap.get(url);
- // Prepend with slash if not already present.
- if (!url.startsWith("/")) {
- url = "/" + url;
- }
- //這裏調用AbstractHandlerMapping中的註冊過程
- registerHandler(url, handler);
- }
- }
- }
protected void registerHandlers(Map urlMap) throws BeansException {
if (urlMap.isEmpty()) {
logger.warn("Neither 'urlMap' nor 'mappings' set on SimpleUrlHandlerMapping");
}
else {
//這裏迭代在SimpleUrlHandlerMapping中定義的所有映射元素
Iterator it = urlMap.keySet().iterator();
while (it.hasNext()) {
//這裏取得配置的url
String url = (String) it.next();
//這裏根據url在bean定義中取得對應的handler
Object handler = urlMap.get(url);
// Prepend with slash if not already present.
if (!url.startsWith("/")) {
url = "/" + url;
}
//這裏調用AbstractHandlerMapping中的註冊過程
registerHandler(url, handler);
}
}
}
在AbstractMappingHandler中的註冊代碼:
- protected void registerHandler(String urlPath, Object handler) throws BeansException, IllegalStateException {
- //試圖從handlerMap中取handler,看看是否已經存在同樣的Url映射關係
- Object mappedHandler = this.handlerMap.get(urlPath);
- if (mappedHandler != null) {
- ........
- }
- //如果是直接用bean名做映射那就直接從容器中取handler
- if (!this.lazyInitHandlers && handler instanceof String) {
- String handlerName = (String) handler;
- if (getApplicationContext().isSingleton(handlerName)) {
- handler = getApplicationContext().getBean(handlerName);
- }
- }
- //或者使用默認的handler.
- if (urlPath.equals("/*")) {
- setDefaultHandler(handler);
- }
- else {
- //把url和handler的對應關係放到handlerMap中去
- this.handlerMap.put(urlPath, handler);
- ........
- }
- }
protected void registerHandler(String urlPath, Object handler) throws BeansException, IllegalStateException {
//試圖從handlerMap中取handler,看看是否已經存在同樣的Url映射關係
Object mappedHandler = this.handlerMap.get(urlPath);
if (mappedHandler != null) {
........
}
//如果是直接用bean名做映射那就直接從容器中取handler
if (!this.lazyInitHandlers && handler instanceof String) {
String handlerName = (String) handler;
if (getApplicationContext().isSingleton(handlerName)) {
handler = getApplicationContext().getBean(handlerName);
}
}
//或者使用默認的handler.
if (urlPath.equals("/*")) {
setDefaultHandler(handler);
}
else {
//把url和handler的對應關係放到handlerMap中去
this.handlerMap.put(urlPath, handler);
........
}
}
handlerMap是持有的一個HashMap,裏面就保存了具體的映射信息:
- private final Map handlerMap = new HashMap();
private final Map handlerMap = new HashMap();
而SimpleUrlHandlerMapping對接口HandlerMapping的實現是這樣的,這個getHandler根據在初始化的時候就得到的映射表來生成DispatcherServlet需要的執行鏈
- public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
- //這裏根據request中的參數得到其對應的handler,具體處理在AbstractUrlHandlerMapping中
- Object handler = getHandlerInternal(request);
- //如果找不到對應的,就使用缺省的handler
- if (handler == null) {
- handler = this.defaultHandler;
- }
- //如果缺省的也沒有,那就沒辦法了
- if (handler == null) {
- return null;
- }
- // 如果handler不是一個具體的handler,那我們還要到上下文中取
- if (handler instanceof String) {
- String handlerName = (String) handler;
- handler = getApplicationContext().getBean(handlerName);
- }
- //生成一個HandlerExecutionChain,其中放了我們匹配上的handler和定義好的攔截器,就像我們在HandlerExecutionChain中看到的那樣,它持有一個handler和一個攔截器組。
- return new HandlerExecutionChain(handler, this.adaptedInterceptors);
- }
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
//這裏根據request中的參數得到其對應的handler,具體處理在AbstractUrlHandlerMapping中
Object handler = getHandlerInternal(request);
//如果找不到對應的,就使用缺省的handler
if (handler == null) {
handler = this.defaultHandler;
}
//如果缺省的也沒有,那就沒辦法了
if (handler == null) {
return null;
}
// 如果handler不是一個具體的handler,那我們還要到上下文中取
if (handler instanceof String) {
String handlerName = (String) handler;
handler = getApplicationContext().getBean(handlerName);
}
//生成一個HandlerExecutionChain,其中放了我們匹配上的handler和定義好的攔截器,就像我們在HandlerExecutionChain中看到的那樣,它持有一個handler和一個攔截器組。
return new HandlerExecutionChain(handler, this.adaptedInterceptors);
}
我們看看具體的handler查找過程:
- protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
- //這裏的HTTP Request傳進來的參數進行分析,得到具體的路徑信息。
- String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
- .......//下面是根據請求信息的查找
- return lookupHandler(lookupPath, request);
- }
- protected Object lookupHandler(String urlPath, HttpServletRequest request) {
- // 如果能夠直接能在SimpleUrlHandlerMapping的映射表中找到,那最好。
- Object handler = this.handlerMap.get(urlPath);
- if (handler == null) {
- // 這裏使用模式來對map中的所有handler進行匹配,調用了Jre中的Matcher類來完成匹配處理。
- String bestPathMatch = null;
- for (Iterator it = this.handlerMap.keySet().iterator(); it.hasNext();) {
- String registeredPath = (String) it.next();
- if (this.pathMatcher.match(registeredPath, urlPath) &&
- (bestPathMatch == null || bestPathMatch.length() <= registeredPath.length())) {
- //這裏根據匹配路徑找到最象的一個
- handler = this.handlerMap.get(registeredPath);
- bestPathMatch = registeredPath;
- }
- }
- if (handler != null) {
- exposePathWithinMapping(this.pathMatcher.extractPathWithinPattern(bestPathMatch, urlPath), request);
- }
- }
- else {
- exposePathWithinMapping(urlPath, request);
- }
- //
- return handler;
- }
protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
//這裏的HTTP Request傳進來的參數進行分析,得到具體的路徑信息。
String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
.......//下面是根據請求信息的查找
return lookupHandler(lookupPath, request);
}
protected Object lookupHandler(String urlPath, HttpServletRequest request) {
// 如果能夠直接能在SimpleUrlHandlerMapping的映射表中找到,那最好。
Object handler = this.handlerMap.get(urlPath);
if (handler == null) {
// 這裏使用模式來對map中的所有handler進行匹配,調用了Jre中的Matcher類來完成匹配處理。
String bestPathMatch = null;
for (Iterator it = this.handlerMap.keySet().iterator(); it.hasNext();) {
String registeredPath = (String) it.next();
if (this.pathMatcher.match(registeredPath, urlPath) &&
(bestPathMatch == null || bestPathMatch.length() <= registeredPath.length())) {
//這裏根據匹配路徑找到最象的一個
handler = this.handlerMap.get(registeredPath);
bestPathMatch = registeredPath;
}
}
if (handler != null) {
exposePathWithinMapping(this.pathMatcher.extractPathWithinPattern(bestPathMatch, urlPath), request);
}
}
else {
exposePathWithinMapping(urlPath, request);
}
//
return handler;
}
我們可以看到,總是在handlerMap這個HashMap中找,當然如果直接找到最好,如果找不到,就看看是不是能通過Match Pattern的模式找,我們一定還記得在配置HnaderMapping的時候是可以通過ANT語法進行配置的,其中的處理就在這裏。
這樣可以清楚地看到整個HandlerMapping的初始化過程 - 同時,我們也看到了一個具體的handler映射是怎樣被存儲和查找的 - 這裏生成一個ExecutionChain來儲存我們找到的handler和在定義bean的時候定義的Interceptors.
讓我們回到DispatcherServlet,初始化完成以後,實際的對web請求是在doService()方法中處理的,我們知道DispatcherServlet只是一個普通的Servlet:
- protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
- .......
- //這裏把屬性信息進行保存
- Map attributesSnapshot = null;
- if (WebUtils.isIncludeRequest(request)) {
- logger.debug("Taking snapshot of request attributes before include");
- attributesSnapshot = new HashMap();
- Enumeration attrNames = request.getAttributeNames();
- while (attrNames.hasMoreElements()) {
- String attrName = (String) attrNames.nextElement();
- if (this.cleanupAfterInclude || attrName.startsWith(DispatcherServlet.class.getName())) {
- attributesSnapshot.put(attrName, request.getAttribute(attrName));
- }
- }
- }
- // Make framework objects available to handlers and view objects.
- 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());
- try {
- //這裏使實際的處理入口
- doDispatch(request, response);
- }
- finally {
- // Restore the original attribute snapshot, in case of an include.
- if (attributesSnapshot != null) {
- restoreAttributesAfterInclude(request, attributesSnapshot);
- }
- }
- }
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
.......
//這裏把屬性信息進行保存
Map attributesSnapshot = null;
if (WebUtils.isIncludeRequest(request)) {
logger.debug("Taking snapshot of request attributes before include");
attributesSnapshot = new HashMap();
Enumeration attrNames = request.getAttributeNames();
while (attrNames.hasMoreElements()) {
String attrName = (String) attrNames.nextElement();
if (this.cleanupAfterInclude || attrName.startsWith(DispatcherServlet.class.getName())) {
attributesSnapshot.put(attrName, request.getAttribute(attrName));
}
}
}
// Make framework objects available to handlers and view objects.
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());
try {
//這裏使實際的處理入口
doDispatch(request, response);
}
finally {
// Restore the original attribute snapshot, in case of an include.
if (attributesSnapshot != null) {
restoreAttributesAfterInclude(request, attributesSnapshot);
}
}
}
我們看到,對於請求的處理實際上是讓doDispatch()來完成的 - 這個方法很長,但是過程很簡單明瞭:
- protected void doDispatch(final HttpServletRequest request, HttpServletResponse response) throws Exception {
- HttpServletRequest processedRequest = request;
- //這是從handlerMapping中得到的執行鏈
- HandlerExecutionChain mappedHandler = null;
- int interceptorIndex = -1;
- ........
- try {
- //我們熟悉的ModelAndView開始出現了。
- ModelAndView mv = null;
- try {
- processedRequest = checkMultipart(request);
- // 這是我們得到handler的過程
- mappedHandler = getHandler(processedRequest, false);
- if (mappedHandler == null || mappedHandler.getHandler() == null) {
- noHandlerFound(processedRequest, response);
- return;
- }
- // 這裏取出執行鏈中的Interceptor進行前處理
- if (mappedHandler.getInterceptors() != null) {
- for (int i = 0; i < mappedHandler.getInterceptors().length; i++) {
- HandlerInterceptor interceptor = mappedHandler.getInterceptors()[i];
- if (!interceptor.preHandle(processedRequest, response, mappedHandler.getHandler())) {
- triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, null);
- return;
- }
- interceptorIndex = i;
- }
- }
- //在執行handler之前,用HandlerAdapter先檢查一下handler的合法性:是不是按Spring的要求編寫的。
- HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
- mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
- // 這裏取出執行鏈中的Interceptor進行後處理
- if (mappedHandler.getInterceptors() != null) {
- for (int i = mappedHandler.getInterceptors().length - 1; i >= 0; i--) {
- HandlerInterceptor interceptor = mappedHandler.getInterceptors()[i];
- interceptor.postHandle(processedRequest, response, mappedHandler.getHandler(), mv);
- }
- }
- }
- ........
- // Did the handler return a view to render?
- //這裏對視圖生成進行處理
- if (mv != null && !mv.wasCleared()) {
- render(mv, processedRequest, response);
- }
- .......
- }
protected void doDispatch(final HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
//這是從handlerMapping中得到的執行鏈
HandlerExecutionChain mappedHandler = null;
int interceptorIndex = -1;
........
try {
//我們熟悉的ModelAndView開始出現了。
ModelAndView mv = null;
try {
processedRequest = checkMultipart(request);
// 這是我們得到handler的過程
mappedHandler = getHandler(processedRequest, false);
if (mappedHandler == null || mappedHandler.getHandler() == null) {
noHandlerFound(processedRequest, response);
return;
}
// 這裏取出執行鏈中的Interceptor進行前處理
if (mappedHandler.getInterceptors() != null) {
for (int i = 0; i < mappedHandler.getInterceptors().length; i++) {
HandlerInterceptor interceptor = mappedHandler.getInterceptors()[i];
if (!interceptor.preHandle(processedRequest, response, mappedHandler.getHandler())) {
triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, null);
return;
}
interceptorIndex = i;
}
}
//在執行handler之前,用HandlerAdapter先檢查一下handler的合法性:是不是按Spring的要求編寫的。
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
// 這裏取出執行鏈中的Interceptor進行後處理
if (mappedHandler.getInterceptors() != null) {
for (int i = mappedHandler.getInterceptors().length - 1; i >= 0; i--) {
HandlerInterceptor interceptor = mappedHandler.getInterceptors()[i];
interceptor.postHandle(processedRequest, response, mappedHandler.getHandler(), mv);
}
}
}
........
// Did the handler return a view to render?
//這裏對視圖生成進行處理
if (mv != null && !mv.wasCleared()) {
render(mv, processedRequest, response);
}
.......
}
我們很清楚的看到和MVC框架緊密相關的代碼,比如如何得到和http請求相對應的執行鏈,怎樣執行執行鏈和怎樣把模型數據展現到視圖中去。
先看怎樣取得Command對象,對我們來說就是Handler - 下面是getHandler的代碼:
- protected HandlerExecutionChain getHandler(HttpServletRequest request, boolean cache) throws Exception {
- //在ServletContext取得執行鏈 - 實際上第一次得到它的時候,我們把它放在ServletContext進行了緩存。
- HandlerExecutionChain handler =
- (HandlerExecutionChain) request.getAttribute(HANDLER_EXECUTION_CHAIN_ATTRIBUTE);
- if (handler != null) {
- if (!cache) {
- request.removeAttribute(HANDLER_EXECUTION_CHAIN_ATTRIBUTE);
- }
- return handler;
- }
- //這裏的迭代器迭代的時在initHandlerMapping中載入的上下文所有的HandlerMapping
- Iterator it = this.handlerMappings.iterator();
- while (it.hasNext()) {
- HandlerMapping hm = (HandlerMapping) it.next();
- .......
- //這裏是實際取得handler的過程,在每個HandlerMapping中建立的映射表進行檢索得到請求對應的handler
- handler = hm.getHandler(request);
- //然後把handler存到ServletContext中去進行緩存
- if (handler != null) {
- if (cache) {
- request.setAttribute(HANDLER_EXECUTION_CHAIN_ATTRIBUTE, handler);
- }
- return handler;
- }
- }
- return null;
- }
protected HandlerExecutionChain getHandler(HttpServletRequest request, boolean cache) throws Exception {
//在ServletContext取得執行鏈 - 實際上第一次得到它的時候,我們把它放在ServletContext進行了緩存。
HandlerExecutionChain handler =
(HandlerExecutionChain) request.getAttribute(HANDLER_EXECUTION_CHAIN_ATTRIBUTE);
if (handler != null) {
if (!cache) {
request.removeAttribute(HANDLER_EXECUTION_CHAIN_ATTRIBUTE);
}
return handler;
}
//這裏的迭代器迭代的時在initHandlerMapping中載入的上下文所有的HandlerMapping
Iterator it = this.handlerMappings.iterator();
while (it.hasNext()) {
HandlerMapping hm = (HandlerMapping) it.next();
.......
//這裏是實際取得handler的過程,在每個HandlerMapping中建立的映射表進行檢索得到請求對應的handler
handler = hm.getHandler(request);
//然後把handler存到ServletContext中去進行緩存
if (handler != null) {
if (cache) {
request.setAttribute(HANDLER_EXECUTION_CHAIN_ATTRIBUTE, handler);
}
return handler;
}
}
return null;
}
如果在ServletContext中可以取得handler則直接返回,實際上這個handler是緩衝了上次處理的結果 - 總要有第一次把這個handler放到ServletContext中去:
如果在ServletContext中找不到handler,那就通過持有的handlerMapping生成一個,我們看到它會迭代當前持有的所有的 handlerMapping,因爲可以定義不止一個,他們在定義的時候也可以指定順序,直到找到第一個,然後返回。先找到一個 handlerMapping,然後通過這個handlerMapping返回一個執行鏈,裏面包含了最終的Handler和我們定義的一連串的 Interceptor。具體的我們可以參考上面的SimpleUrlHandlerMapping的代碼分析知道getHandler是怎樣得到一個 HandlerExecutionChain的。
得到HandlerExecutionChain以後,我們通過HandlerAdapter對這個Handler的合法性進行判斷:
- protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
- Iterator it = this.handlerAdapters.iterator();
- while (it.hasNext()) {
- //同樣對持有的所有adapter進行匹配
- HandlerAdapter ha = (HandlerAdapter) it.next();
- if (ha.supports(handler)) {
- return ha;
- }
- }
- ........
- }
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
Iterator it = this.handlerAdapters.iterator();
while (it.hasNext()) {
//同樣對持有的所有adapter進行匹配
HandlerAdapter ha = (HandlerAdapter) it.next();
if (ha.supports(handler)) {
return ha;
}
}
........
}
通過判斷,我們知道這個handler是不是一個Controller接口的實現,比如對於具體的HandlerAdapter - SimpleControllerHandlerAdapter:
- public class SimpleControllerHandlerAdapter implements HandlerAdapter {
- public boolean supports(Object handler) {
- return (handler instanceof Controller);
- }
- .......
- }
public class SimpleControllerHandlerAdapter implements HandlerAdapter {
public boolean supports(Object handler) {
return (handler instanceof Controller);
}
.......
}
簡單的判斷一下handler是不是實現了Controller接口。這也體現了一種對配置文件進行驗證的機制。
讓我們再回到DispatcherServlet看到代碼:
- mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
這個就是對handle的具體調用!相當於Command模式裏的Command.execute();理所當然的返回一個ModelAndView,下面就是一個對View進行處理的過程:
- if (mv != null && !mv.wasCleared()) {
- render(mv, processedRequest, response);
- }
if (mv != null && !mv.wasCleared()) {
render(mv, processedRequest, response);
}
調用的是render方法:
- protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response)
- throws Exception {response.setLocale(locale);
- View view = null;
- //這裏把默認的視圖放到ModelAndView中去。
- if (!mv.hasView()) {
- mv.setViewName(getDefaultViewName(request));
- }
- if (mv.isReference()) {
- // 這裏對視圖名字進行解析
- view = resolveViewName(mv.getViewName(), mv.getModelInternal(), locale, request);
- .......
- }
- else {
- // 有可能在ModelAndView裏已經直接包含了View對象,那我們就直接使用。
- view = mv.getView();
- ........
- }
- //得到具體的View對象以後,我們用它來生成視圖。
- view.render(mv.getModelInternal(), request, response);
- }
protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response)
throws Exception {response.setLocale(locale);
View view = null;
//這裏把默認的視圖放到ModelAndView中去。
if (!mv.hasView()) {
mv.setViewName(getDefaultViewName(request));
}
if (mv.isReference()) {
// 這裏對視圖名字進行解析
view = resolveViewName(mv.getViewName(), mv.getModelInternal(), locale, request);
.......
}
else {
// 有可能在ModelAndView裏已經直接包含了View對象,那我們就直接使用。
view = mv.getView();
........
}
//得到具體的View對象以後,我們用它來生成視圖。
view.render(mv.getModelInternal(), request, response);
}
從整個過程我們看到先在ModelAndView中尋找視圖的邏輯名,如果找不到那就使用缺省的視圖,如果能夠找到視圖的名字,那就對他進行解析得到實際的需要使用的視圖對象。還有一種可能就是在ModelAndView中已經包含了實際的視圖對象,這個視圖對象是可以直接使用的。
不管怎樣,得到一個視圖對象以後,通過調用視圖對象的render來完成數據的顯示過程,我們可以看看具體的JstlView是怎樣實現的,我們在JstlView的抽象父類 AbstractView中找到render方法:
- public void render(Map model, HttpServletRequest request, HttpServletResponse response) throws Exception {
- ......
- // 這裏把所有的相關信息都收集到一個Map裏
- Map mergedModel = new HashMap(this.staticAttributes.size() + (model != null ? model.size() : 0));
- mergedModel.putAll(this.staticAttributes);
- if (model != null) {
- mergedModel.putAll(model);
- }
- // Expose RequestContext?
- if (this.requestContextAttribute != null) {
- mergedModel.put(this.requestContextAttribute, createRequestContext(request, mergedModel));
- }
- //這是實際的展現模型數據到視圖的調用。
- renderMergedOutputModel(mergedModel, request, response);
- }
public void render(Map model, HttpServletRequest request, HttpServletResponse response) throws Exception {
......
// 這裏把所有的相關信息都收集到一個Map裏
Map mergedModel = new HashMap(this.staticAttributes.size() + (model != null ? model.size() : 0));
mergedModel.putAll(this.staticAttributes);
if (model != null) {
mergedModel.putAll(model);
}
// Expose RequestContext?
if (this.requestContextAttribute != null) {
mergedModel.put(this.requestContextAttribute, createRequestContext(request, mergedModel));
}
//這是實際的展現模型數據到視圖的調用。
renderMergedOutputModel(mergedModel, request, response);
}
註解寫的很清楚了,先把所有的數據模型進行整合放到一個Map - mergedModel裏,然後調用renderMergedOutputModel();這個renderMergedOutputModel是一個模板方法,他的實現在InternalResourceView也就是JstlView的父類:
- protected void renderMergedOutputModel(
- Map model, HttpServletRequest request, HttpServletResponse response) throws Exception {
- // Expose the model object as request attributes.
- exposeModelAsRequestAttributes(model, request);
- // Expose helpers as request attributes, if any.
- exposeHelpers(request);
- // 這裏得到InternalResource定義的內部資源路徑。
- String dispatcherPath = prepareForRendering(request, response);
- //這裏把請求轉發到前面得到的內部資源路徑中去。
- RequestDispatcher rd = request.getRequestDispatcher(dispatcherPath);
- if (rd == null) {
- throw new ServletException(
- "Could not get RequestDispatcher for [" + getUrl() + "]: check that this file exists within your WAR");
- }
- .......
protected void renderMergedOutputModel(
Map model, HttpServletRequest request, HttpServletResponse response) throws Exception {
// Expose the model object as request attributes.
exposeModelAsRequestAttributes(model, request);
// Expose helpers as request attributes, if any.
exposeHelpers(request);
// 這裏得到InternalResource定義的內部資源路徑。
String dispatcherPath = prepareForRendering(request, response);
//這裏把請求轉發到前面得到的內部資源路徑中去。
RequestDispatcher rd = request.getRequestDispatcher(dispatcherPath);
if (rd == null) {
throw new ServletException(
"Could not get RequestDispatcher for [" + getUrl() + "]: check that this file exists within your WAR");
}
.......
}
首先對模型數據進行處理,exposeModelAsRequestAttributes是在AbstractView中實現的,這個方法把 ModelAndView中的模型數據和其他request數據統統放到ServletContext當中去,這樣整個模型數據就通過 ServletContext暴露並得到共享使用了:
- protected void exposeModelAsRequestAttributes(Map model, HttpServletRequest request) throws Exception {
- Iterator it = model.entrySet().iterator();
- while (it.hasNext()) {
- Map.Entry entry = (Map.Entry) it.next();
- ..........
- String modelName = (String) entry.getKey();
- Object modelValue = entry.getValue();
- if (modelValue != null) {
- request.setAttribute(modelName, modelValue);
- ...........
- }
- else {
- request.removeAttribute(modelName);
- .......
- }
- }
- }
protected void exposeModelAsRequestAttributes(Map model, HttpServletRequest request) throws Exception {
Iterator it = model.entrySet().iterator();
while (it.hasNext()) {
Map.Entry entry = (Map.Entry) it.next();
..........
String modelName = (String) entry.getKey();
Object modelValue = entry.getValue();
if (modelValue != null) {
request.setAttribute(modelName, modelValue);
...........
}
else {
request.removeAttribute(modelName);
.......
}
}
}
讓我們回到數據處理部分的exposeHelper();這是一個模板方法,其實現在JstlView中實現:
- public class JstlView extends InternalResourceView {
- private MessageSource jstlAwareMessageSource;
- protected void initApplicationContext() {
- super.initApplicationContext();
- this.jstlAwareMessageSource =
- JstlUtils.getJstlAwareMessageSource(getServletContext(), getApplicationContext());
- }
- protected void exposeHelpers(HttpServletRequest request) throws Exception {
- JstlUtils.exposeLocalizationContext(request, this.jstlAwareMessageSource);
- }
- }
public class JstlView extends InternalResourceView {
private MessageSource jstlAwareMessageSource;
protected void initApplicationContext() {
super.initApplicationContext();
this.jstlAwareMessageSource =
JstlUtils.getJstlAwareMessageSource(getServletContext(), getApplicationContext());
}
protected void exposeHelpers(HttpServletRequest request) throws Exception {
JstlUtils.exposeLocalizationContext(request, this.jstlAwareMessageSource);
}
}
在JstlUtils中包含了對於其他而言jstl特殊的數據處理和設置。
過程是不是很長?我們現在在哪裏了?呵呵,我們剛剛完成的事MVC中View的render,對於InternalResourceView的render 過程比較簡單只是完成一個資源的重定向處理。需要做的就是得到實際view的internalResource路徑,然後轉發到那個資源中去。怎樣得到資源的路徑呢通過調用:
- protected String prepareForRendering(HttpServletRequest request, HttpServletResponse response)
- throws Exception {
- return getUrl();
protected String prepareForRendering(HttpServletRequest request, HttpServletResponse response)
throws Exception {
return getUrl();
}
那這個url在哪裏生成呢?我們在View相關的代碼中沒有找到,實際上,他在ViewRosolve的時候就生成了,在UrlBasedViewResolver中:
- protected AbstractUrlBasedView buildView(String viewName) throws Exception {
- AbstractUrlBasedView view = (AbstractUrlBasedView) BeanUtils.instantiateClass(getViewClass());
- view.setUrl(getPrefix() + viewName + getSuffix());
- String contentType = getContentType();
- if (contentType != null) {
- view.setContentType(contentType);
- }
- view.setRequestContextAttribute(getRequestContextAttribute());
- view.setAttributesMap(getAttributesMap());
- return view;
- }
protected AbstractUrlBasedView buildView(String viewName) throws Exception {
AbstractUrlBasedView view = (AbstractUrlBasedView) BeanUtils.instantiateClass(getViewClass());
view.setUrl(getPrefix() + viewName + getSuffix());
String contentType = getContentType();
if (contentType != null) {
view.setContentType(contentType);
}
view.setRequestContextAttribute(getRequestContextAttribute());
view.setAttributesMap(getAttributesMap());
return view;
}
這裏是生成View的地方,自然也把生成的url和其他一些和view相關的屬性也配置好了。
那這個ViewResolve是什麼時候被調用的呢?哈哈,我們這樣又要回到DispatcherServlet中去看看究竟,在DispatcherServlet中:
- protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response)
- throws Exception {
- ........
- View view = null;
- // 這裏設置視圖名爲默認的名字
- if (!mv.hasView()) {
- mv.setViewName(getDefaultViewName(request));
- }
- if (mv.isReference()) {
- //這裏對視圖名進行解析,在解析的過程中根據需要生成實際需要的視圖對象。
- view = resolveViewName(mv.getViewName(), mv.getModelInternal(), locale, request);
- ..........
- }
- ......
- }
protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response)
throws Exception {
........
View view = null;
// 這裏設置視圖名爲默認的名字
if (!mv.hasView()) {
mv.setViewName(getDefaultViewName(request));
}
if (mv.isReference()) {
//這裏對視圖名進行解析,在解析的過程中根據需要生成實際需要的視圖對象。
view = resolveViewName(mv.getViewName(), mv.getModelInternal(), locale, request);
..........
}
......
}
下面是對視圖名進行解析的具體過程:
- protected View resolveViewName(String viewName, Map model, Locale locale, HttpServletRequest request)
- throws Exception {
- //我們有可能不止一個視圖解析器
- for (Iterator it = this.viewResolvers.iterator(); it.hasNext();) {
- ViewResolver viewResolver = (ViewResolver) it.next();
- //這裏是視圖解析器進行解析並生成視圖的過程。
- View view = viewResolver.resolveViewName(viewName, locale);
- if (view != null) {
- return view;
- }
- }
- return null;
- }
protected View resolveViewName(String viewName, Map model, Locale locale, HttpServletRequest request)
throws Exception {
//我們有可能不止一個視圖解析器
for (Iterator it = this.viewResolvers.iterator(); it.hasNext();) {
ViewResolver viewResolver = (ViewResolver) it.next();
//這裏是視圖解析器進行解析並生成視圖的過程。
View view = viewResolver.resolveViewName(viewName, locale);
if (view != null) {
return view;
}
}
return null;
}
這裏調用具體的ViewResolver對視圖的名字進行解析 - 除了單純的解析之外,它還根據我們的要求生成了我們實際需要的視圖對象。具體的viewResolver在bean定義文件中進行定義同時在 initViewResolver()方法中被初始化到viewResolver變量中,我們看看具體的 InternalResourceViewResolver是怎樣對視圖名進行處理的並生成V視圖對象的:對resolveViewName的調用模板在 AbstractCachingViewResolver中,
- public View resolveViewName(String viewName, Locale locale) throws Exception {
- //如果沒有打開緩存設置,那創建需要的視圖
- if (!isCache()) {
- logger.warn("View caching is SWITCHED OFF -- DEVELOPMENT SETTING ONLY: This can severely impair performance");
- return createView(viewName, locale);
- }
- else {
- Object cacheKey = getCacheKey(viewName, locale);
- // No synchronization, as we can live with occasional double caching.
- synchronized (this.viewCache) {
- //這裏查找緩存裏的視圖對象
- View view = (View) this.viewCache.get(cacheKey);
- if (view == null) {
- //如果在緩存中沒有找到,創建一個並把創建的放到緩存中去
- view = createView(viewName, locale);
- this.viewCache.put(cacheKey, view);
- ........
- }
- return view;
- }
- }
- }
public View resolveViewName(String viewName, Locale locale) throws Exception {
//如果沒有打開緩存設置,那創建需要的視圖
if (!isCache()) {
logger.warn("View caching is SWITCHED OFF -- DEVELOPMENT SETTING ONLY: This can severely impair performance");
return createView(viewName, locale);
}
else {
Object cacheKey = getCacheKey(viewName, locale);
// No synchronization, as we can live with occasional double caching.
synchronized (this.viewCache) {
//這裏查找緩存裏的視圖對象
View view = (View) this.viewCache.get(cacheKey);
if (view == null) {
//如果在緩存中沒有找到,創建一個並把創建的放到緩存中去
view = createView(viewName, locale);
this.viewCache.put(cacheKey, view);
........
}
return view;
}
}
}
關於這些createView(),loadView(),buildView()的關係,我們看看Eclipse裏的call hiearchy
然後我們回到view.render中完成數據的最終對httpResponse的寫入,比如在AbstractExcelView中的實現:
- protected final void renderMergedOutputModel(
- Map model, HttpServletRequest request, HttpServletResponse response) throws Exception {
- .........
- // response.setContentLength(workbook.getBytes().length);
- response.setContentType(getContentType());
- ServletOutputStream out = response.getOutputStream();
- workbook.write(out);
- out.flush();
- }
protected final void renderMergedOutputModel(
Map model, HttpServletRequest request, HttpServletResponse response) throws Exception {
.........
// response.setContentLength(workbook.getBytes().length);
response.setContentType(getContentType());
ServletOutputStream out = response.getOutputStream();
workbook.write(out);
out.flush();
}
這樣就和我們前面的分析一致起來了:DispatcherServlet在解析視圖名的時候就根據要求生成了視圖對象,包括在InternalResourceView中需要使用的url和其他各種和HTTP response相關的屬性都會寫保持在生成的視圖對象中,然後就直接調用視圖對象的render來完成數據的展示。
這就是整個Spring Web MVC框架的大致流程,整個MVC流程由DispatcherServlet來控制。MVC的關鍵過程包括:
配置到handler的映射關係和怎樣根據請求參數得到對應的handler,在Spring中,這是由handlerMapping通過執行鏈來完成的,而具體的映射關係我們在bean定義文件中定義並在HandlerMapping載入上下文的時候就被配置好了。然後 DispatcherServlet調用HandlerMapping來得到對應的執行鏈,最後通過視圖來展現模型數據,但我們要注意的是視圖對象是在解析視圖名的時候生成配置好的。這些作爲核心類的HanderMapping,ViewResolver,View,Handler的緊密協作實現了MVC的功能。