JavaWeb学习之——Spring篇之HandlerAdapter(未完)

1. 概述

handlerAdpater的实现类总共有5个(其中一个已经废弃),而且层级都很少。

163941_KbqL_3039671.png

public interface HandlerAdapter {

   boolean supports(Object handler);

   ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;

   long getLastModified(HttpServletRequest request, Object handler);

}

handlerAdapter的接口很简单,只有3个方法。

  • support——判断handler是否适用于当前HandlerAdapter
  • handler——根据handler返回ModelAndView
  • getLastModified——看名字就知道就是返回最后修改时间

2. HttpRequestHandlerAdapter、SimpleServletHandlerAdapter、SimpleControllerHandlerAdapter

这3个adapter很简单,都是分别针对特定的handler的类型,直接调用就可以了

@Override
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
      throws Exception {

   ((HttpRequestHandler) handler).handleRequest(request, response);
   return null;
}
@Override
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
      throws Exception {

   ((Servlet) handler).service(request, response);
   return null;
}
@Override
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
      throws Exception {

   return ((Controller) handler).handleRequest(request, response);
}

3. RequestMappingHandlerAdapter

RequestMappingHandlerAdapter是AbstractHandlerMethodAdapter的唯一实现类

这个处理就比较复杂(但正是因为他为我们带来了快捷的使用方式)。但总的来说做了3件事

  1. 准备好Handler所需要的参数
  2. 使用Handler处理request请求
  3. 把Handler处理后的结果统一转换为ModelAndView

3.1 准备好Handler所需要的参数

这个步骤应该是3步中最复杂的,抛开直接把Request中的参数(url中的或者body中的)以外、cookie中的参数、session中的参数,还要处理Spring MVC中的一些东西,例如FlashMap中的参数、SessionAttribute中的参数、ModelAttitude中的参数

可以看出,正式因为SpringMVC为我们提供了各种扩展以及便捷的使用,才会处理各种各样的情况。代码虽然很多,如果你知道了用法,反过来看代码逻辑就会轻松得多(用结果推原因,也就是事后诸葛亮)。

@InitBinder,@ModelAttribute,@ControllerAdvice,@SessionAttribute你应该知道。

3.2 初始化

首先看afterPropertiesSet(实现了InitializingBean接口,回在bean初始化的时候调用)

@Override
public void afterPropertiesSet() {
   // Do this first, it may add ResponseBody advice beans
   initControllerAdviceCache();

   if (this.argumentResolvers == null) {
      List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();
      this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
   }
   if (this.initBinderArgumentResolvers == null) {
      List<HandlerMethodArgumentResolver> resolvers = getDefaultInitBinderArgumentResolvers();
      this.initBinderArgumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
   }
   if (this.returnValueHandlers == null) {
      List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();
      this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);
   }
}

3.2.1 initControllerAdviceCache

这个方法可以看出,就是初始化@ControllerAdvice注解标注的类,里面的逻辑就是

  1. 找出@ModelAttribute注解(并且没有@RequestMapping)注解的方法和@InitBinder注解的方法,分别放入modelAttributeAdviceCache和initBinderAdviceCache中
  2. 找出RequestBodyAdvice和ResponseBodyAdvice实现类,放入requestResponseBodyAdvice

核心代码如下:

private void initControllerAdviceCache() {

   List<ControllerAdviceBean> beans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());
   AnnotationAwareOrderComparator.sort(beans);

   List<Object> requestResponseBodyAdviceBeans = new ArrayList<Object>();

   for (ControllerAdviceBean bean : beans) {
      Set<Method> attrMethods = MethodIntrospector.selectMethods(bean.getBeanType(), MODEL_ATTRIBUTE_METHODS);
      if (!attrMethods.isEmpty()) {
         this.modelAttributeAdviceCache.put(bean, attrMethods);
      }
      Set<Method> binderMethods = MethodIntrospector.selectMethods(bean.getBeanType(), INIT_BINDER_METHODS);
      if (!binderMethods.isEmpty()) {
         this.initBinderAdviceCache.put(bean, binderMethods);
      }
      if (RequestBodyAdvice.class.isAssignableFrom(bean.getBeanType())) {
         requestResponseBodyAdviceBeans.add(bean);
      }
      if (ResponseBodyAdvice.class.isAssignableFrom(bean.getBeanType())) {
         requestResponseBodyAdviceBeans.add(bean);
      }
   }

   if (!requestResponseBodyAdviceBeans.isEmpty()) {
      this.requestResponseBodyAdvice.addAll(0, requestResponseBodyAdviceBeans);
   }
}

