处理器适配器关于Model的一些东西

116 阅读10分钟

来到获取handler对应的适配器,经过上一篇的讲解,我们知道这个handler就是HandlerMethod类型了

HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
   if (this.handlerAdapters != null) {
      for (HandlerAdapter adapter : this.handlerAdapters) {
         if (adapter.supports(handler)) {
            return adapter;
         }
      }
   }
   throw new ServletException("No adapter for handler [" + handler +
         "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}

我们点击supports可以看到:

image.png

public final boolean supports(Object handler) {
   return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));
}

第一个AbstractHandlerMethodAdapter是支持HandlerMethod 类型的,就这样,我们就得到了HandlerAdapter 适配器了;

image.png

然后下面就是我们关注mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

return handleInternal(request, response, (HandlerMethod) handler);

protected ModelAndView handleInternal(HttpServletRequest request,
      HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
//先声明ModelAndView结果
   ModelAndView mav;

//检查请求是否被支持,包含两个逻辑:

//1.判断本实例中的supportedMethods是否包含请求方法,默认支持所有请求方法,就是get,post这些method;

//2.如果配置的需要session属性为true,检查请求是否包含session,默认不需要session
   checkRequest(request);


//处理时是否对session加锁,默认为false
   // Execute invokeHandlerMethod in synchronized block if required.
   if (this.synchronizeOnSession) {

//获取session对象
      HttpSession session = request.getSession(false);

//如果session不为空
      if (session != null) {

//获取session中的锁对象
         Object mutex = WebUtils.getSessionMutex(session);

//加锁后执行调用处理器方法逻辑
         synchronized (mutex) {
            mav = invokeHandlerMethod(request, response, handlerMethod);
         }
      }
      else {
         // No HttpSession available -> no mutex necessary

//没有session,忽略加锁,直接执行调用处理器方法逻辑
         mav = invokeHandlerMethod(request, response, handlerMethod);
      }
   }
   else {
      // No synchronization on session demanded at all..

//配置的无需锁session,则直接调用处理器方法逻辑
      mav = invokeHandlerMethod(request, response, handlerMethod);
   }
//如果响应结果不包含缓存控制头
   if (!response.containsHeader(HEADER_CACHE_CONTROL)) {

//如果该处理器方法包含SessionAttribute
      if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {

//应用SessionAttribute的缓存策略,默认cacheSecondsForSessionAttributeHandlers为0,表示不缓存
         applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
      }
      else {

//不包含SessionAttribute,准备请求。内部逻辑应用配置的缓存策略。本适配器默认没有缓存策略,故所有请求都不返回缓存响应头
         prepareResponse(response);
      }
   }
//返回结果invokeHandlerMethod的执行结果;
   return mav;
}

 

从上面可以看到,重点就是invokeHandlerMethod方法:

protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
      HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {


//把请求与响应封装为一个ServletWebRequest对象,用于后续处理逻辑使用
   ServletWebRequest webRequest = new ServletWebRequest(request, response);
   try {

//1.准备组件工厂

//获取处理器方法对应WebDataBinderFactory工厂,该工厂用于获取处理器方法对应的WebDataBinder组件
      WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);

//调用getModelFactory方法获取当前处理器方法对应的Model工厂,该工厂用于获取处理器方法对应的Model
      ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);


//2.准备可调用处理器方法实例

//创建一个Servlet下可调用处理器方法,该方法内部直接使用new ServletInvocableHandle(handleMethod)创建实例
      ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);

//本组件的参数解析器不为空,则为创建的可执行方法设置参数解析器,初始化时参数解析器会被初始化,所以这里不为null ->这里的初始化是指这个RequestMappingHandlerAdapter类,它实现了InitializingBean接口,会去执行afterPropertiesSet方法
      if (this.argumentResolvers != null) {
         invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
      }

//本组件的返回值处理器不为空,则为创建的可执行方法设置返回值处理器,初始化时返回值处理器会被初始化,所以这里不为null
      if (this.returnValueHandlers != null) {
         invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
      }

//设置可执行方法的DataBinder工厂,用于获取WebDataBinder实例
      invocableMethod.setDataBinderFactory(binderFactory);

//设置可执行方法的参数获得器,用于获取方法上的参数名
      invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);


//3.准备ModelAndView容器并初始化

//创建用于处理过程中使用的ModelAndView容器
      ModelAndViewContainer mavContainer = new ModelAndViewContainer();

//向当前ModelAndView容器的Model中添加输入FlashMap中的所有属性
      mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));

//初始化Model,包含调用Model相关的初始化方法,如@ModelAttribute注解标记的方法
      modelFactory.initModel(webRequest, mavContainer, invocableMethod);

