前端控制器
前端控制器,即所謂的Front Controller,展現的是設計模式中的前端控制器模式。前端控制器處理所有從使用者過來的請求。所有使用者的請求都要通過前端控制器。SpringMVC架構和其他請求驅動的表示層架構一樣,也是圍繞一個将請求分發到相應控制器的核心Servlet來設計的。DispatcherServlet和其他架構中的Servlet不一樣的地方在于,它和Spring容器無縫整合在了一起,是以你可以在SpringMVC中使用Spring容器所有的特性。
DispatcherServlet這個前端控制器,在SpringMVC中的作用,以官方文檔中的配圖來說明:

整個流程可以被大緻描述為:一個http請求到達伺服器,被DispatcherServlet接收。DispatcherServlet将請求委派給合适的處理器Controller,此時處理控制權到達Controller對象。Controller内部完成請求的資料模型的建立和業務邏輯的處理,然後再将填充了資料後的模型即model和控制權一并交還給DispatcherServlet,委派DispatcherServlet來渲染響應。DispatcherServlet再将這些資料和适當的資料模版視圖結合,向Response輸出響應。
DispatcherServlet
SpringMVC完成初始化流程之後,就進入Servlet标準生命周期的第二個階段,即“service”階段。在“service”階段中,每一次Http請求到來,容器都會啟動一個請求線程,通過service()方法,委派到doGet()或者doPost()這些方法,完成Http請求的處理。
在初始化流程中,SpringMVC巧妙的運用依賴注入讀取參數,并最終建立一個與容器上下文相關聯的Spring子上下文。這個子上下文,就像Struts2中xwork容器一樣,為接下來的Http處理流程中各種程式設計元素提供了容身之所。如果說将Spring上下文關聯到Servlet容器中,是SpringMVC架構的第一個亮點,那麼在請求轉發流程中,SpringMVC對各種處理環節程式設計元素的抽象,就是另外一個獨具匠心的亮點。
Struts2采取的是一種完全和Web容器隔離和解耦的事件機制。諸如Action對象、Result對象、Interceptor對象,這些都是完全脫離Servlet容器的程式設計元素。Struts2将資料流和事件處理完全剝離開來,從Http請求中讀取資料後,下面的事件處理流程就隻依賴于這些資料,而完全不知道有Web環境的存在。
反觀SpringMVC,無論HandlerMapping對象、HandlerAdapter對象還是View對象,這些核心的接口所定義的方法中,HttpServletRequest和HttpServletResponse對象都是直接作為方法的參數出現的。這也就意味着,架構的設計者,直接将SpringMVC架構和容器綁定到了一起。或者說,整個SpringMVC架構,都是依托着Servlet容器元素來設計的。下面就來看一下,源碼中是如何展現這一點的。
請求轉發的入口
就像任何一個注冊在容器中的Servlet一樣,DispatcherServlet也是通過自己的service()方法來接收和轉發Http請求到具體的doGet()或doPost()這些方法的。以一次典型的GET請求為例,經過HttpServlet基類中service()方法的委派,請求會被轉發到doGet()方法或者doPost()方法中。
protected voidservice(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String method=req.getMethod();longlastModified;if (method.equals("GET")) {
lastModified= this.getLastModified(req);if (lastModified == -1L) {this.doGet(req, resp);
}else{long ifModifiedSince = req.getDateHeader("If-Modified-Since");if (ifModifiedSince
}else{
resp.setStatus(304);
}
}
}else if (method.equals("HEAD")) {
lastModified= this.getLastModified(req);this.maybeSetLastModified(resp, lastModified);this.doHead(req, resp);
}else if (method.equals("POST")) {this.doPost(req, resp);
}else if (method.equals("PUT")) {this.doPut(req, resp);
}else if (method.equals("DELETE")) {this.doDelete(req, resp);
}else if (method.equals("OPTIONS")) {this.doOptions(req, resp);
}else if (method.equals("TRACE")) {this.doTrace(req, resp);
}else{
String errMsg= lStrings.getString("http.method_not_implemented");
Object[] errArgs= newObject[]{method};
errMsg=MessageFormat.format(errMsg, errArgs);
resp.sendError(501, errMsg);
}
}
doGet() 和 doPost() 方法,在DispatcherServlet的父類FrameworkServlet類中被覆寫。
protected final voiddoGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {this.processRequest(request, response);
}protected final voiddoPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {this.processRequest(request, response);
}
可以看到,這裡隻是簡單的轉發到processRequest()這個方法。
protected final voidprocessRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {long startTime =System.currentTimeMillis();
Throwable failureCause= null;//Expose current LocaleResolver and request as LocaleContext.
LocaleContext previousLocaleContext =LocaleContextHolder.getLocaleContext();
LocaleContextHolder.setLocaleContext(buildLocaleContext(request),this.threadContextInheritable);//Expose current RequestAttributes to current thread.
RequestAttributes previousRequestAttributes =RequestContextHolder.getRequestAttributes();
ServletRequestAttributes requestAttributes= null;if (previousRequestAttributes == null || previousRequestAttributes.getClass().equals(ServletRequestAttributes.class)) {
requestAttributes= newServletRequestAttributes(request);
RequestContextHolder.setRequestAttributes(requestAttributes,this.threadContextInheritable);
}if(logger.isTraceEnabled()) {
logger.trace("Bound request context to thread:" +request);
}try{
doService(request, response);
}finally{//Clear request attributes and reset thread-bound context.
LocaleContextHolder.setLocaleContext(previousLocaleContext, this.threadContextInheritable);if (requestAttributes != null) {
RequestContextHolder.setRequestAttributes(previousRequestAttributes,this.threadContextInheritable);
requestAttributes.requestCompleted();
}if (this.publishEvents) {//Whether or not we succeeded, publish an event.
long processingTime = System.currentTimeMillis() -startTime;this.webApplicationContext.publishEvent(new ServletRequestHandledEvent(this,
request.getRequestURI(), request.getRemoteAddr(),
request.getMethod(), getServletConfig().getServletName(),
WebUtils.getSessionId(request), getUsernameForRequest(request),
processingTime, failureCause));
}
}
}
可以看到,processRequest()方法隻是做了一些線程安全的隔離,真正的請求處理,發生在doService()方法中。點開FrameworkServlet類中的doService()方法。
protected abstract voiddoService(HttpServletRequest request, HttpServletResponse response)
throws Exception;
又是一個抽象方法,這也是SpringMVC類設計中的慣用伎倆:父類抽象處理流程,子類給予具體的實作。真正的實作是在DispatcherServlet類中。
讓我們接着看DispatcherServlet類中實作的doService()方法。
@Overrideprotected voiddoService(HttpServletRequest request, HttpServletResponse response) throws Exception {if(logger.isDebugEnabled()) {
String requestUri=urlPathHelper.getRequestUri(request);
logger.debug("DispatcherServlet with name '" + getServletName() + "' processing" + request.getMethod() +
"request for [" + requestUri + "]");
}//Keep a snapshot of the request attributes in case of an include,//to be able to restore the original attributes after the include.
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("org.springframework.web.servlet")) {
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());
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,newFlashMap());
request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE,this.flashMapManager);try{
doDispatch(request, response);
}finally{//Restore the original attribute snapshot, in case of an include.
if (attributesSnapshot != null) {
restoreAttributesAfterInclude(request, attributesSnapshot);
}
}
}
幾個requet.setAttribute()方法的調用,将前面在初始化流程中執行個體化的對象設定到http請求的屬性中,供下一步處理使用,其中有容器的上下文對象、本地化解析器等SpringMVC特有的程式設計元素。不同于Struts2中的ValueStack,SpringMVC的資料并沒有從HttpServletRequest對象中抽離出來再存進另外一個程式設計元素,這也跟SpringMVC的設計思想有關。因為從一開始,SpringMVC的設計者就認為,不應該将請求處理過程和Web容器完全隔離。
是以,你可以看到,真正發生請求轉發的方法doDispatch()中,它的參數是HttpServletRequest和HttpServletResponse對象。這給我們傳遞的意思也很明确,從request中能擷取到一切請求的資料,從response中,我們又可以往伺服器端輸出任何響應,Http請求的處理,就應該圍繞這兩個對象來設計。我們不妨可以将SpringMVC這種設計方案,是從Struts2的過度設計中吸取教訓,而向Servlet程式設計的一種回歸和簡化。
而對請求的處理交給doDispatcher方法
protected voiddoDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest=request;
HandlerExecutionChain mappedHandler= null;
boolean multipartRequestParsed= false;
WebAsyncManager asyncManager=WebAsyncUtils.getAsyncManager(request);try{
ModelAndView mv= null;
Exception dispatchException= null;try{//處理檔案上傳
processedRequest =checkMultipart(request);
multipartRequestParsed= (processedRequest !=request);//決定目前請求的Handler
mappedHandler =getHandler(processedRequest);if (mappedHandler == null || mappedHandler.getHandler() == null) {
noHandlerFound(processedRequest, response);return;
}//決定目前請求的HandlerAdapter
HandlerAdapter ha =getHandlerAdapter(mappedHandler.getHandler());//處理last-modified請求頭
String method =request.getMethod();
boolean isGet= "GET".equals(method);if (isGet || "HEAD".equals(method)) {long lastModified =ha.getLastModified(request, mappedHandler.getHandler());if(logger.isDebugEnabled()) {
logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is:" +lastModified);
}if (new ServletWebRequest(request, response).checkNotModified(lastModified) &&isGet) {return;
}
}//攔截器的前置處理
if (!mappedHandler.applyPreHandle(processedRequest, response)) {return;
}//Handler實際執行請求
mv =ha.handle(processedRequest, response, mappedHandler.getHandler());if(asyncManager.isConcurrentHandlingStarted()) {return;
}//設定預設視圖名
applyDefaultViewName(processedRequest, mv);//攔截器後置處理
mappedHandler.applyPostHandle(processedRequest, response, mv);
}catch(Exception ex) {
dispatchException=ex;
}catch(Throwable err) {//As of 4.3, we're processing Errors thrown from handler methods as well,//making them available for @ExceptionHandler methods and other scenarios.
dispatchException = new NestedServletException("Handler dispatch failed", err);
}//選擇視圖并渲染視圖
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}catch(Exception ex) {
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}catch(Throwable err) {
triggerAfterCompletion(processedRequest, response, mappedHandler,new NestedServletException("Handler processing failed", err));
}finally{if(asyncManager.isConcurrentHandlingStarted()) {//Instead of postHandle and afterCompletion
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
}else{//Clean up any resources used by a multipart request.
if(multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}
先看doDispatcher方法執行的主要操作時序圖
請求路由
getHandler方法就是從HandlerMapping中查詢比對目前request的Handler。我們看到隻要一比對上 handler 就不再循環,直接傳回
protectedHandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {for (HandlerMapping hm : this.handlerMappings) {
HandlerExecutionChain handler=hm.getHandler(request);if (handler != null) {returnhandler;
}
}return null;
}
HandlerMapping的getHandler方法在抽象基類AbstractHandlerMapping
publicfinal HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {//由子類根據request擷取Handler
Object handler =getHandlerInternal(request);//如果沒比對到,則擷取預設Handler
if (handler == null) {
handler=getDefaultHandler();
}if (handler == null) {return null;
}//如果傳回的Handler為String,則使用Spring容器執行個體化
if(handler instanceof String) {
String handlerName=(String) handler;
handler=getApplicationContext().getBean(handlerName);
}//查詢比對的攔截器,組裝Handler生成HandlerExecutionChain
HandlerExecutionChain executionChain =getHandlerExecutionChain(handler, request);if(CorsUtils.isCorsRequest(request)) {
CorsConfiguration globalConfig= this.corsConfigSource.getCorsConfiguration(request);
CorsConfiguration handlerConfig=getCorsConfiguration(handler, request);
CorsConfiguration config= (globalConfig != null ?globalConfig.combine(handlerConfig) : handlerConfig);
executionChain=getCorsHandlerExecutionChain(request, executionChain, config);
}returnexecutionChain;
}
最終傳回的Handler是由攔截器鍊和Handler共同組成的,而具體比對Handler的方法是交給子類來完成的。上一章元件初始化中提到生産環境下使用的是RequestMappingHandlerMapping,getHandlerInternal方法的實作在它的基類AbstractHandlerMethodMapping。
protectedHandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {//從request擷取比對url
String lookupPath =getUrlPathHelper().getLookupPathForRequest(request);if(logger.isDebugEnabled()) {
logger.debug("Looking up handler method for path" +lookupPath);
}this.mappingRegistry.acquireReadLock();try{//查詢比對的HandlerMethod
HandlerMethod handlerMethod =lookupHandlerMethod(lookupPath, request);if(logger.isDebugEnabled()) {if (handlerMethod != null) {
logger.debug("Returning handler method [" + handlerMethod + "]");
}else{
logger.debug("Did not find handler method for [" + lookupPath + "]");
}
}return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
}finally{this.mappingRegistry.releaseReadLock();
}
}
可以看到傳回的Handler的類型為HandlerMethod,它對應于Controller中的方法。上一章也提過,在AbstractHandlerMethodMapping中有一個MappingRegistry,統一管理URL和Controller方法的映射關系,lookupHandlerMethod就是對MappingRegistry的操作。
protectedHandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
List matches = new ArrayList();//從mappingRegistry擷取比對到的RequestMappingInfo
List directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);if (directPathMatches != null) {
addMatchingMappings(directPathMatches, matches, request);
}if(matches.isEmpty()) {//No choice but to go through all mappings...
addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);
}//對比對項進行排序
if (!matches.isEmpty()) {
Comparator comparator = newMatchComparator(getMappingComparator(request));
Collections.sort(matches, comparator);if(logger.isTraceEnabled()) {
logger.trace("Found" + matches.size() + "matching mapping(s) for [" +lookupPath+ "] :" +matches);
}
Match bestMatch= matches.get(0);if (matches.size() > 1) {if(CorsUtils.isPreFlightRequest(request)) {returnPREFLIGHT_AMBIGUOUS_MATCH;
}
Match secondBestMatch= matches.get(1);if (comparator.compare(bestMatch, secondBestMatch) == 0) {
Method m1=bestMatch.handlerMethod.getMethod();
Method m2=secondBestMatch.handlerMethod.getMethod();throw new IllegalStateException("Ambiguous handler methods mapped for HTTP path '" +request.getRequestURL()+ "': {" + m1 + "," + m2 + "}");
}
}
handleMatch(bestMatch.mapping, lookupPath, request);returnbestMatch.handlerMethod;
}else{//無比對項處理
return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
}
}
通過mappingRegistry比對傳回RequestMappingInfo,對應于每個有@RequestMapping注解解析後的Method。
我們來看看,HandlerExecutionChain類的代碼。
package org.springframework.web.servlet;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.springframework.util.CollectionUtils;public classHandlerExecutionChain {privatefinal Object handler;privateHandlerInterceptor[] interceptors;private ListinterceptorList;publicHandlerExecutionChain(Object handler) {this(handler, null);
}publicHandlerExecutionChain(Object handler, HandlerInterceptor[] interceptors) {if(handler instanceof HandlerExecutionChain) {
HandlerExecutionChain originalChain=(HandlerExecutionChain) handler;this.handler =originalChain.getHandler();this.interceptorList = new ArrayList();
CollectionUtils.mergeArrayIntoCollection(originalChain.getInterceptors(),this.interceptorList);
CollectionUtils.mergeArrayIntoCollection(interceptors,this.interceptorList);
}else{this.handler =handler;this.interceptors =interceptors;
}
}publicObject getHandler() {return this.handler;
}public voidaddInterceptor(HandlerInterceptor interceptor) {
initInterceptorList();this.interceptorList.add(interceptor);
}public voidaddInterceptors(HandlerInterceptor[] interceptors) {if (interceptors != null) {
initInterceptorList();this.interceptorList.addAll(Arrays.asList(interceptors));
}
}private voidinitInterceptorList() {if (this.interceptorList == null) {this.interceptorList = new ArrayList();
}if (this.interceptors != null) {this.interceptorList.addAll(Arrays.asList(this.interceptors));this.interceptors = null;
}
}publicHandlerInterceptor[] getInterceptors() {if (this.interceptors == null && this.interceptorList != null) {this.interceptors = this.interceptorList.toArray(new HandlerInterceptor[this.interceptorList.size()]);
}return this.interceptors;
}
}
一個攔截器清單,一個執行對象,這個類的内容十分的簡單,它蘊含的設計思想,卻十分的豐富。
1.攔截器組成的清單,在執行對象被調用的前後,會依次執行。這裡可以看成是一個的AOP環繞通知,攔截器可以對處理對象随心所欲的進行處理和增強。這裡明顯是吸收了Struts2中攔截器的設計思想。這種AOP環繞式的擴充點設計,也幾乎成為所有架構必備的内容。
2.實際的處理對象,即handler對象,是由Object對象來引用的。
private final Object handler;
當我們拿到HandlerExecutionChain,就完成了request到Controller的路由操作。
擴充卡比對
有了Handler後,需要合适的HandlerAdapter對其進行操作,因而就要根據Handler進行比對。
protectedHandlerAdapter getHandlerAdapter(Object handler) throws ServletException {for (HandlerAdapter ha : this.handlerAdapters) {if(logger.isTraceEnabled()) {
logger.trace("Testing handler adapter [" + ha + "]");
}if(ha.supports(handler)) {returnha;
}
}throw new ServletException("No adapter for handler [" + handler +
"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}
HandlerAdapter接口中定義了supports方法,用于檢測是否支援Handler。生産環境使用的RequestMappingHandlerAdapter在其基類AbstractHandlerMethodAdapter中實作了supports方法。
publicfinal boolean supports(Object handler) {return (handler instanceof HandlerMethod &&supportsInternal((HandlerMethod) handler));
}
supportsInternal方法在RequestMappingHandlerAdapter的實作裡預設傳回true。因而RequestMappingHandlerAdapter就是用來支援類型為HandlerMethod的Handler的處理的。
攔截器處理
在SpringMVC中的攔截器接口HandlerInterceptor中定義了三個方法
public interfaceHandlerInterceptor {//在Handler找到後,執行前攔截
boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception;//在Handler執行後,視圖渲染前攔截
voidpostHandle(
HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
throws Exception;//請求處理完成,視圖渲染後執行資源清理等
voidafterCompletion(
HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception;
}
可以很清晰地對應到doDispatcher方法中。需要注意的有幾點
前置處理preHandle,傳回值為boolean。如果傳回true,則執行下一個,如果傳回false,則認為目前攔截器完成了請求,DispatcherServlet會直接傳回,在傳回前會調用所有攔截器的afterCompletion方法,完成清理工作。
afterCompletion方法在遇到任何情況時都需要被執行,無論是成功傳回還是抛出異常。
執行請求
HandlerAdapter的handle方法完成請求的真正執行。在AbstractHandlerMethodAdapter中由handleInternal執行。
protectedModelAndView handleInternal(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ModelAndView mav;
checkRequest(request);//執行HandlerMethod
mav =invokeHandlerMethod(request, response, handlerMethod);//處理緩存
if (!response.containsHeader(HEADER_CACHE_CONTROL)) {if(getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
applyCacheSeconds(response,this.cacheSecondsForSessionAttributeHandlers);
}else{
prepareResponse(response);
}
}returnmav;
}
在invokeHandlerMethod中,HandlerMethod被封裝ServletInvocableHandlerMethod,包裹上方法執行需要的資訊。
protectedModelAndView invokeHandlerMethod(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ServletWebRequest webRequest= newServletWebRequest(request, response);try{
WebDataBinderFactory binderFactory=getDataBinderFactory(handlerMethod);
ModelFactory modelFactory=getModelFactory(handlerMethod, binderFactory);//封裝HandlerMethod
ServletInvocableHandlerMethod invocableMethod =createInvocableHandlerMethod(handlerMethod);
invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
invocableMethod.setDataBinderFactory(binderFactory);
invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
ModelAndViewContainer mavContainer= newModelAndViewContainer();
mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
modelFactory.initModel(webRequest, mavContainer, invocableMethod);
mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);//異步請求處理
AsyncWebRequest asyncWebRequest =WebAsyncUtils.createAsyncWebRequest(request, response);
asyncWebRequest.setTimeout(this.asyncRequestTimeout);
WebAsyncManager asyncManager=WebAsyncUtils.getAsyncManager(request);
asyncManager.setTaskExecutor(this.taskExecutor);
asyncManager.setAsyncWebRequest(asyncWebRequest);
asyncManager.registerCallableInterceptors(this.callableInterceptors);
asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);if(asyncManager.hasConcurrentResult()) {
Object result=asyncManager.getConcurrentResult();
mavContainer= (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];
asyncManager.clearConcurrentResult();if(logger.isDebugEnabled()) {
logger.debug("Found concurrent result value [" + result + "]");
}
invocableMethod=invocableMethod.wrapConcurrentResult(result);
}//執行處理
invocableMethod.invokeAndHandle(webRequest, mavContainer);if(asyncManager.isConcurrentHandlingStarted()) {return null;
}//封裝資料和視圖
returngetModelAndView(mavContainer, modelFactory, webRequest);
}finally{
webRequest.requestCompleted();
}
}
再到ServletInvocableHandlerMethod的invokeAndHandle方法
public voidinvokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {//執行request
Object returnValue =invokeForRequest(webRequest, mavContainer, providedArgs);
setResponseStatus(webRequest);if (returnValue == null) {if (isRequestNotModified(webRequest) || getResponseStatus() != null ||mavContainer.isRequestHandled()) {
mavContainer.setRequestHandled(true);return;
}
}else if(StringUtils.hasText(getResponseStatusReason())) {
mavContainer.setRequestHandled(true);return;
}
mavContainer.setRequestHandled(false);try{//對傳回值進行處理
this.returnValueHandlers.handleReturnValue(
returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
}catch(Exception ex) {if(logger.isTraceEnabled()) {
logger.trace(getReturnValueHandlingErrorMessage("Error handling return value", returnValue), ex);
}throwex;
}
}publicObject invokeForRequest(NativeWebRequest request, ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {//執行方法參數
Object[] args =getMethodArgumentValues(request, mavContainer, providedArgs);if(logger.isTraceEnabled()) {
logger.trace("Invoking '" + ClassUtils.getQualifiedMethodName(getMethod(), getBeanType()) +
"' with arguments" +Arrays.toString(args));
}
Object returnValue=doInvoke(args);if(logger.isTraceEnabled()) {
logger.trace("Method [" + ClassUtils.getQualifiedMethodName(getMethod(), getBeanType()) +
"] returned [" + returnValue + "]");
}returnreturnValue;
}protectedObject doInvoke(Object... args) throws Exception {
ReflectionUtils.makeAccessible(getBridgedMethod());returngetBridgedMethod().invoke(getBean(), args);
}
需要說明的一點是方法執行完成的傳回值通過傳回值處理器HandlerMethodReturnValueHandler進行處理。在RequestMappingHandlerAdapter的初始化中,内置了衆多的HandlerMethodReturnValueHandler來處理多種類型的傳回值。
在完成請求執行後,doDispatcher方法中做了一個預設View的設定。
applyDefaultViewName(processedRequest, mv);private voidapplyDefaultViewName(HttpServletRequest request, ModelAndView mv) throws Exception {if (mv != null && !mv.hasView()) {
mv.setViewName(getDefaultViewName(request));
}
}
而這個getDefaultViewName是通過RequestToViewNameTranslator的實作類來解析的
protectedString getDefaultViewName(HttpServletRequest request) throws Exception {return this.viewNameTranslator.getViewName(request);
}
預設實作DefaultRequestToViewNameTranslator,根據配置的一些通用url進行比對
public發表于 2019-07-16 13:20
閱讀 ( 333 )
推薦
收藏