SpringBoot--->>>web-->>视图解析原理

视图解析原理

以上述的redirect前缀的重定向视图为例,观察源码运行

首先debug,然后控制器方法打断点,开始快乐

一切请求先看DispatcherServlet

来到DispatcherServlet中

在doDispatch方法中

先是确定当前请求的处理方法

mappedHandler = this.getHandler(processedRequest);

然后接着往下看

再就是熟悉的获取请求适配器,从4个适配器里面找出来一个合适的,当前使用的注解是@RequestMapping,所以适配器是ha = {RequestMappingHandlerAdapter@6686}

HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());
接着来到这一行,处理请求的这一行

// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

进入,经过几次跳转会来到这里。熟悉的开始,进入

mav = invokeHandlerMethod(request, response, handlerMethod);

直到来到这,这里是处理请求和执行控制器方法的

Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
//第一行执行完会执行控制器方法
setResponseStatus(webRequest);

之后就拿到了控制器返回值,ReturnValue

再往下就是处理返回结果,这一句,进入

this.returnValueHandlers.handleReturnValue(
      returnValue, getReturnValueType(returnValue), mavContainer, webRequest);

会看见

//这一行是找返回值解析器
HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
​
//这一行是处理返回值
handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);

在selectHandler中

循环所有返回值解析器,找到能解析的那一个

private HandlerMethodReturnValueHandler selectHandler(@Nullable Object value, MethodParameter returnType) {
   boolean isAsyncValue = isAsyncReturnValue(value, returnType);
   for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) {
      if (isAsyncValue && !(handler instanceof AsyncHandlerMethodReturnValueHandler)) {
         continue;
      }
      if (handler.supportsReturnType(returnType)) {
         return handler;
      }
   }
   return null;
}
//找到了一个叫12 = {ViewNameMethodReturnValueHandler@7616} 的解析器,他的supportsReturnType
方法中有一条,只要返回值是字符串就能解析,所以解析器是它
    public boolean supportsReturnType(MethodParameter returnType) {
        Class<?> paramType = returnType.getParameterType();
        return (void.class == paramType || CharSequence.class.isAssignableFrom(paramType));
    }
​

然后在handleReturnValue中

public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
      ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
​
   if (returnValue instanceof CharSequence) {
       //在这里把返回值设置为mav的视图名,返回值是:redirect:/main
      String viewName = returnValue.toString();
      mavContainer.setViewName(viewName);
      if (isRedirectViewName(viewName)) {
         mavContainer.setRedirectModelScenario(true);
      }
   }
   else if (returnValue != null) {
      // should not happen
      throw new UnsupportedOperationException("Unexpected return type: " +
            returnType.getParameterType().getName() + " in method: " + returnType.getMethod());
   }
}

然后会结束一些方法的执行,然后会获取mav的内容,进入

return getModelAndView(mavContainer, modelFactory, webRequest)

会来到

private ModelAndView getModelAndView(ModelAndViewContainer mavContainer,
      ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception {
​
   modelFactory.updateModel(webRequest, mavContainer);
   if (mavContainer.isRequestHandled()) {
      return null;
   }
    //获取model值,没有就创建一个空的
   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;
}

返回值 ModelAndView [view="redirect:/main"; model={}]

然后就一路返回来到,这一句就执行完了,因为没有请求参数,处理的步骤不那么多。

mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

接下来,已经拿到了初步处理的mav,继续执行下去,会看见这一行,进入

processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);

会来到

// Did the handler return a view to render?
if (mv != null && !mv.wasCleared()) {
    //有mv,从这一行进入
   render(mv, request, response);
   if (errorView) {
      WebUtils.clearErrorRequestAttributes(request);
   }
}

来到render方法中,在方法上他的注中也说到这是处理请求的最后一个步骤,可能会根据名称解析视图

在 render方法中

protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
   // Determine locale for request and apply it to the response.
    //拿一些 国际化的内容确定编码格式什么的,执行下去后就能看见utf-8、zh_cn等等
   Locale locale =
         (this.localeResolver != null ? this.localeResolver.resolveLocale(request) : request.getLocale());
   response.setLocale(locale);