//在重定向时忽略默认的Model属性值,只考虑重定向Model的属性值,默认为true
      mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);


//4.准备异步请求处理组件

//根据当前请求与响应创建异步请求
      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();
         LogFormatUtils.traceDebug(logger, traceOn -> {
            String formatted = LogFormatUtils.formatValue(result, !traceOn);
            return "Resume with async result [" + formatted + "]";
         });
         invocableMethod = invocableMethod.wrapConcurrentResult(result);
      }
//5.调用处理器方法并处理返回值
      invocableMethod.invokeAndHandle(webRequest, mavContainer);

//如果处理完成后,开启了异步请求并在处理中,说明返回值为一个异步的结果,直接返回null,等待异步结果返回,再执行上面的获取异步结果逻辑
      if (asyncManager.isConcurrentHandlingStarted()) {
         return null;
      }

//6.获取ModelAndView结果
      return getModelAndView(mavContainer, modelFactory, webRequest);
   }
   finally {

//标记请求完成
      webRequest.requestCompleted();
   }
}

1.准备组件工厂:

关于@InitBinder的一些用法:可以看下大佬写的blog.csdn.net/wang0907/ar…

private WebDataBinderFactory getDataBinderFactory(HandlerMethod handlerMethod) throws Exception {

//获取处理器方法所在的Bean类型
   Class<?> handlerType = handlerMethod.getBeanType();

//尝试从Initbinder缓存中获取当前处理器类型对应的所有标记了@InitBinder注解的方法
   Set<Method> methods = this.initBinderCache.get(handlerType);

//如果为空,说明缓存中还没有,进入获取逻辑
   if (methods == null) {

//找到处理器类型中所有标记了注解@InitBinder的方法
      methods = MethodIntrospector.selectMethods(handlerType, INIT_BINDER_METHODS);

//添加到缓存,以提高后续处理速度
      this.initBinderCache.put(handlerType, methods);
   }

//用于保存全部标记了@InitBinder方法的结果列表,标记了@InitBinder的方法会被封装为InvocableHandlerMethod可调用处理器方法,一遍调用该方法
   List<InvocableHandlerMethod> initBinderMethods = new ArrayList<>();
   // Global methods first

//全局@InitBinder方法放在结果列表前面,@ControllerAdvice标记的处理器增强Bean中的@InitBinder即为全局的

//遍历全局@InitBinder增强器缓存(缓存数据是来源于组件初始化时),controllerAdviceBean和methodSet是这个Bean中标记@InitBinder注解的方法的集合
   this.initBinderAdviceCache.forEach((controllerAdviceBean, methodSet) -> {

//先判断全局InitBinder是否被应用到当前处理器类型,判断依据是@ControllerAdvice注解中配置的一些属性
      if (controllerAdviceBean.isApplicableToBeanType(handlerType)) {

//如果可以被应用,则把增强器解析为Bean实例,为什么需要这个Bean?因为反射调用方法时需要
         Object bean = controllerAdviceBean.resolveBean();

//编辑集合
         for (Method method : methodSet) {

//把bean和method封装为InvocableHandlerMethod
            initBinderMethods.add(createInitBinderMethod(bean, method));
         }
      }
   });

//全局的添加完后,添加当前参数handlerMethod所在的Bean的@InitBinder方法
   for (Method method : methods) {
      Object bean = handlerMethod.getBean();
      initBinderMethods.add(createInitBinderMethod(bean, method));
   }

//创建WebDataBingder工厂
   return createDataBinderFactory(initBinderMethods);
}

类似上面@InitBinder的流程,这里时@ModelAttribute和@SessionAttributes的获取,@ControllerAdivce的也是为全局的

//获取Model工厂,传入参数为处理器方法与WebDataBinder工厂

