前言
從DispatchServlet可以看出整個(gè)SpringMVC流程十艾,關(guān)于過濾器(filter),servelet,攔截器(interceptor)的執(zhí)行流程如下如
使用到的組件
過濾器Filter:對(duì)請(qǐng)求信息進(jìn)行校驗(yàn),處理,例如判斷是否有token存璃,或者向請(qǐng)求添加請(qǐng)求頭,請(qǐng)求參數(shù)等
新增filter的兩種方式:
@WebFilter(urlPatterns = "/*",filterName = "myFilter") // 指定filter的處理路徑和雕拼,filter名稱 @Component public class MyFilter implements Filter { @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { servletRequest.setAttribute("userId","zhangsan"); MDC.put("traceId", IdUtil.simpleUUID()); //log.error("過濾器doFilter---請(qǐng)求添加請(qǐng)求數(shù)據(jù)userId"); filterChain.doFilter(servletRequest,servletResponse); } }
@Bean public FilterRegistrationBean<MyFilter> myFilter(){ final FilterRegistrationBean<MyFilter> registrationBean = new FilterRegistrationBean<>(); final MyFilter myFilter = new MyFilter(); registrationBean.setFilter(myFilter); registrationBean.setUrlPatterns(Arrays.asList("/*")); registrationBean.setName("myFilter"); return registrationBean; }
servlet:serlvet是處理請(qǐng)求的入口纵东,DispatchServlet繼承自Servlet,所有的請(qǐng)求都會(huì)走DispatchServlet啥寇,并且內(nèi)部添加了對(duì)Rest風(fēng)格的請(qǐng)求處理邏輯
添加普通servlet的兩種方式
@WebServlet(urlPatterns = "/*",name = "myServlet") @Component public class MyServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { super.doGet(req, resp); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { super.doPost(req, resp); } @Override protected void doDelete(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { super.doDelete(req, resp); } }
@Bean public ServletRegistrationBean<MyServlet> myServlet(){ final ServletRegistrationBean<MyServlet> registrationBean = new ServletRegistrationBean<>(); final MyServlet myServlet = new MyServlet(); registrationBean.setServlet(myServlet); registrationBean.setUrlMappings(Arrays.asList("/*")); registrationBean.setName("myServlet"); return registrationBean; }
自定義DIspatchServlet:
@Component("dispatcherServlet")
public class MyDispatcherServlet extends DispatcherServlet {
@Override
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
log.error("請(qǐng)求調(diào)用----:"+request.getRequestURL());
super.doService(request, response);
}
}
攔截器interceptor:在方法執(zhí)行前后偎球,結(jié)束時(shí)進(jìn)行處理
新增攔截器的2種方式:
@Configuration @EnableWebMvc // 這種方式會(huì)導(dǎo)致springSecurity的一些默認(rèn)配置失效 public class WebMvcConfigByConfigure implements WebMvcConfigurer{ @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new MyInterceptor()); } }
-
@Configuration public class WebMvcConfigBySupport extends DelegatingWebMvcConfiguration { @Override protected void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new MyInterceptor()); } }
注意:當(dāng)使用第一種@EnableWebMvc+WebMvcConfigurer方式進(jìn)行實(shí)現(xiàn)時(shí),會(huì)導(dǎo)致SpringSecurity的默認(rèn)配置失效辑甜,建議使用第二種衰絮,或者兩者結(jié)合使用,兩者結(jié)合時(shí)磷醋,需要將
@EnableWebMvc進(jìn)行注釋猫牡,這樣Spring會(huì)將所有的配置進(jìn)行合并
處理器映射HandlerMapping:將程序中定義的映射記錄,會(huì)根據(jù)請(qǐng)求來判斷是否使用某個(gè)映射邓线,即根據(jù)請(qǐng)求獲取到需要調(diào)用的方法淌友,目前默認(rèn)有三個(gè)煌恢,
在添加了@RequestMapping和@GetMapping等等注解的方法,會(huì)被注冊(cè)到HandlerMapping的mappingRegistry屬性中震庭,請(qǐng)求進(jìn)來瑰抵,會(huì)根據(jù)請(qǐng)求來匹配指定的方法
處理器適配器handlerAdapter:上一步找到了需要執(zhí)行的處理器,處理器適配器就是用來執(zhí)行處理器的器联,目前有4個(gè)
方法參數(shù)處理器MethodArgumentResolver:方法的參數(shù)如何進(jìn)行解析二汛,例如實(shí)現(xiàn)@RequestBody和@RequestParam等注解的功能(案例:自定義@CurrentUser注解,通過解析請(qǐng)求頭token來映射方法參數(shù)中的用戶信息)
案例實(shí)現(xiàn):
public class CurrentUserMethodArgumentResolver implements HandlerMethodArgumentResolver {
/**
* 該處理器是否能處理這個(gè)參數(shù)
* @param methodParameter
* @return
*/
@Override
public boolean supportsParameter(MethodParameter methodParameter) {
return methodParameter.hasParameterAnnotation(CurrentUser.class);
}
/**
* 如何處理參數(shù)
* @param methodParameter
* @param modelAndViewContainer
* @param nativeWebRequest
* @param webDataBinderFactory
* @return
* @throws Exception
*/
@Override
public Object resolveArgument(MethodParameter methodParameter, ModelAndViewContainer modelAndViewContainer, NativeWebRequest nativeWebRequest, WebDataBinderFactory webDataBinderFactory) throws Exception {
final String token = nativeWebRequest.getHeader("token");
SysUser user= UserService.getUserFromToken(token);
return user;
}
}
@Configuration
public class WebMvcConfig extends WebMvcConfigurationSupport {
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
// 注冊(cè)參數(shù)處理器
argumentResolvers.add(new CurrentUserMethodArgumentResolver());
}
}
返回值處理器HandlerMethodReturnValueHandler:對(duì)于請(qǐng)求方法的返回值進(jìn)行處理
創(chuàng)建返回值處理器的方式
public class MyReturnHandlerResolver implements HandlerMethodReturnValueHandler {
@Override
public boolean supportsReturnType(MethodParameter methodParameter) {
return methodParameter.hasMethodAnnotation(ParseReturn.class);
}
@Override
public void handleReturnValue(Object o, MethodParameter methodParameter, ModelAndViewContainer modelAndViewContainer, NativeWebRequest nativeWebRequest) throws Exception {
//o就是返回值拨拓,根據(jù)業(yè)務(wù)情況進(jìn)行處理肴颊,例如對(duì)一個(gè)字段加減等等
}
}
@Override
protected void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> returnValueHandlers) {
returnValueHandlers.add(new MyReturnHandlerResolver());
}
響應(yīng)體處理器RequestBodyAdvice:對(duì)于最后的數(shù)據(jù)進(jìn)行處理,定義如何向response寫出數(shù)據(jù)
實(shí)現(xiàn)RequestBodyAdvice的方式(可以多個(gè)渣磷,內(nèi)部最終合并為一個(gè)RequestBodyAdviceChain苫昌,處理器鏈):
@ControllerAdvice
@ResponseBody
public class UnityExceptionHandler implements ResponseBodyAdvice<Object> {
@ExceptionHandler(Exception.class)
public Response<String> exception(Exception e){
return Response.fail(e.getMessage());
}
// 是否進(jìn)行處理,根據(jù)自己的業(yè)務(wù)邏輯進(jìn)行判斷
@Override
public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
return true;
}
// 真正的處理邏輯幸海,如果上一步返回true,這一步進(jìn)行處理
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
if (body instanceof Response){
return body;
}else {
return Response.success(body);
}
}
}
視圖解析器ViewResolver:例如像thymeleaf奥务,freemaker 都實(shí)現(xiàn)了該接口物独,如何進(jìn)行視圖的渲染
文件請(qǐng)求解析器MultipartResolver:如果屬于文件上傳類的接口會(huì)使用到該解析器
代碼分析
DispatchServlet家族管理圖如下
調(diào)用鏈路為:Servlet.service()->GenericServlet.service()->HttpServlet.service()->FrameWorkServlet.doGet()/doPost()...->FrameWorkServlet.processRequest()->DispatcherServlet.doService()->DispatcherServlet.doDispatch() 不管get還是post或其他請(qǐng)求最終都會(huì)調(diào)用processRequest方法,然后調(diào)用doService方法氯葬,最后是doDispatch方法挡篓,所以重點(diǎn)在doDispatch方法的邏輯。
doDispatch:
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
// 是否為文件上傳的請(qǐng)求標(biāo)識(shí)
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
// 是否文件上傳請(qǐng)求的判斷帚称,如果是文件上傳請(qǐng)求官研,將普通Request轉(zhuǎn)換為MultipartHttpServletRequest對(duì)象
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// 通過HandlerMapping獲取處理方法,查看【分析1】.
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
// 如果沒有處理的方法闯睹,返回異常戏羽,也就是我們看見的No mapping for GET '/user/get/123'
noHandlerFound(processedRequest, response);
return;
}
// 根據(jù)處理方法獲取該處理方法的適配器
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// Process last-modified header, if supported by the handler.
String method = request.getMethod();
boolean isGet = HttpMethod.GET.matches(method);
if (isGet || HttpMethod.HEAD.matches(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
// 調(diào)用攔截器鏈中的開始方法,查看【分析2】
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// 使用適配器調(diào)用處理方法楼吃,返回ModelAndView對(duì)象作為處理結(jié)果始花,查看【分析4】
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
// 根據(jù)返回的ModelAndView對(duì)象判斷是否需要設(shè)置默認(rèn)視圖,查看【分析5】
applyDefaultViewName(processedRequest, mv);
// 調(diào)用所有攔截器的postHandle方法孩锡,查看【分析6】
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);
}
// 根據(jù)modelAndView進(jìn)行結(jié)果處理酷宵,查看【分析7】
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
// 異常結(jié)束也會(huì)調(diào)用攔截器的完成方法,查看【分析3】
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);
}
}
}
}
-
分析1:最終調(diào)用AbstractHandlerMethodMapping.getHandlerInternal()方法
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception { // 獲取請(qǐng)求的路徑 String lookupPath = initLookupPath(request); this.mappingRegistry.acquireReadLock(); try { // 通過請(qǐng)求路徑從mappingRegistry獲取出處理該請(qǐng)求的方法躬窜,所有的可以處理請(qǐng)求的方法在項(xiàng)目啟動(dòng)時(shí)都會(huì)注冊(cè)到mappingRegistry HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request); return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null); } finally { this.mappingRegistry.releaseReadLock(); } }
-
分析2:
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception { for (int i = 0; i < this.interceptorList.size(); i++) { HandlerInterceptor interceptor = this.interceptorList.get(i); // 遍歷所有的攔截器并調(diào)用preHandle方法 if (!interceptor.preHandle(request, response, this.handler)) { // 如果其中一個(gè)方法處理并攔截掉了浇垦,不再執(zhí)行后面的處理了,直接調(diào)用所有攔截器的完成方法 triggerAfterCompletion(request, response, null); return false; } this.interceptorIndex = i; } return true; }
-
分析3:調(diào)用所有攔截器的處理完成方法荣挨,很多地方有調(diào)用男韧,例如出現(xiàn)了異常朴摊,都會(huì)回調(diào)攔截器的處理完成方法,注意:反向遍歷
void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex) { for (int i = this.interceptorIndex; i >= 0; i--) { HandlerInterceptor interceptor = this.interceptorList.get(i); try { // 調(diào)用所有攔截器的處理完成方法 interceptor.afterCompletion(request, response, this.handler, ex); } catch (Throwable ex2) { logger.error("HandlerInterceptor.afterCompletion threw exception", ex2); } } }
-
分析4:會(huì)調(diào)用RequestMappingHandlerAdapter.invokeHandlerMethod()方法
@Nullable protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception { ServletWebRequest webRequest = new ServletWebRequest(request, response); try { WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod); ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory); ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod); // 獲取所有的方法參數(shù)處理器(可以將請(qǐng)求傳遞的參數(shù)煌抒,封裝為java對(duì)象等等) if (this.argumentResolvers != null) { invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers); } // 獲取所有的返回結(jié)果處理器仍劈,(針對(duì)返回結(jié)果進(jìn)行處理) if (this.returnValueHandlers != null) { invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers); } ......省略代碼 // 調(diào)用方法,并對(duì)參數(shù)和結(jié)果進(jìn)行處理寡壮,返回結(jié)果放在mavConatiner里面贩疙,查看【分析4-1】 invocableMethod.invokeAndHandle(webRequest, mavContainer); if (asyncManager.isConcurrentHandlingStarted()) { return null; } // 獲取ModelAndView對(duì)象,查看【分析4-2】 return getModelAndView(mavContainer, modelFactory, webRequest); } finally { webRequest.requestCompleted(); } }
-
分析4-1:
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception { // 反射調(diào)用方法况既,獲取最原始的返回結(jié)果 Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs); setResponseStatus(webRequest); if (returnValue == null) { if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) { disableContentCachingIfNecessary(webRequest); mavContainer.setRequestHandled(true); return; } } else if (StringUtils.hasText(getResponseStatusReason())) { mavContainer.setRequestHandled(true); return; } // 這個(gè)值很重要这溅,表示是否已經(jīng)處理了請(qǐng)求,例如直接返回的json數(shù)據(jù)時(shí)值為true棒仍,因?yàn)樵诜祷刂堤幚砥鲿r(shí)已經(jīng)將請(qǐng)求結(jié)果寫入response悲靴,返回視圖時(shí),值為false mavContainer.setRequestHandled(false); Assert.state(this.returnValueHandlers != null, "No return value handlers"); try { // 篩選出一個(gè)返回值處理器莫其,使用返回值處理器處理返回結(jié)果癞尚,注意只有一個(gè)最合適的處理器進(jìn)行處理,并且該處理器的處理方法中會(huì)根據(jù)自身情況選擇設(shè)置mavContainer.setRequestHandled(false/true);表示自己是否處理返回結(jié)果乱陡,例如加了@ResponseBody的方法內(nèi)部就會(huì)將值設(shè)置為true浇揩,并且調(diào)用所有的RequestBodyAdvice鏈再次處理請(qǐng)求結(jié)果,最終結(jié)果直接寫入到Response憨颠,后續(xù)就不用再使用視圖解析器了 this.returnValueHandlers.handleReturnValue( returnValue, getReturnValueType(returnValue), mavContainer, webRequest); } catch (Exception ex) { if (logger.isTraceEnabled()) { logger.trace(formatErrorForReturnValue(returnValue), ex); } throw ex; } }
-
分析4-2:將上一步的ModelAndViewContainer容器中獲取modelAndVIew對(duì)象進(jìn)行返回
@Nullable private ModelAndView getModelAndView(ModelAndViewContainer mavContainer, ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception { modelFactory.updateModel(webRequest, mavContainer); // 重點(diǎn)胳徽,根據(jù)這個(gè)標(biāo)記值判斷是否已經(jīng)處理了返回請(qǐng)求,如果已經(jīng)處理了爽彤,返回modelAndView為null养盗,外面就不會(huì)再調(diào)用viewResoler進(jìn)行視圖解析了 if (mavContainer.isRequestHandled()) { return null; } // 如果沒有處理返回請(qǐng)求,使用ModelAndViewContainer封裝一個(gè)ModelAndView進(jìn)行返回 ModelMap model = mavContainer.getModel(); ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, mavContainer.getStatus()); if (!mavContainer.isViewReference()) { mav.setView((View) mavContainer.getView()); } if (model instanceof RedirectAttributes) { Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes(); HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class); if (request != null) { RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes); } } return mav; }
-
-
分析5:
private void applyDefaultViewName(HttpServletRequest request, @Nullable ModelAndView mv) throws Exception { // 當(dāng)mv不為空&&mv沒有視圖時(shí)适篙,當(dāng)我們自己的代碼邏輯返回String值時(shí)往核,該ModeAndView的view就為返回值,hasView方法的值就為true嚷节,結(jié)束執(zhí)行铆铆,但是當(dāng)返回值為null,或者方法為void時(shí)丹喻,hasView方法會(huì)返回false薄货,就會(huì)執(zhí)行下面的方法,默認(rèn)將請(qǐng)求路徑作為視圖 if (mv != null && !mv.hasView()) { String defaultViewName = getDefaultViewName(request); if (defaultViewName != null) { mv.setViewName(defaultViewName); } } } //例如: /** * 如果請(qǐng)求/template/test 則mv.hasView = true碍论,因?yàn)関iew = test * 如果請(qǐng)求/template/test2 則mv.hasView = false,因?yàn)関iew = null,然后執(zhí)行g(shù)etDefaultViewName谅猾,則mv.viewName = /template/test2(請(qǐng)求路徑) * 請(qǐng)求/template/test3效果與請(qǐng)求/template/test2效果一致 **/ @Controller @RequestMapping("/template") public class TemplateController { @GetMapping("/test") public String test(ModelAndView mv){ mv.addObject("name","張三"); mv.addObject("age",18); return "test"; } @GetMapping("/test2") public String test2(ModelAndView mv){ mv.addObject("name","張三"); mv.addObject("age",18); return null; } @GetMapping("/test3") public void test3(ModelAndView mv){ mv.addObject("name","張三"); mv.addObject("age",18); } }
-
分析6:反向遍歷所有攔截器,依次調(diào)用postHandle方法
for (int i = this.interceptorList.size() - 1; i >= 0; i--) { HandlerInterceptor interceptor = this.interceptorList.get(i); interceptor.postHandle(request, response, this.handler, mv); }
-
分析7:處理異常和視圖解析
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response, @Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv, @Nullable Exception exception) throws Exception { boolean errorView = false; // 如果在執(zhí)行自定義邏輯方法時(shí)出現(xiàn)異常會(huì)執(zhí)行以下方法 if (exception != null) { if (exception instanceof ModelAndViewDefiningException) { logger.debug("ModelAndViewDefiningException encountered", exception); mv = ((ModelAndViewDefiningException) exception).getModelAndView(); } else { Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null); // 其他異常,調(diào)用異常處理器進(jìn)行處理税娜,查看【分析7-1】 mv = processHandlerException(request, response, handler, exception); errorView = (mv != null); } } // 當(dāng)mv不為空時(shí)才進(jìn)行視圖渲染坐搔,也可以看出,mavContainer.setRequestHandled(false);這個(gè)標(biāo)志位控制了是否渲染視圖 if (mv != null && !mv.wasCleared()) { // 渲染視圖敬矩,查看【分析7-2】 render(mv, request, response); if (errorView) { WebUtils.clearErrorRequestAttributes(request); } } else { if (logger.isTraceEnabled()) { logger.trace("No view rendering, null ModelAndView returned."); } } if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) { // Concurrent handling started during a forward return; } if (mappedHandler != null) { // 觸發(fā)所有攔截器的完成方法概行,查看【分析3】 mappedHandler.triggerAfterCompletion(request, response, null); } }
-
分析7-1:異常處理器處理請(qǐng)求,例如可以使用@ControllerAdvice+@ExceptionHandler進(jìn)行統(tǒng)一異常處理
@ControllerAdvice @ResponseBody public class MyExceptionHandler { @ExceptionHandler(RuntimeException.class) public String runtime(Exception e){ return e.getMessage(); } }
-
分析7-2:
protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception { // 國(guó)際化信息 Locale locale = (this.localeResolver != null ? this.localeResolver.resolveLocale(request) : request.getLocale()); response.setLocale(locale); View view; String viewName = mv.getViewName(); if (viewName != null) { // 視圖名稱不為空時(shí)使用所有的視圖解析器解析該名稱生成view對(duì)象弧岳,查看【分析7-2-1】 view = resolveViewName(viewName, mv.getModelInternal(), locale, request); if (view == null) { throw new ServletException("Could not resolve view with name '" + mv.getViewName() + "' in servlet with name '" + getServletName() + "'"); } } else { // 如果視圖名為空凳忙,則查看modelAndView中的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() + "'"); } } // Delegate to the View object for rendering. if (logger.isTraceEnabled()) { logger.trace("Rendering view [" + view + "] "); } try { if (mv.getStatus() != null) { response.setStatus(mv.getStatus().value()); } // 使用view和mv請(qǐng)求等信息進(jìn)行視圖渲染涧卵,將結(jié)果寫入到response view.render(mv.getModelInternal(), request, response); } catch (Exception ex) { if (logger.isDebugEnabled()) { logger.debug("Error rendering view [" + view + "]", ex); } throw ex; } }
-
分析7-2-1:
@Nullable protected View resolveViewName(String viewName, @Nullable Map<String, Object> model, Locale locale, HttpServletRequest request) throws Exception { if (this.viewResolvers != null) { // 調(diào)用所有的視圖解析器解析名稱,獲得view腹尖,所有的模板引擎也就是實(shí)現(xiàn)了viewResolver柳恐,使用自己的邏輯進(jìn)行視圖解析,例如freemaker默認(rèn)解析路徑為 classpath:/template下面后綴名為html的文件热幔,例如viewName為test,則找到的文件則是classpath:/template/test.html,然后將該文件解析為view對(duì)象返回 for (ViewResolver viewResolver : this.viewResolvers) { View view = viewResolver.resolveViewName(viewName, locale); if (view != null) { return view; } } } return null; }
-
-