​
   View view;
   String viewName = mv.getViewName();
   if (viewName != null) {
      // We need to resolve the view name.
       //解析视图名,然后进入
       
      view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
........

在上面解析视图名时,用一个view对象接收。在View对象中

在进入View对象后,发现他是一个接口,里面就一个方法,方法中提供了model、request、response参数用来渲染对象。这说明View对象是经过渲染后,再进行getDispather或redirect的

​

​
void render(@Nullable Map<String, ?> model, HttpServletRequest request, HttpServletResponse response)
      throws Exception;

​

进入后

public View resolveViewName(String viewName, Locale locale) throws Exception {
   RequestAttributes attrs = RequestContextHolder.getRequestAttributes();
   Assert.state(attrs instanceof ServletRequestAttributes, "No current ServletRequestAttributes");
    //内容协商
   List<MediaType> requestedMediaTypes = getMediaTypes(((ServletRequestAttributes) attrs).getRequest());
   if (requestedMediaTypes != null) {
       //获取第一次解析后的初始的候选视图名,也可以进去看一下,在里面就是for循环视图解析器,看那个能解析就解析出来,然后放进候选数组中
      List<View> candidateViews = getCandidateViews(viewName, locale, requestedMediaTypes);
      View bestView = getBestView(candidateViews, requestedMediaTypes, attrs);
      if (bestView != null) {
         return bestView;
      }
   }

可以看见当前视图解析器有5个,而第一个内容协商视图解析器就能解析这个视图名,进入后

进入后发现在内容协商视图解析器中有其它4个解析器

 

 

再次进行for循环寻找解析视图名解析器,循环到【到每个视图解析器都应该进入看看是怎么匹配解析类型的】thymeleafViewResolver时,进入会发现在里面就创建了一个名字为RerurnValue的view对象,然后返回,在最后一个InternalResourceViewResolver也能解析这个返回值的视图名

这个createView方法中

protected View createView(final String viewName, final Locale locale) throws Exception {
.........
    // Process redirects (HTTP redirects)
    //通过判断viewName的开头是不是它指定的几种中的一种,是就返回一个View对象。当前的viewname为redirect:/main,符合条件
    
    默认值
    public static final String REDIRECT_URL_PREFIX = "redirect:";
    
    
    if (viewName.startsWith(REDIRECT_URL_PREFIX)) {
        vrlogger.trace("[THYMELEAF] View \"{}\" is a redirect, and will not be handled directly by ThymeleafViewResolver.", viewName);
        final String redirectUrl = viewName.substring(REDIRECT_URL_PREFIX.length(), viewName.length());
        final RedirectView view = new RedirectView(redirectUrl, isRedirectContextRelative(), isRedirectHttp10Compatible());
        return (View) getApplicationContext().getAutowireCapableBeanFactory().initializeBean(view, REDIRECT_URL_PREFIX);
        //最后返回一个view对象
    }
......
}

所以最后 返回了两个视图解析器,得到后就会去选择最佳匹配结果,进入best那一行

List<View> candidateViews = getCandidateViews(viewName, locale, requestedMediaTypes);
View bestView = getBestView(candidateViews, requestedMediaTypes, attrs);

会来到getBestView方法中,里面还是一个循环,确定最佳结果,在循环第一个结果,就是 thymeleafViewResolver解析出来的时候,判断是不是转发视图 ,直接返回true,所以返回的是thymeleaf解析的结果。

for (View candidateView : candidateViews) {
   if (candidateView instanceof SmartView) {
      SmartView smartView = (SmartView) candidateView;
      if (smartView.isRedirectView()) {
         return candidateView;
      }
   }
}

继续往下执行会来到这一行,渲染视图

view.render(mv.getModelInternal(), request, response);

进入后,这一行实现视图重定向,进入

renderMergedOutputModel(mergedModel, getRequestToExpose(request), response);

进入后

sendRedirect(request, response, targetUrl, this.http10Compatible);

再进入,先是判断有没有转发数据,然后直接重定向,简单吧

response.sendRedirect(encodedURL);

前缀为forword的,render的时候就用的是request.getDispatch方法

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值