private ModelFactory getModelFactory(HandlerMethod handlerMethod, WebDataBinderFactory binderFactory) {

//获取SessionAttributes的处理器,用于处理处理器Bean类型上标记的@SessionAttributes注解,方法里边有个handlerMethod.getBeanType
   SessionAttributesHandler sessionAttrHandler = getSessionAttributesHandler(handlerMethod);

//获取处理器Bean类型
   Class<?> handlerType = handlerMethod.getBeanType();

//从ModelAttribute缓存中获取Bean类型对应的@ModelAttribute标记的方法
   Set<Method> methods = this.modelAttributeCache.get(handlerType);
   if (methods == null) {

//若缓存中没有,则进入获取逻辑,获取处理器类型中标记了@ModelAttribute注解且未标记@RequestMapping注解的方法(在MODEL_ATTRIBUTE_METHODS中),作为ModelAttribute方法
      methods = MethodIntrospector.selectMethods(handlerType, MODEL_ATTRIBUTE_METHODS);

//放入缓存中,加快下次处理逻辑
      this.modelAttributeCache.put(handlerType, methods);
   }

//创建一个列表,用于保存@ModelAttribute注解标记方法的InvocableHandleMethod,可调用封装
   List<InvocableHandlerMethod> attrMethods = new ArrayList<>();
   // Global methods first

//全局的@ControllerAdvice标记的处理器增强中的@ModelAttribute方法优先

//遍历处理器增强Bean(被@ControllerAdvice标记的Bean)中的@ModelAttribute注解方法缓存,该缓存内容在组件初始化时填充,controllerAdviceBean是对应的处理器增强Bean的封装:ControllerAdviceBean,methodSet是这个处理器增强Bean中的全部@ModelAttribute标记的方法
   this.modelAttributeAdviceCache.forEach((controllerAdviceBean, methodSet) -> {

//通过@ControllerAdvice的属性进行过滤,判断是否可悲应用到当前处理器Bean中去
      if (controllerAdviceBean.isApplicableToBeanType(handlerType)) {

//解析处理器增强Bean为实例
         Object bean = controllerAdviceBean.resolveBean();
         for (Method method : methodSet) {

//遍历对应的方法的集合,创建ModelAttribute可调用方法,添加到结果列表中
            attrMethods.add(createModelAttributeMethod(binderFactory, bean, method));
         }
      }
   });

//全局@ModelAttribute方法添加完成后,添加当前处理器Bean中的@ModelAttribute方法
   for (Method method : methods) {
      Object bean = handlerMethod.getBean();
      attrMethods.add(createModelAttributeMethod(binderFactory, bean, method));
   }

//返回Model工厂实例,封装全部@ModelAttribute的可调用方法,WebDataBinder工厂与SessionAttrubutes处理器
   return new ModelFactory(attrMethods, binderFactory, sessionAttrHandler);
}

SessionAttributesHandler sessionAttrHandler = getSessionAttributesHandler(handlerMethod);
这个是处理@SessionAttributes注解的一个方法,主要当前Bean头上的注解中的names属性和types属性添加到SessionAttributes处理器中,以供后续@SessionAttributes注解的相关处理逻辑使用。
我们看下它的构造函数:

public SessionAttributesHandler(Class<?> handlerType, SessionAttributeStore sessionAttributeStore) {
   Assert.notNull(sessionAttributeStore, "SessionAttributeStore may not be null");
   this.sessionAttributeStore = sessionAttributeStore;

   SessionAttributes ann = AnnotatedElementUtils.findMergedAnnotation(handlerType, SessionAttributes.class);
   if (ann != null) {
      Collections.addAll(this.attributeNames, ann.names());
      Collections.addAll(this.attributeTypes, ann.types());
   }
   this.knownAttributeNames.addAll(this.attributeNames);
}

image.png

image.png

来到主流程的第3点:准备ModelAndView容器并初始化

//创建ModelAndView容器,构造时其内部已经创建了用于保存Model属性的ModelMap实例

ModelAndViewContainer mavContainer = new ModelAndViewContainer();

//把重定向输入的FlashMap中所有属性添加到当前ModelAndView容器的Model属性中,以供本次处理逻辑使用(FlashMap的作用就是把上一次的输出FlashMap作为这一次的输入FlashMap)
mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
//这里使用Model工厂初始化新创建的ModelAndView容器

modelFactory.initModel(webRequest, mavContainer, invocableMethod);

//设置容器的属性,如果不忽略,默认Model中的属性将通过url的路径参数传递
mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
让我们看一下modelFactory.initModel(webRequest, mavContainer, invocableMethod);

public void initModel(NativeWebRequest request, ModelAndViewContainer container, HandlerMethod handlerMethod)
      throws Exception {
//使用SessionAttributes处理器,先从Session中获取@SessionAttribute注解声明的Session属性(Object value = session.getAttribute(name);)
   Map<String, ?> sessionAttributes = this.sessionAttributesHandler.retrieveAttributes(request);

//把@SessionAttributes声明的Session属性全部合并到ModelAndView容器中,以供后续处理逻辑使用。这就是@SessionAttributes的工作原理了
   container.mergeAttributes(sessionAttributes);
//调用全部@ModelAttribute注解标记的方法,把方法返回值放入ModelAndView容器的Model中 

  invokeModelAttributeMethods(request, container);


//在findSessionAttributeArguments方法中,获取了HandlerMethod中所有标记了@ModelAttribute的参数

//再判断该类型是否在@SessionAttributes的types属性中,如果是,则把获取该参数上@ModelAttribute注解的name属性添加到方法的返回值列表中
   for (String name : findSessionAttributeArguments(handlerMethod)) {

//如果ModelAndView容器不包含name属性,则从Session中获取该属性,添加到容器中
      if (!container.containsAttribute(name)) {
         Object value = this.sessionAttributesHandler.retrieveAttribute(request, name);
         if (value == null) {
            throw new HttpSessionRequiredException("Expected session attribute '" + name + "'", name);
         }
         container.addAttribute(name, value);
      }
   }
}