其中 MODEL_ATTRIBUTE_METHODS和INIT_BINDER_METHODS是静态类

public static final MethodFilter MODEL_ATTRIBUTE_METHODS = new MethodFilter() {
   @Override
   public boolean matches(Method method) {
      return ((AnnotationUtils.findAnnotation(method, RequestMapping.class) == null) &&
            (AnnotationUtils.findAnnotation(method, ModelAttribute.class) != null));
   }
};
public static final MethodFilter INIT_BINDER_METHODS = new MethodFilter() {
   @Override
   public boolean matches(Method method) {
      return AnnotationUtils.findAnnotation(method, InitBinder.class) != null;
   }
};

3.2.2 初始化ArgumentResolvers

if (this.argumentResolvers == null) {
   List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();
   this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
}

首先是使用getDefaultArgumentResolvers 获取默认的ArgumentResolver类,然后封装成一个Composite放入到argumentResolvers中。默认的ArgumentResolver有如下:

private List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers() {
   List<HandlerMethodArgumentResolver> resolvers = new ArrayList<HandlerMethodArgumentResolver>();

   // 基于注释相关参数解析器,从名字就可以看出来分别对应哪些注解
   resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), false));
   resolvers.add(new RequestParamMapMethodArgumentResolver());
   resolvers.add(new PathVariableMethodArgumentResolver());
   resolvers.add(new PathVariableMapMethodArgumentResolver());
   resolvers.add(new MatrixVariableMethodArgumentResolver());
   resolvers.add(new MatrixVariableMapMethodArgumentResolver());
   resolvers.add(new ServletModelAttributeMethodProcessor(false));
   resolvers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice));
   resolvers.add(new RequestPartMethodArgumentResolver(getMessageConverters(), this.requestResponseBodyAdvice));
   resolvers.add(new RequestHeaderMethodArgumentResolver(getBeanFactory()));
   resolvers.add(new RequestHeaderMapMethodArgumentResolver());
   resolvers.add(new ServletCookieValueMethodArgumentResolver(getBeanFactory()));
   resolvers.add(new ExpressionValueMethodArgumentResolver(getBeanFactory()));
   resolvers.add(new SessionAttributeMethodArgumentResolver());
   resolvers.add(new RequestAttributeMethodArgumentResolver());

   // 基于类型的参数解析器
   resolvers.add(new ServletRequestMethodArgumentResolver());
   resolvers.add(new ServletResponseMethodArgumentResolver());
   resolvers.add(new HttpEntityMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice));
   resolvers.add(new RedirectAttributesMethodArgumentResolver());
   resolvers.add(new ModelMethodProcessor());
   resolvers.add(new MapMethodProcessor());
   resolvers.add(new ErrorsMethodArgumentResolver());
   resolvers.add(new SessionStatusMethodArgumentResolver());
   resolvers.add(new UriComponentsBuilderMethodArgumentResolver());

   // 自定义的参数解析器
   if (getCustomArgumentResolvers() != null) {
      resolvers.addAll(getCustomArgumentResolvers());
   }

   // 针对所有类型的参数解析器
   resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true));
   resolvers.add(new ServletModelAttributeMethodProcessor(true));

   return resolvers;
}

加载的默认解析器很多,主要分为4类

  1. 针对注解的参数解析器,根据名字就可以知道是针对那些哪些注解
  2. 基于参数类型的解析器(例如,参数里面的HttpServletRequest,HttpSession,Model等)
  3. 自定义参数解析器,用户可以自己写一个resolver,然后注入到customArgumentResolvers这个参数中。不过由于自定义的解析器顺序是在上面2种解析器后面,如果1、2已经解析成功的,就不会再使用自定义的解析器
  4. 针对所有类型的解析器(例如@RequestParamter注解)
if (this.initBinderArgumentResolvers == null) {
   List<HandlerMethodArgumentResolver> resolvers = getDefaultInitBinderArgumentResolvers();
   this.initBinderArgumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
}
if (this.returnValueHandlers == null) {
   List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();
   this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);
}

和上面类似,就不在赘述

3.3 调用

RequestMappingHandlerAdapter实现了HandlerAdapter,其中supports和getLastModified的方法很简单,重点是看

ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;

