前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >SpringMVC源码解析(二)

SpringMVC源码解析(二)

作者头像
Java学习录
发布2019-09-08 14:24:26
4260
发布2019-09-08 14:24:26
举报
文章被收录于专栏:Java学习录Java学习录

注意,读完本篇文章需要很长很长时间

在上篇文章SpringMVC源码解析(一)中,我们搭建了一个SpringBoot的启动demo,分析了SpringBoot中SpringMVC的自动配置原理以及DispatcherServlet的初始化流程。本篇文章就分析一次请求在SpringMVC中的处理流程

在日常开发中,我们最常用的请求方式大概就是Get和Post了,Tomcat或者Jetty等web服务器在接受到请求后会调用到DispatcherServlet对应的方法

代码语言:javascript
复制
@Override
protected final void doGet(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
   processRequest(request, response);
}

@Override
protected final void doPost(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
   processRequest(request, response);
}

可以看到其实最终都是调用的同一个方法

代码语言:javascript
复制
protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
//记录开始时间
   long startTime = System.currentTimeMillis();
   Throwable failureCause = null;
//记录当前线程的信息
   LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
   LocaleContext localeContext = buildLocaleContext(request);

   RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
   ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);

   WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
   asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());

   initContextHolders(request, localeContext, requestAttributes);

   try {
//核心处理,往下看
      doService(request, response);
   }
   catch (ServletException | IOException ex) {
      failureCause = ex;
      throw ex;
   }
   catch (Throwable ex) {
      failureCause = ex;
      throw new NestedServletException("Request processing failed", ex);
   }

   finally {//清除线程绑定信息
      resetContextHolders(request, previousLocaleContext, previousAttributes);
      if (requestAttributes != null) {
         requestAttributes.requestCompleted();
      }

      if (logger.isDebugEnabled()) {
         if (failureCause != null) {
            this.logger.debug("Could not complete request", failureCause);
         }
         else {
            if (asyncManager.isConcurrentHandlingStarted()) {
               logger.debug("Leaving response open for concurrent processing");
            }
            else {
               this.logger.debug("Successfully completed request");
            }
         }
      }
//发送事件通知
      publishRequestHandledEvent(request, response, startTime, failureCause);
   }
}


protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
   if (logger.isDebugEnabled()) {
      String resumed = WebAsyncUtils.getAsyncManager(request).hasConcurrentResult() ? " resumed" : "";
      logger.debug("DispatcherServlet with name '" + getServletName() + "'" + resumed +
            " processing " + request.getMethod() + " request for [" + getRequestUri(request) + "]");
   }

   // Keep a snapshot of the request attributes in case of an include,
   // to be able to restore the original attributes after the include.
   Map<String, Object> attributesSnapshot = null;
   if (WebUtils.isIncludeRequest(request)) {
      attributesSnapshot = new HashMap<>();
      Enumeration<?> attrNames = request.getAttributeNames();
      while (attrNames.hasMoreElements()) {
         String attrName = (String) attrNames.nextElement();
         if (this.cleanupAfterInclude || attrName.startsWith(DEFAULT_STRATEGIES_PREFIX)) {
            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());

   if (this.flashMapManager != null) {
      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()) {
         // Restore the original attribute snapshot, in case of an include.
         if (attributesSnapshot != null) {
            restoreAttributesAfterInclude(request, attributesSnapshot);
         }
      }
   }
}

可以看到上方的大段代码都是做的一些准备工作,具体的逻辑接着往下看吧,这个核心流程都在下面这个方法里了

代码语言:javascript
复制
protected void doDispatch(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);

         		// 1.获取对应的handler
			    mappedHandler = getHandler(processedRequest);
				if (mappedHandler == null) {
				    //如果没有获取到对应的handler则往response中写入错误信息
					noHandlerFound(processedRequest, response);
					return;
				}

				// 2. 获取对应的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;
				}

				// 3.调用handle
				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);
			}
		  	//4. 处理返回结果
			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);
				}
			}
		}
	}
1. 获取handler
代码语言:javascript
复制
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
		if (this.handlerMappings != null) {
		  //遍历所有的handlerMapping,这里的handlemapping就是初始化阶段构造的三个
			for (HandlerMapping hm : this.handlerMappings) {
				if (logger.isTraceEnabled()) {
					logger.trace(
							"Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");
				}
			  //这里调用具体的handler,哪个handler能够处理就直接返回
				HandlerExecutionChain handler = hm.getHandler(request);
				if (handler != null) {
					return handler;
				}
			}
		}
		return null;
	}

public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
//1. 调用具体的实现去获取handler
   Object handler = getHandlerInternal(request);