这里调用了@ModelAttribute注解标注的方法及初始化ModelAndView容器中Model属性外,还包括把@SessionAttributes注解生命的所有Session属性放入容器中。

被标记为@ModelAttribute的参数,如果其对应类型在@SessionAttributes注解的types中时,也会被作为Session属性名与对应的属性值放入容器中;

来看下这个方法 invokeModelAttributeMethods(request, container);

// 调用全部模型属性方法,向ModelAndView容器中添加相关属性

private void invokeModelAttributeMethods(NativeWebRequest request, ModelAndViewContainer container)
      throws Exception {
//遍历全部模型属性方法,直到为空\
   while (!this.modelMethods.isEmpty()) {

//获取当前遍历的可调用模型属性方法(就是被@ModelAttribute注解的方法)
      InvocableHandlerMethod modelMethod = getNextModelMethod(container).getHandlerMethod();

//检查该方法是否存在@ModelAttribute注解
      ModelAttribute ann = modelMethod.getMethodAnnotation(ModelAttribute.class);

//不存在,则状态异常,因为该方法的检查逻辑时通过判断是否存在这个注解进行添加,正常情况下不会触发
      Assert.state(ann != null, "No ModelAttribute annotation");

//如果当前容器已经包括了模型属性方法生命的模型名,则不覆盖现有的属性
      if (container.containsAttribute(ann.name())) {

//如果注解标记了binding = false,则把这个属性名添加到容器的不绑定列表中,用于在参数绑定时忽略此模型属性的绑定
         if (!ann.binding()) {
            container.setBindingDisabled(ann.name());
         }

//跳过,不覆盖现有属性
         continue;
      }


//ModelAndView容器中不包含该属性则执行下面逻辑

//根据请求执行模型属性方法,获取模型属性方法的返回值。
      Object returnValue = modelMethod.invokeForRequest(request, container);

//如果方法声明返回值是void
      if (modelMethod.isVoid()) {

//判断注解属性的value是否有值
         if (StringUtils.hasText(ann.value())) {
            if (logger.isDebugEnabled()) {
               logger.debug("Name in @ModelAttribute is ignored because method returns void: " +
                     modelMethod.getShortLogMessage());
            }
         }

//跳过覆盖现有属性
         continue;
      }


//如果返回值不是void的话,获取方法的返回值

//根据返回值与类型获取返回的属性名,一般是从方法注解@ModelAttribute的name属性中获取该属性名
      String returnValueName = getNameForReturnValue(returnValue, modelMethod.getReturnType());
 //判断是否声明了binding = false

if (!ann.binding()) {
         container.setBindingDisabled(returnValueName);
      }
      if (!container.containsAttribute(returnValueName)) {
         container.addAttribute(returnValueName, returnValue);
      }
   }
}

@ModelAttribute的参数会优先去获取@ModelAttribute方法中的匹配值,优先于@SessionAttribues;比如get请求,这时候尽管我往session中设置了值,第二次请求get请求去获取@ModelAttribute标记的参数时,依然会是先拉到@ModelAttribute方法的值。在上面代码我们也知道如果ModelAndView中存在了这个属性的话,是不会进行覆盖的,且@SessionAttributes在@ModelAttribute方法前,为什么会这样呢?
因为:
是因为,下面有个操作会把Model中的值替换掉session中已有同名的属性,在第一次请求的时候,设置了Session,但是这时候@ModelAttribute方法已经在Model中了,然后执行完下面这个代码就把Session中的属性给替换了;

这个代码就是主流程的第6点: return getModelAndView(mavContainer, modelFactory, webRequest);
modelFactory.updateModel(webRequest, mavContainer);这个方法
this.sessionAttributesHandler.storeAttributes(request, defaultModel);
this.sessionAttributeStore.storeAttribute(request, name, value);
session.setAttribute(name, value);
以上就是@ModelAttribute和@SessionAttribues的原理了,在后面参数解析的时候,会用到这里面Model的值,还有@InitBinder的参数格式转换;

下面我们来讲一下第4个流程,异步管理那里;