这个接口。

这个接口的实现是在handleInternal函数中

@Override
protected ModelAndView handleInternal(HttpServletRequest request,
      HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

   ModelAndView mav;
   //判断request参数,1. 判断method是否一致(GET,POST),如果需要session判断是否有session
   checkRequest(request);

   // 是否是同步的,默认是false
   if (this.synchronizeOnSession) {
      HttpSession session = request.getSession(false);
      if (session != null) {
         Object mutex = WebUtils.getSessionMutex(session);
         synchronized (mutex) {
            //调用handler
            mav = invokeHandlerMethod(request, response, handlerMethod);
         }
      }
      else {
         // No HttpSession available -> no mutex necessary
         mav = invokeHandlerMethod(request, response, handlerMethod);
      }
   }
   else {
      // No synchronization on session demanded at all...
      mav = invokeHandlerMethod(request, response, handlerMethod);
   }
   
   // 如果response头里面没有Cache-Control,并且该handler对应的类有@SessionAttributes,则
   // 设置Cache-Control为non—cache
   if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
      //使用HandlerMethod.getBeanType()作为key,在缓存sessionAttributesHandlerCache。
      if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
         applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
      }
      else {
         prepareResponse(response);
      }
   }

   return mav;
}

这个最主要的方法就是invokeHandlerMethod,这个方法就包含了参数绑定、执行Handler、处理返回值的整个流程,主要步骤注释上都有标明。

protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
      HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
   //首先是利用HttpServletRequest和HttpServletResponse封装成ServletWebRequest,在参数解析器中入参就是这个类。
   ServletWebRequest webRequest = new ServletWebRequest(request, response);
   try {
      //@InitBinder注解的方法获取(先从缓存获取,否则从类定义中获取放入缓存),
      //最后利用获取的数据创建WebDataBinderFactory 
      WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
      //处理@SessiohAttributes和@ModeAttribute
      //看名字就是是为Model服务的,例如@SessiohAttributes和
      //@ModeAttribute(包括全局和局部,具体看初始化流程)注解参数加入的model中等
      ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
       
      //ServletInvocableHandlerMethod继承InvocableHandlerMethod(实现了HandlerMethod接口)。
      //实际处理请求就是通过这个类执行,包括参数绑定、请求处理调用和返回值。
      ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
      // 设置参数解析器
      invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
      // 设置返回值处理器,封装成ModelAndView
      invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
      // InitBinder相关函数
      invocableMethod.setDataBinderFactory(binderFactory);
      // 参数查询
      invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
       
      // 新建ModelAndViewContainer 
      ModelAndViewContainer mavContainer = new ModelAndViewContainer();
      // 设置falshMap
      mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
      // 将@SessionAttributes和@ModelAttribute的相关属性设置到Model中
      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;
      }

      //更新model信息,
      // 这里面需要注意,如果@SessionStatus如果Complate为true,则会清除,这里需要注意的是清除实在Handler处理完后才调用。
      // 根据mavContainer创建ModelAndView(如果有RedirectAttributes,设置FlashMap)
      return getModelAndView(mavContainer, modelFactory, webRequest);
   }
   finally {
      webRequest.requestCompleted();
   }
}

要理解整个处理流程,还需要对方法中涉及的一些对象进行分析,下面就会对ModelAndViewContainer、ModelFactory等类进行分析

4.  ModelAndViewContainer

ModelAndViewContainer就是负责保存Model、View等等功能

private boolean ignoreDefaultModelOnRedirect = false;

// view对象,也可以是String类型的逻辑视图
private Object view;

// 默认Model
private final ModelMap defaultModel = new BindingAwareModelMap();

//redirect Model
private ModelMap redirectModel;

//返回Redirect 视图表示
private boolean redirectModelScenario = false;

//SessionAttribute使用完成标志
private final SessionStatus sessionStatus = new SimpleSessionStatus();

//请求是否完成标志
private boolean requestHandled = false;
  • 默认视图使用的是BindingAwareModelMap这个类,同时继承了Model和ModelMap这2个类(因此在参数里设置 Model 或者ModelMap 都是同一个对象)
  • 如果是redirect视图,那么defaultModel中的参数就没有任何用处,同理非redirect视图的Redirect Model也是没用的

这个类的最主要的方法是getModel()