//如果为空使用默认的
   if (handler == null) {
      handler = getDefaultHandler();
   }
//没有默认的返回空
   if (handler == null) {
      return null;
   }
   // 尝试通过BeanName去获取handler
   if (handler instanceof String) {
      String handlerName = (String) handler;
      handler = obtainApplicationContext().getBean(handlerName);
   }
//2. 获取handler执行链
   HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
   if (CorsUtils.isCorsRequest(request)) {
      CorsConfiguration globalConfig = this.globalCorsConfigSource.getCorsConfiguration(request);
      CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
      CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
      executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
   }
   return executionChain;
}
1. 获取具体的handler

这里以AbstractUrlHandlerMapping为例解读一下,顾明思议,这个类是根据请求url获取响应的handler的

代码语言:javascript
复制
protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
//截取url
   String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
//根据url寻找handler
   Object handler = lookupHandler(lookupPath, request);
   if (handler == null) {
      // 如果请求路径为/则使用RootHandler
      Object rawHandler = null;
      if ("/".equals(lookupPath)) {
         rawHandler = getRootHandler();
      }
      if (rawHandler == null) {
//使用默认
         rawHandler = getDefaultHandler();
      }
      if (rawHandler != null) {
         // 根据beanName尝试获取Handler
         if (rawHandler instanceof String) {
            String handlerName = (String) rawHandler;
            rawHandler = obtainApplicationContext().getBean(handlerName);
         }//校验
         validateHandler(rawHandler, request);
         handler = buildPathExposingHandler(rawHandler, lookupPath, lookupPath, null);
      }
   }
   if (handler != null && logger.isDebugEnabled()) {
      logger.debug("Mapping [" + lookupPath + "] to " + handler);
   }
   else if (handler == null && logger.isTraceEnabled()) {
      logger.trace("No handler mapping found for [" + lookupPath + "]");
   }
   return handler;
}


protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception {
   // 直接根据url匹配
   Object handler = this.handlerMap.get(urlPath);
   if (handler != null) {
      // Bean name or resolved handler?
      if (handler instanceof String) {
         String handlerName = (String) handler;
         handler = obtainApplicationContext().getBean(handlerName);
      }
      validateHandler(handler, request);
//封装执行链
      return buildPathExposingHandler(handler, urlPath, urlPath, null);
   }

   // 正则匹配
   List<String> matchingPatterns = new ArrayList<>();
   for (String registeredPattern : this.handlerMap.keySet()) {
      if (getPathMatcher().match(registeredPattern, urlPath)) {
         matchingPatterns.add(registeredPattern);
      }
      else if (useTrailingSlashMatch()) {
         if (!registeredPattern.endsWith("/") && getPathMatcher().match(registeredPattern + "/", urlPath)) {
            matchingPatterns.add(registeredPattern +"/");
         }
      }
   }

   String bestMatch = null;
   Comparator<String> patternComparator = getPathMatcher().getPatternComparator(urlPath);
   if (!matchingPatterns.isEmpty()) {
      matchingPatterns.sort(patternComparator);
      if (logger.isDebugEnabled()) {
         logger.debug("Matching patterns for request [" + urlPath + "] are " + matchingPatterns);
      }
      bestMatch = matchingPatterns.get(0);
   }
   if (bestMatch != null) {
      handler = this.handlerMap.get(bestMatch);
      if (handler == null) {
         if (bestMatch.endsWith("/")) {
            handler = this.handlerMap.get(bestMatch.substring(0, bestMatch.length() - 1));
         }
         if (handler == null) {
            throw new IllegalStateException(
                  "Could not find handler for best pattern match [" + bestMatch + "]");
         }
      }
      // Bean name or resolved handler?
      if (handler instanceof String) {
         String handlerName = (String) handler;
         handler = obtainApplicationContext().getBean(handlerName);
      }
      validateHandler(handler, request);
      String pathWithinMapping = getPathMatcher().extractPathWithinPattern(bestMatch, 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<>();
      for (String matchingPattern : matchingPatterns) {
         if (patternComparator.compare(bestMatch, matchingPattern) == 0) {
            Map<String, String> vars = getPathMatcher().extractUriTemplateVariables(matchingPattern, urlPath);
            Map<String, String> decodedVars = getUrlPathHelper().decodePathVariables(request, vars);
            uriTemplateVariables.putAll(decodedVars);
         }
      }
      if (logger.isDebugEnabled()) {
         logger.debug("URI Template variables for request [" + urlPath + "] are " + uriTemplateVariables);
      }
      return buildPathExposingHandler(handler, bestMatch, pathWithinMapping, uriTemplateVariables);
   }

   // No handler found...
   return null;
}
2. 封装执行链

当获取到相应的handler后,查看是否存在拦截器,如果存在的话则加入执行链中

代码语言:javascript
复制
protected Object buildPathExposingHandler(Object rawHandler, String bestMatchingPattern,
      String pathWithinMapping, @Nullable Map<String, String> uriTemplateVariables) {

   HandlerExecutionChain chain = new HandlerExecutionChain(rawHandler);
   chain.addInterceptor(new PathExposingHandlerInterceptor(bestMatchingPattern, pathWithinMapping));
   if (!CollectionUtils.isEmpty(uriTemplateVariables)) {
      chain.addInterceptor(new UriTemplateVariablesHandlerInterceptor(uriTemplateVariables));
   }
   return chain;
}
2. 获取handlerAdpter

根据handler获取匹配的handlerAdpter

代码语言:javascript
复制
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
   if (this.handlerAdapters != null) {
      for (HandlerAdapter ha : this.handlerAdapters) {
         if (logger.isTraceEnabled()) {
            logger.trace("Testing handler adapter [" + ha + "]");
         }
         //不同的handlerAdapter的判断方法不同
         if (ha.supports(handler)) {
            return ha;
         }
      }
   }
   throw new ServletException("No adapter for handler [" + handler +
         "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}
以SimpleControllerHandlerAdapter为例,判断是否实现Controller接口
public boolean supports(Object handler) {
   return (handler instanceof Controller);
}
3. 执行请求
代码语言:javascript
复制
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
      throws Exception {

   return ((Controller) handler).handleRequest(request, response);
}
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response)
      throws Exception {

   if (HttpMethod.OPTIONS.matches(request.getMethod())) {
      response.setHeader("Allow", getAllowHeader());
      return null;
   }

   // Delegate to WebContentGenerator for checking and preparing.
   checkRequest(request);
   prepareResponse(response);

   // 如果需要同步session
   if (this.synchronizeOnSession) {
      HttpSession session = request.getSession(false);
      if (session != null) {
         Object mutex = WebUtils.getSessionMutex(session);
         synchronized (mutex) {
            return handleRequestInternal(request, response);
         }
      }
   }
调用Controller方法
   return handleRequestInternal(request, response);
}
4.处理返回结果
代码语言:javascript
复制
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
      @Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
      @Nullable 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);
      }
   }

        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);
		}
	} 
   if (mv != null && !mv.wasCleared()) {

页面跳转的逻辑

代码语言:javascript
复制
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 != null ? this.localeResolver.resolveLocale(request) : request.getLocale());
   response.setLocale(locale);

   View view;
   String viewName = mv.getViewName();
   if (viewName != null) {
      //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 {
      // No need to lookup: the ModelAndView object contains the actual View object.
      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.isDebugEnabled()) {
      logger.debug("Rendering view [" + view + "] in DispatcherServlet with name '" + getServletName() + "'");
   }
   try {
      if (mv.getStatus() != null) {
         response.setStatus(mv.getStatus().value());
      }//2.跳转
      view.render(mv.getModelInternal(), request, response);
   }
   catch (Exception ex) {
      if (logger.isDebugEnabled()) {
         logger.debug("Error rendering view [" + view + "] in DispatcherServlet with name '" +
               getServletName() + "'", ex);
      }
      throw ex;
   }
}
解析视图名称
代码语言:javascript
复制
protected View resolveViewName(String viewName, @Nullable Map<String, Object> model,
      Locale locale, HttpServletRequest request) throws Exception {

   if (this.viewResolvers != null) {
      for (ViewResolver viewResolver : this.viewResolvers) {
         View view = viewResolver.resolveViewName(viewName, locale);
         if (view != null) {
            return view;
         }
      }
   }
   return null;
}
页面跳转

具体的跳转逻辑是根据当前使用的渲染引擎决定的,比如html、jsp、Thymeleaf等,这里简单 列举一个Thymeleaf的逻辑吧