public ModelMap getModel() {
   if (useDefaultModel()) {
      return this.defaultModel;
   }
   else {
      if (this.redirectModel == null) {
         this.redirectModel = new ModelMap();
      }
      return this.redirectModel;
   }
}
private boolean useDefaultModel() {
   return (!this.redirectModelScenario || (this.redirectModel == null && !this.ignoreDefaultModelOnRedirect));
}

从代码里面看出,使用defaultModel的情况有:1、返回非redirect视图。2、返回的虽然是redirect视图,但redirectMap不为空并且ignoreDefaultModelOnRedirect为false的情况

5. ModelFactory

5.1 SessionAttributesHandler

在说ModelFactory之前必须先了解SessionAttributesHandler。SessionAttributesHandler就是用于处理@SessionAttitudes注解的。

@SessionAttitudes的功能简单来说,就是把一些数据在放入Model的时候保存起来,然后在其他Handler中的Model方法中可以获取。相当于用session来传递参数。只不过我们可以方便的一键删除这些。

SessionAttributesHandler中的属性有:

// 保存@SessionAttributes注解里的value
private final Set<String> attributeNames = new HashSet<String>();
// 保存@SessionAttributes注解里的types
private final Set<Class<?>> attributeTypes = new HashSet<Class<?>>();
// 保存@SessionAttributes中当前已知的Session Name
private final Set<String> knownAttributeNames =
      Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>(4));
// 实际保存数据的处理类,但是数据并不是在这个类里,默认是保存在Session中
private final SessionAttributeStore sessionAttributeStore;

除此之外,还提供了并且提供增删查的功能。查询所有SessionAttribute和删除所有SessionAttribute都是删除的是knownAttributeNames中的

5.2 ModelFactory 初始化

public void initModel(NativeWebRequest request, ModelAndViewContainer container,
      HandlerMethod handlerMethod) throws Exception {
   // 第一步:获取所有SessionAttribute中的参数,merge到ModelAndViewContainer中
   Map<String, ?> sessionAttributes = this.sessionAttributesHandler.retrieveAttributes(request);
   container.mergeAttributes(sessionAttributes);

   // 第二步:调用@ModelAttritude注解的方法(结果加入到ModelAndViewContainer中)
   invokeModelAttributeMethods(request, container);

   // 第三步:处理使用@ModelAttribute注解的参数(需要在@SessionAttribute中存在),最后加入到ModelAndViewContainer
   for (String name : findSessionAttributeArguments(handlerMethod)) {
      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);
      }
   }
}
  • 第一步比较简单,直接从上面介绍过的SessionAttributesHandler中获取所有SessionAttribute参数。然后merge到ModelAndViewContainer(不覆盖原有的)
  • 第二步就是调用invokeModelAttributeMethods处理@ModelAttritude注解的方法. 重要说明见注释
  • private void invokeModelAttributeMethods(NativeWebRequest request, ModelAndViewContainer container)
          throws Exception {
    
       while (!this.modelMethods.isEmpty()) {
          InvocableHandlerMethod modelMethod = getNextModelMethod(container).getHandlerMethod();
          ModelAttribute ann = modelMethod.getMethodAnnotation(ModelAttribute.class);
          if (container.containsAttribute(ann.name())) {
             if (!ann.binding()) {
                container.setBindingDisabled(ann.name());
             }
             continue;
          }
          // 根据request调用HandlerMethod,需要说明的是其中的参数使用了ArgumentResolver,因此注入的Model
          // 就是container中的model(session,httpRequest类似也有对用的ArgumentResolver)
          Object returnValue = modelMethod.invokeForRequest(request, container);
    
          // 如何返回的参数不为空,则把参数设置到model中
          if (!modelMethod.isVoid()){
             // 优先使用注解中的名字,没有则根据返回的class类型设置名字
             String returnValueName = getNameForReturnValue(returnValue, modelMethod.getReturnType());
             if (!ann.binding()) {
                container.setBindingDisabled(returnValueName);
             }
             if (!container.containsAttribute(returnValueName)) {
                container.addAttribute(returnValueName, returnValue);
             }
          }
       }
  • 第三步:处理使用@ModelAttribute注解的参数,前提是SessionAttribute中有,并且Model中并不存在才会加入。

因此,分析到这里,Model中设置参数的优先级就知道了

FlashMap>SessionAttribute>ModelAttribute方法(全局优先于局部)>ModelAttribute参数(全局优先于局部)

未完待续

转载于:https://my.oschina.net/u/3039671/blog/825986

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值