代码语言:javascript
复制
public void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
       this.renderFragment(this.markupSelectors, model, request, response);
   }

   protected void renderFragment(Set<String> markupSelectorsToRender, Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
       ServletContext servletContext = this.getServletContext();
       String viewTemplateName = this.getTemplateName();
       ISpringTemplateEngine viewTemplateEngine = this.getTemplateEngine();
       if (viewTemplateName == null) {
           throw new IllegalArgumentException("Property 'templateName' is required");
       } else if (this.getLocale() == null) {
           throw new IllegalArgumentException("Property 'locale' is required");
       } else if (viewTemplateEngine == null) {
           throw new IllegalArgumentException("Property 'templateEngine' is required");
       } else {
           Map<String, Object> mergedModel = new HashMap(30);
           Map<String, Object> templateStaticVariables = this.getStaticVariables();
           if (templateStaticVariables != null) {
               mergedModel.putAll(templateStaticVariables);
           }

           if (pathVariablesSelector != null) {
               Map<String, Object> pathVars = (Map)request.getAttribute(pathVariablesSelector);
               if (pathVars != null) {
                   mergedModel.putAll(pathVars);
               }
           }

           if (model != null) {
               mergedModel.putAll(model);
           }

           ApplicationContext applicationContext = this.getApplicationContext();
           RequestContext requestContext = new RequestContext(request, response, this.getServletContext(), mergedModel);
           SpringWebMvcThymeleafRequestContext thymeleafRequestContext = new SpringWebMvcThymeleafRequestContext(requestContext, request);
           addRequestContextAsVariable(mergedModel, "springRequestContext", requestContext);
           addRequestContextAsVariable(mergedModel, "springMacroRequestContext", requestContext);
           mergedModel.put("thymeleafRequestContext", thymeleafRequestContext);
           ConversionService conversionService = (ConversionService)request.getAttribute(ConversionService.class.getName());
           ThymeleafEvaluationContext evaluationContext = new ThymeleafEvaluationContext(applicationContext, conversionService);
           mergedModel.put("thymeleaf::EvaluationContext", evaluationContext);
           IEngineConfiguration configuration = viewTemplateEngine.getConfiguration();
           WebExpressionContext context = new WebExpressionContext(configuration, request, response, servletContext, this.getLocale(), mergedModel);
           String templateName;
           Set markupSelectors;
           if (!viewTemplateName.contains("::")) {
               templateName = viewTemplateName;
               markupSelectors = null;
           } else {
               IStandardExpressionParser parser = StandardExpressions.getExpressionParser(configuration);

               FragmentExpression fragmentExpression;
               try {
                   fragmentExpression = (FragmentExpression)parser.parseExpression(context, "~{" + viewTemplateName + "}");
               } catch (TemplateProcessingException var24) {
                   throw new IllegalArgumentException("Invalid template name specification: '" + viewTemplateName + "'");
               }

               ExecutedFragmentExpression fragment = FragmentExpression.createExecutedFragmentExpression(context, fragmentExpression);
               templateName = FragmentExpression.resolveTemplateName(fragment);
               markupSelectors = FragmentExpression.resolveFragments(fragment);
               Map<String, Object> nameFragmentParameters = fragment.getFragmentParameters();
               if (nameFragmentParameters != null) {
                   if (fragment.hasSyntheticParameters()) {
                       throw new IllegalArgumentException("Parameters in a view specification must be named (non-synthetic): '" + viewTemplateName + "'");
                   }

                   context.setVariables(nameFragmentParameters);
               }
           }

           String templateContentType = this.getContentType();
           Locale templateLocale = this.getLocale();
           String templateCharacterEncoding = this.getCharacterEncoding();
           Set processMarkupSelectors;
           if (markupSelectors != null && markupSelectors.size() > 0) {
               if (markupSelectorsToRender != null && markupSelectorsToRender.size() > 0) {
                   throw new IllegalArgumentException("A markup selector has been specified (" + Arrays.asList(markupSelectors) + ") for a view that was already being executed as a fragment (" + Arrays.asList(markupSelectorsToRender) + "). Only one fragment selection is allowed.");
               }

               processMarkupSelectors = markupSelectors;
           } else if (markupSelectorsToRender != null && markupSelectorsToRender.size() > 0) {
               processMarkupSelectors = markupSelectorsToRender;
           } else {
               processMarkupSelectors = null;
           }

           response.setLocale(templateLocale);
           if (!this.getForceContentType()) {
               String computedContentType = SpringContentTypeUtils.computeViewContentType(request, templateContentType != null ? templateContentType : "text/html;charset=ISO-8859-1", templateCharacterEncoding != null ? Charset.forName(templateCharacterEncoding) : null);
               response.setContentType(computedContentType);
           } else {
               if (templateContentType != null) {
                   response.setContentType(templateContentType);
               } else {
                   response.setContentType("text/html;charset=ISO-8859-1");
               }

               if (templateCharacterEncoding != null) {
                   response.setCharacterEncoding(templateCharacterEncoding);
               }
           }

           viewTemplateEngine.process(templateName, processMarkupSelectors, context, response.getWriter());
       }
   }
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-09-05,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Java学习录 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. 获取handler
    • 1. 获取具体的handler
      • 2. 封装执行链
      • 2. 获取handlerAdpter
      • 3. 执行请求
      • 4.处理返回结果
        • 解析视图名称
          • 页面跳转
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档