SpringMVC常见组件之HandlerAdapter分析

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
简介: SpringMVC常见组件之HandlerAdapter分析

处理器适配器,主要是DispatcherServlet与handler之间的过渡/桥梁。简单来说就是DispatcherServlet根据handlerMapping创建一个处理器执行链对象HandlerExecutionChain。然后根据HandlerExecutionChain里面的handler来获取HandlerAdapter。


HandlerAdapter调用handle方法处理请求。对DispatcherServlet来说,其不关心底层细节,只需要找到handler对应的HandlerAdapter然后交给HandlerAdapter即可。


为啥要HandlerAdapter呢? 适配器模式! 根据不同类型的handler在调用目标(方法)前进行相应处理。如果不用适配器,这部分相应处理就需要放到目标(方法)中。而根据接口设计原则,这是被反感的。


在DispatcherServlet中,根据DispatcherServlet内部的handler获取HandlerAdapter 。

// Determine handler adapter for the current request.
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

当获取到请求对应的handlermapping以及HandlerExecutionChain(包含handler和interceptors)时,将根据handler和请求获取对应的HandlerAdapter来进行处理。如果找不到一个合适的HandlerAdapter(根据其supports方法判断),则抛出异常:

protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
  if (this.handlerAdapters != null) {
    for (HandlerAdapter adapter : this.handlerAdapters) {
        // 可以看到其是根据supports方法来抉择HandlerAdapter 
      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");
}

当获取到一个合适的HandlerAdapter后,会调用其handle方法进行实际的目标方法处理。 也就是说DispatcherServlet只关心下面这行代码,至于handler是什么,handler目标方法调用前需要做什么准备工作,DispatcherServlet概不关心,交给handler对应的HandlerAdapter处理。

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

需要注意的是,当xml中配置了<mvc:annotation-driven/>标签后,spring默认注入DefaultAnnotationHandlerMapping 和AnnotationMethodHandlerAdapter。该二者已过时,被RequestMappingHandlerMapping 和RequestMappingHandlerAdapter 替代。


HandlerAdapter提供的三个方法:

// 判断是否支持入参handler,也就是当前HandlerAdapter是否适用于入参handler
boolean supports(Object handler);
//使用给定的handler处理请求 返回响应--这是核心入口
ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
// 最后修饰标记,如何不支持handler,则返回 -1
long getLastModified(HttpServletRequest request, Object handler);

idea里面操作:选中某个接口右键 -> Diagrams(show Diagram) -> 再出来的图上选中某个接口右键 ->show Implementations(或者Ctrl+T) ->出来的框框Ctrl + A ->Enter

适配器 支持类型
AbstractHandlerMethodAdapter HandlerMethod
SimpleServletHandlerAdapter javax.servlet.Servlet
SimpleControllerHandlerAdapter org.springframework.web.servlet.mvc.Controller
HandlerFunctionAdapter org.springframework.web.servlet.function.HandlerFunction
HttpRequestHandlerAdapter org.springframework.web.HttpRequestHandler

如下是RequestMappingHandlerAdapter核心处理流程时序图:


【1】AbstractHandlerMethodAdapter

AbstractHandlerMethodAdapter主要用来处理Handler为HandlerMethod类型,也就是最常见的某个controller的某个方法。其只有一个子类RequestMappingHandlerAdapter。

其supports方法对handler做了类型校验必须是HandlerMethod 类型,然后暴露抽象方法supportsInternal给子类实现

其supports方法对handler做了类型校验必须是HandlerMethod 类型,然后暴露抽象方法supportsInternal给子类实现
publi

其handle方法也是暴露了抽象方法给子类实现handleInternal。

@Override
@Nullable
public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
    throws Exception {
  return handleInternal(request, response, (HandlerMethod) handler);
}

getLastModifiedInternal抽象方法给子类实现。

protected abstract long getLastModifiedInternal(HttpServletRequest request, HandlerMethod handlerMethod);

也就是说AbstractHandlerMethodAdapter本身没有实现HandlerAdapter的三个核心方法,交给了子类处理。

同时,我们也可以从其类结构图可以看到核心方法都是抽象方法,让唯一子类RequestMappingHandlerAdapter实现。

① RequestMappingHandlerAdapter的核心属性

// 是否忽略spring的xml配置,默认false
private static final boolean shouldIgnoreXml = SpringProperties.getFlag("spring.xml.ignore");
 // 找标注了@InitBinder方法
public static final MethodFilter INIT_BINDER_METHODS = method ->
    AnnotatedElementUtils.hasAnnotation(method, InitBinder.class);
// 找标注了 @ModelAttribute方法,但是没有标注@RequestMapping
public static final MethodFilter MODEL_ATTRIBUTE_METHODS = method ->
    (!AnnotatedElementUtils.hasAnnotation(method, RequestMapping.class) &&
        AnnotatedElementUtils.hasAnnotation(method, ModelAttribute.class));
// 自定义的参数解析器
@Nullable
private List<HandlerMethodArgumentResolver> customArgumentResolvers;
// 框架内部的参数解析器
@Nullable
private HandlerMethodArgumentResolverComposite argumentResolvers;
// 标注了@InitBinder方法的参数解析器
@Nullable
private HandlerMethodArgumentResolverComposite initBinderArgumentResolvers;
// 自定义返回结果处理器
@Nullable
private List<HandlerMethodReturnValueHandler> customReturnValueHandlers;
// 框架内部的返回结果处理器
@Nullable
private HandlerMethodReturnValueHandlerComposite returnValueHandlers;
// 框架内部视图解析器
@Nullable
private List<ModelAndViewResolver> modelAndViewResolvers;
// 框架内部内容协商管理器
private ContentNegotiationManager contentNegotiationManager = new ContentNegotiationManager();
// 信息转换器
private List<HttpMessageConverter<?>> messageConverters;
// 请求响应体通知/顾问器,主要用来在读取请求或者输出响应前做一些处理
private final List<Object> requestResponseBodyAdvice = new ArrayList<>();
// 数据绑定初始化器
@Nullable
private WebBindingInitializer webBindingInitializer;
private AsyncTaskExecutor taskExecutor = new SimpleAsyncTaskExecutor("MvcAsync");
@Nullable
private Long asyncRequestTimeout;
private CallableProcessingInterceptor[] callableInterceptors = new CallableProcessingInterceptor[0];
private DeferredResultProcessingInterceptor[] deferredResultInterceptors = new DeferredResultProcessingInterceptor[0];
private ReactiveAdapterRegistry reactiveAdapterRegistry = ReactiveAdapterRegistry.getSharedInstance();
private boolean ignoreDefaultModelOnRedirect = false;
private int cacheSecondsForSessionAttributeHandlers = 0;
private boolean synchronizeOnSession = false;
private SessionAttributeStore sessionAttributeStore = new DefaultSessionAttributeStore();
// 参数名发现/解析器
private ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();
// beanFactory
@Nullable
private ConfigurableBeanFactory beanFactory;
// 如下是一些缓存
private final Map<Class<?>, SessionAttributesHandler> sessionAttributesHandlerCache = new ConcurrentHashMap<>(64);
private final Map<Class<?>, Set<Method>> initBinderCache = new ConcurrentHashMap<>(64);
// @ControllerAdvice类中的@InitBinder方法
private final Map<ControllerAdviceBean, Set<Method>> initBinderAdviceCache = new LinkedHashMap<>();
private final Map<Class<?>, Set<Method>> modelAttributeCache = new ConcurrentHashMap<>(64);
// @ControllerAdvice类中的@ModelAttribute方法
private final Map<ControllerAdviceBean, Set<Method>> modelAttributeAdviceCache = new LinkedHashMap<>();

构造函数如下所示在实例化时添加了ByteArrayHttpMessageConverter、StringHttpMessageConverter、SourceHttpMessageConverter以及AllEncompassingFormHttpMessageConverter:

public RequestMappingHandlerAdapter() {
  this.messageConverters = new ArrayList<>(4);
  this.messageConverters.add(new ByteArrayHttpMessageConverter());
  this.messageConverters.add(new StringHttpMessageConverter());
  if (!shouldIgnoreXml) {
    try {
      this.messageConverters.add(new SourceHttpMessageConverter<>());
    }
    catch (Error err) {
      // Ignore when no TransformerFactory implementation is available
    }
  }
  this.messageConverters.add(new AllEncompassingFormHttpMessageConverter());
}

② afterPropertiesSet

如下图所示,如何bean实现了InitializingBean接口,那么在初始化过程中一定会调用其afterPropertiesSet方法。


afterPropertiesSet方法源码如下:

@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);
  }
}

代码主要意思就是先初始化ControllerAdviceCache,然后判断argumentResolvers 、initBinderArgumentResolvers 以及returnValueHandlers 是否为null,如果为null则获取系统内部的"对应的bean",然后放到XXXXXComposite中。

① initControllerAdviceCache

这里需要额外说明的是,如下是从系统内部全局扫描哦,也就是说modelAttributeAdviceCache、initBinderAdviceCache以及requestResponseBodyAdviceBeans放的都是全局的。

private void initControllerAdviceCache() {
    if (getApplicationContext() == null) {
      return;
    }
// 获取标注了@ControllerAdvice注解的类,包装为ControllerAdviceBean,然后排序返回
    List<ControllerAdviceBean> adviceBeans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());
    List<Object> requestResponseBodyAdviceBeans = new ArrayList<>();
    for (ControllerAdviceBean adviceBean : adviceBeans) {
      Class<?> beanType = adviceBean.getBeanType();
      if (beanType == null) {
        throw new IllegalStateException("Unresolvable type for ControllerAdviceBean: " + adviceBean);
      }
      // 获取系统内adviceBean 中的@ModelAttribute方法,然后放入modelAttributeAdviceCache
      Set<Method> attrMethods = MethodIntrospector.selectMethods(beanType, MODEL_ATTRIBUTE_METHODS);
      if (!attrMethods.isEmpty()) {
        this.modelAttributeAdviceCache.put(adviceBean, attrMethods);
      }
      // 获取系统内adviceBean 中的@InitBinder方法,然后放入initBinderAdviceCache
      Set<Method> binderMethods = MethodIntrospector.selectMethods(beanType, INIT_BINDER_METHODS);
      if (!binderMethods.isEmpty()) {
        this.initBinderAdviceCache.put(adviceBean, binderMethods);
      }
// 获取系统内adviceBean 是RequestBodyAdvice或ResponseBodyAdvice类型,然后放入requestResponseBodyAdviceBeans
      if (RequestBodyAdvice.class.isAssignableFrom(beanType) || ResponseBodyAdvice.class.isAssignableFrom(beanType)) {
        requestResponseBodyAdviceBeans.add(adviceBean);
      }
    }
    if (!requestResponseBodyAdviceBeans.isEmpty()) {
      this.requestResponseBodyAdvice.addAll(0, requestResponseBodyAdviceBeans);
    }
//... 一些日志打印 不用管
  }

代码解释如下:


① 获取标注了@ControllerAdvice注解的类,包装为ControllerAdviceBean,然后排序返回List<ControllerAdviceBean> adviceBeans

② 对adviceBeans进行遍历:

③ 检索标注了@ModelAttribute注解的方法集合attrMethods,然后将(adviceBean, attrMethods)放入modelAttributeAdviceCache;

④ 检索标注了@InitBinder注解的方法集合binderMethods,然后将(adviceBean, binderMethods)放入initBinderAdviceCache;

⑤ 如果当前bean是RequestBodyAdvice或者ResponseBodyAdvice,则放入requestResponseBodyAdviceBeans;

⑥ 如果requestResponseBodyAdviceBeans不为空,则放入requestResponseBodyAdvice

② getDefaultArgumentResolvers

如下所示,返回参数解析器列表,包括框架内置解析器和自定义解析器。

private List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers() {
  List<HandlerMethodArgumentResolver> resolvers = new ArrayList<>(30);
  // Annotation-based argument resolution
  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());
  // Type-based argument resolution
  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 (KotlinDetector.isKotlinPresent()) {
    resolvers.add(new ContinuationHandlerMethodArgumentResolver());
  }
  // Custom arguments
  if (getCustomArgumentResolvers() != null) {
    resolvers.addAll(getCustomArgumentResolvers());
  }
  // Catch-all  这里很有意思哦
  resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true));
  resolvers.add(new ServletModelAttributeMethodProcessor(true));
  return resolvers;
}

③ getDefaultInitBinderArgumentResolvers

这里是获取解析那些标注了@InitBinder方法的参数解析器

private List<HandlerMethodArgumentResolver> getDefaultInitBinderArgumentResolvers() {
  List<HandlerMethodArgumentResolver> resolvers = new ArrayList<>(20);
  // Annotation-based argument resolution
  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 ExpressionValueMethodArgumentResolver(getBeanFactory()));
  resolvers.add(new SessionAttributeMethodArgumentResolver());
  resolvers.add(new RequestAttributeMethodArgumentResolver());
  // Type-based argument resolution
  resolvers.add(new ServletRequestMethodArgumentResolver());
  resolvers.add(new ServletResponseMethodArgumentResolver());
  // Custom arguments
  if (getCustomArgumentResolvers() != null) {
    resolvers.addAll(getCustomArgumentResolvers());
  }
  // Catch-all
  resolvers.add(new PrincipalMethodArgumentResolver());
  resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true));
  return resolvers;
}

④ getDefaultReturnValueHandlers

获取返回结果处理器,包括框架内置的和自定义的。

private List<HandlerMethodReturnValueHandler> getDefaultReturnValueHandlers() {
  List<HandlerMethodReturnValueHandler> handlers = new ArrayList<>(20);
  // Single-purpose return value types
  handlers.add(new ModelAndViewMethodReturnValueHandler());
  handlers.add(new ModelMethodProcessor());
  handlers.add(new ViewMethodReturnValueHandler());
  handlers.add(new ResponseBodyEmitterReturnValueHandler(getMessageConverters(),
      this.reactiveAdapterRegistry, this.taskExecutor, this.contentNegotiationManager));
  handlers.add(new StreamingResponseBodyReturnValueHandler());
  handlers.add(new HttpEntityMethodProcessor(getMessageConverters(),
      this.contentNegotiationManager, this.requestResponseBodyAdvice));
  handlers.add(new HttpHeadersReturnValueHandler());
  handlers.add(new CallableMethodReturnValueHandler());
  handlers.add(new DeferredResultMethodReturnValueHandler());
  handlers.add(new AsyncTaskMethodReturnValueHandler(this.beanFactory));
  // Annotation-based return value types
  handlers.add(new ServletModelAttributeMethodProcessor(false));
  handlers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(),
      this.contentNegotiationManager, this.requestResponseBodyAdvice));
  // Multi-purpose return value types
  handlers.add(new ViewNameMethodReturnValueHandler());
  handlers.add(new MapMethodProcessor());
  // Custom return value types
  if (getCustomReturnValueHandlers() != null) {
    handlers.addAll(getCustomReturnValueHandlers());
  }
  // Catch-all
  if (!CollectionUtils.isEmpty(getModelAndViewResolvers())) {
    handlers.add(new ModelAndViewResolverMethodReturnValueHandler(getModelAndViewResolvers()));
  }
  else {
    handlers.add(new ServletModelAttributeMethodProcessor(true));
  }
  return handlers;
}

这里我们同样不展开分析,放在返回结果处理器中说明。OK到这里我们的afterPropertiesSet完毕,也就是说bean实例化之后,需要的参数解析器、返回结果处理器以及标注了@ControllerAdvice的bean都已经注入


③ handleInternal

RequestMappingHandlerAdaptersupportsInternal()方法很简单,直接返回了true。这里我们分析handleInternal方法。

protected ModelAndView handleInternal(HttpServletRequest request,
    HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
  ModelAndView mav;
  checkRequest(request);
  // Execute invokeHandlerMethod in synchronized block if required.
  if (this.synchronizeOnSession) {
    HttpSession session = request.getSession(false);
    if (session != null) {
      Object mutex = WebUtils.getSessionMutex(session);
      synchronized (mutex) {
        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);
  }
  if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
    if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
      applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
    }
    else {
      prepareResponse(response);
    }
  }
  return mav;
}

方法解释如下:


① 检查请求,主要是检查supportedMethods与requireSession。如果不满足可能会抛出HttpRequestMethodNotSupportedException或者HttpSessionRequiredException异常。

② 判断是否需要同步锁,如果是且session不为null则获取锁再调用invokeHandlerMethod,否则直接调用invokeHandlerMethod

③ 如果response的头部没有Cache-Control,则进行会话与缓存处理:

④ 往Map<Class<?>, SessionAttributesHandler> sessionAttributesHandlerCache中放入BeanType与SessionAttributesHandler;

⑤ 如果SessionAttributesHandler有SessionAttributes,则调用applyCacheSeconds方法否则直接调用prepareResponse(response);其实这两个方法都是对cacheControl、cacheSeconds进行处理。


SessionAttributesHandler 的核心属性和构造函数

public class SessionAttributesHandler {
  private final Set<String> attributeNames = new HashSet<>();
  private final Set<Class<?>> attributeTypes = new HashSet<>();
  private final Set<String> knownAttributeNames = Collections.newSetFromMap(new ConcurrentHashMap<>(4));
  private final SessionAttributeStore sessionAttributeStore;
  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);
  }
  //...
}


① 核心方法invokeHandlerMethod

这个方法是springmvc请求流程中的核心入口方法,首先会进行一些bean的初始化,如binderFactory、modelFactory、invocableMethod、mavContainer、asyncWebRequest以及asyncManager。当准备工作做好之后,其会调用ServletInvocableHandlerMethod#invokeAndHandle方法进行目标方法的反射调用与返回结果处理。

protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
    HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
  ServletWebRequest webRequest = new ServletWebRequest(request, response);
  try {
  //① 获取`WebDataBinderFactory`,这里获取的是`ServletRequestDataBinderFactory`
    WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
    //② 获取`ModelFactory`
    ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
//③ 将`handlerMethod`包装为`ServletInvocableHandlerMethod`
    ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
//④ 如果参数解析器不为null,则为`ServletInvocableHandlerMethod`设置`argumentResolvers`;
    if (this.argumentResolvers != null) {
      invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
    }
// ⑤ 如果返回结果处理器不为null,则为`ServletInvocableHandlerMethod`设置`returnValueHandlers`;
    if (this.returnValueHandlers != null) {
      invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
    }
    // ⑥ 设置binderFactory
    invocableMethod.setDataBinderFactory(binderFactory);
    invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
    // 实例化ModelAndViewContainer 并进行model初始化
    ModelAndViewContainer mavContainer = new ModelAndViewContainer();
    mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
    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();
      LogFormatUtils.traceDebug(logger, traceOn -> {
        String formatted = LogFormatUtils.formatValue(result, !traceOn);
        return "Resume with async result [" + formatted + "]";
      });
      invocableMethod = invocableMethod.wrapConcurrentResult(result);
    }
    // 核心方法
    invocableMethod.invokeAndHandle(webRequest, mavContainer);
    if (asyncManager.isConcurrentHandlingStarted()) {
      return null;
    }
      // 获取ModelAndView,可能为null
    return getModelAndView(mavContainer, modelFactory, webRequest);
  }
  finally {
    webRequest.requestCompleted();
  }
}

代码解释如下:


① 获取WebDataBinderFactory,这里获取的是ServletRequestDataBinderFactory,关于数据绑定器参考SpringMVC常见组件之DataBinder数据绑定器分析;这个过程会获取@InitBinder方法创建ServletRequestDataBinderFactory。

② 获取ModelFactory,该工厂主要用来在方法调用前初始化model,方法调用后update model。会根据SessionAttributesHandler、ModelAttributeMethod以及binderFactory来创建ModelFactory实例。这个过程会扫描获取@ModelAttribute方法哦;

③ 将handlerMethod包装为ServletInvocableHandlerMethod,其继承自InvocableHandlerMethod,额外提供了返回结果处理能力(内部拥有HandlerMethodReturnValueHandlerComposite returnValueHandlers;成员变量);

④ 如果参数解析器不为null,则为ServletInvocableHandlerMethod设置argumentResolvers;

⑤ 如果返回结果处理器不为null,则为ServletInvocableHandlerMethod设置returnValueHandlers;

⑥ 为ServletInvocableHandlerMethod设置DataBinderFactory;

⑦ 为ServletInvocableHandlerMethod设置ParameterNameDiscoverer,默认是DefaultParameterNameDiscoverer;

⑧ 实例化ModelAndViewContainer,其包括了defaultModel、redirectModel以及视图View;

⑨ 往ModelAndViewContainer的defaultModel或者redirectModel添加“闪存属性”,闪存属性、redirectModel出现在请求重定向场景中。

⑩ 初始化model,这里会获取sessionAttributes合并到model中,然后调用ModelAttributeMethods(就是标注了@ModelAttribute的方法),最后对sessionAttributes做校验(如果model中没有某个参数但是类注解@SessionAttributes中声明有,但是没有获取到值,那么会抛出异常HttpSessionRequiredException)。

11 ModelAndViewContainer设置在重定向的时候是否忽略defaultModel,默认值false;

12 一系列异步处理,应用在异步请求中…

13 核心方法invocableMethod.invokeAndHandle(webRequest, mavContainer);,准备开始反射调用;

14 getModelAndView(mavContainer, modelFactory, webRequest);获取ModelAndView,可能为null哦

15 webRequest.requestCompleted();定义在finally中,最后处理请求结束,如更新session attributes。

② 核心方法getDataBinderFactory


这里我们看一下getDataBinderFactory方法,为什么特意分析呢?该方法在创建DataBinderFactory前会拿到全局initBinderAdviceCache中"适合"当前handlerType的标注了@InitBinder的方法然后和当前handlerType中的标注了@InitBinder的方法一起构造为List<InvocableHandlerMethod> initBinderMethods提供给createDataBinderFactory(initBinderMethods);方法用来创建数据绑定器工厂。

private WebDataBinderFactory getDataBinderFactory(HandlerMethod handlerMethod) throws Exception {
  Class<?> handlerType = handlerMethod.getBeanType();
  // 获取当前handlerType,其实就是controller中的标注了`@InitBinder`的方法
  Set<Method> methods = this.initBinderCache.get(handlerType);
  if (methods == null) {
    methods = MethodIntrospector.selectMethods(handlerType, INIT_BINDER_METHODS);
    this.initBinderCache.put(handlerType, methods);
  }
  List<InvocableHandlerMethod> initBinderMethods = new ArrayList<>();
  // Global methods first
//拿到全局`initBinderAdviceCache`中`"适合"`当前`handlerType`的标注了`@InitBinder`的方法
  this.initBinderAdviceCache.forEach((controllerAdviceBean, methodSet) -> {
    if (controllerAdviceBean.isApplicableToBeanType(handlerType)) {
      Object bean = controllerAdviceBean.resolveBean();
      for (Method method : methodSet) {
        initBinderMethods.add(createInitBinderMethod(bean, method));
      }
    }
  });
  for (Method method : methods) {
    Object bean = handlerMethod.getBean();
    initBinderMethods.add(createInitBinderMethod(bean, method));
  }
  return createDataBinderFactory(initBinderMethods);
}

③ 核心方法getModelFactory


同样的,在获取ModelFactory时候该方法在创建ModelFactory 前会拿到全局modelAttributeAdviceCache中"适合"当前handlerType的标注了@ModelAttribute的方法然后和当前handlerType中的标注了@ModelAttribute的方法一起构造为List<InvocableHandlerMethod> attrMethods 提供给new ModelFactory(attrMethods, binderFactory, sessionAttrHandler);方法用来创建ModelFactory 。

private ModelFactory getModelFactory(HandlerMethod handlerMethod, WebDataBinderFactory binderFactory) {
  SessionAttributesHandler sessionAttrHandler = getSessionAttributesHandler(handlerMethod);
  Class<?> handlerType = handlerMethod.getBeanType();
  Set<Method> methods = this.modelAttributeCache.get(handlerType);
  if (methods == null) {
    methods = MethodIntrospector.selectMethods(handlerType, MODEL_ATTRIBUTE_METHODS);
    this.modelAttributeCache.put(handlerType, methods);
  }
  List<InvocableHandlerMethod> attrMethods = new ArrayList<>();
  // Global methods first
  this.modelAttributeAdviceCache.forEach((controllerAdviceBean, methodSet) -> {
    if (controllerAdviceBean.isApplicableToBeanType(handlerType)) {
      Object bean = controllerAdviceBean.resolveBean();
      for (Method method : methodSet) {
        attrMethods.add(createModelAttributeMethod(binderFactory, bean, method));
      }
    }
  });
  for (Method method : methods) {
    Object bean = handlerMethod.getBean();
    attrMethods.add(createModelAttributeMethod(binderFactory, bean, method));
  }
  return new ModelFactory(attrMethods, binderFactory, sessionAttrHandler);
}

④ 核心方法ModelFactory.initModel

public void initModel(NativeWebRequest request, ModelAndViewContainer container, HandlerMethod handlerMethod)
    throws Exception {
  //获取SessionAttributesHandler.knownAttributeNames中在session里面不为null的属性
  Map<String, ?> sessionAttributes = this.sessionAttributesHandler.retrieveAttributes(request);
  //合并值不为null的,且container中的model不包含该key的属性
  container.mergeAttributes(sessionAttributes);
  // 调用@ModelAttribute方法
  invokeModelAttributeMethods(request, container);
  // 检测
  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);
    }
  }
}


为什么这里我们会细致看一下这个方法呢?前面checkRequest(request);只是一种宏观的请求过滤,而initModel方法中呢,会对复杂对象类型的name-value做进一步过滤,这里可能会抛出HttpSessionRequiredException异常。


findSessionAttributeArguments如下所示从方法参数中找到标注了@ModelAttribute注解的参数,如果这个参数(或类型)是@SessionAttributes中标注的,则放到result中返回。

private List<String> findSessionAttributeArguments(HandlerMethod handlerMethod) {
  List<String> result = new ArrayList<>();
  for (MethodParameter parameter : handlerMethod.getMethodParameters()) {
    if (parameter.hasParameterAnnotation(ModelAttribute.class)) {
      String name = getNameForParameter(parameter);
      Class<?> paramType = parameter.getParameterType();
      if (this.sessionAttributesHandler.isHandlerSessionAttribute(name, paramType)) {
        result.add(name);
      }
    }
  }
  return result;
}


也就是说如果handlerMethod的参数中有使用了@ModelAttribute注解的参数且其是"SessionAttribute属性"(this.sessionAttributesHandler.isHandlerSessionAttribute(name, paramType))。如果ModelAndViewContainer 不包含该属性但是SessionAttributesHandler(内部的knownAttributeNames)包含该属性但是获取到的值为null,则抛出异常。


⑤ 核心方法invokeAndHandle

ServletInvocableHandlerMethod#invokeAndHandle方法主要是调用目标方法获取返回结果并对返回结果进行处理。

public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
    Object... providedArgs) throws Exception {
  // 这里业务代码可能抛出异常,那么将会一路抛出到dispatchServlet中
  Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
  // 设置响应状态,如果HttpStatus不为null的话
  setResponseStatus(webRequest);
  if (returnValue == null) {
  // 判断请求是否未修改、响应状态是否不为null、请求是否已经被处理
    if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
    // 如果有必要则禁用内容缓存
      disableContentCachingIfNecessary(webRequest);
      // 设置请求被处理完毕
      mavContainer.setRequestHandled(true);
      return;
    }
  }else if (StringUtils.hasText(getResponseStatusReason())) {
    mavContainer.setRequestHandled(true);//是否有responseStatusReason,如果有设置请求已处理
    return;
  }
// 设置请求还没有被处理
  mavContainer.setRequestHandled(false);
  Assert.state(this.returnValueHandlers != null, "No return value handlers");
  try {
    this.returnValueHandlers.handleReturnValue( // 处理返回结果
        returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
  }
  catch (Exception ex) {
    if (logger.isTraceEnabled()) {
      logger.trace(formatErrorForReturnValue(returnValue), ex);
    }
    throw ex;
  }
}

代码解释如下:


① invokeForRequest方法会解析参数然后反射调用目标方法获取返回结果;

② 如果HttpStatus不为null,则setResponseStatus设置响应状态如状态码、状态说明;

③ 如果返回结果为null或者ResponseStatusReason不为空,则尝试进行判断并处理webRequest和mavContainer,如mavContainer设置请求处理完毕状态标记为true。getResponseStatus和getResponseStatusReason常用在@ResponseStatus声明的方法场景中。

④ 设置请求未被处理然后使用返回结果处理器处理返回结果。

可以看到该方法是一个模板方法,将具体功能抽离了出去,如invokeForRequest、handleReturnValue。关于返回结果处理这里不展开分析,会有单独章节说明。我们接下来继续追踪一下invokeForRequest方法。


⑥ 核心方法invokeForRequest

invokeForRequest方法是ServletInvocableHandlerMethod的父类InvocableHandlerMethod提供的方法,该方法很简洁-获取参数值然后反射调用目标方法。

@Nullable
public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
    Object... providedArgs) throws Exception {
  //获取解析到的参数值
  Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
  if (logger.isTraceEnabled()) {
    logger.trace("Arguments: " + Arrays.toString(args));
  }
  //反射调用方法获取方法返回结果
  return doInvoke(args);
}

这里核心在于getMethodArgumentValues,也就是解析参数值:

protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
    Object... providedArgs) throws Exception {
  MethodParameter[] parameters = getMethodParameters();
  if (ObjectUtils.isEmpty(parameters)) {
    return EMPTY_ARGS;
  }
  Object[] args = new Object[parameters.length];
  for (int i = 0; i < parameters.length; i++) {
    MethodParameter parameter = parameters[i];
    parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
    args[i] = findProvidedArgument(parameter, providedArgs);
    if (args[i] != null) {
      continue;
    }
    if (!this.resolvers.supportsParameter(parameter)) {
      throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
    }
    try {
      args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
    }
    //...
  }
  return args;
}

代码解释如下:

  • ① 获取方法参数数组,如果为空则返回new Object[0]
  • ② 遍历循环方法参数,如果未找到一个合适的参数解析器则抛出异常IllegalStateException,如果有合适的参数解析器则进行参数解析。


现在依次返回,我们看下getModelAndView方法。

⑦ 核心方法getModelAndView

@Nullable
private ModelAndView getModelAndView(ModelAndViewContainer mavContainer,
    ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception {
  // 更新model
  modelFactory.updateModel(webRequest, mavContainer);
  // 很关键哦,表示请求已经处理,比如添加@ResponseBody注解的时候
  if (mavContainer.isRequestHandled()) {
    return null;
  }
  // 获取model,封装实例化ModelAndView 
  ModelMap model = mavContainer.getModel();
  ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, mavContainer.getStatus());
  if (!mavContainer.isViewReference()) {
    mav.setView((View) mavContainer.getView());
  }
  // 重定向请求时,如果有重定向属性 放到outputFlashMap中
  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;
}

代码解释如下 :


① 更新model,这里会处理sessionAttributes(如clean或者store),然后尝试将BindingResult放到defaultModel中;

② mavContainer.isRequestHandled()如果判断当前请求已经处理完毕,那么直接返回null;

③ 获取ModelAndView 实例,包括Object view、ModelMap model、HttpStatus status;等。

④ 如果view不是string,也就是经过解析的View,那么mav.setView((View) mavContainer.getView());;

⑤ 如果model是RedirectAttributes,也就是说当前请求是重定向。那么从model中获取"闪存"属性,然后放到HttpServletRequest 带给重定向后的请求域。


RedirectAttributes接口继承了Model接口,有唯一实现类RedirectAttributesModelMap

【2】SimpleControllerHandlerAdapter


其主要是用来为那些实现了Controller接口的handler服务的。什么叫做实现了Controller接口的handler呢?我们可以回顾一下其使用方法。

<mvc:view-controller path="/" view-name="index"></mvc:view-controller>


或者如下配置定义一个SimpleUrlHandlerMapping:

<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping" >
      <!--或者如下配置-->
      <property name="mappings">
          <value>
              /myController=myController
          </value>
      </property>
      <property name="order">
          <value>
              1
          </value>
      </property>
  </bean>


可以看到上面我们定义了一个order属性为1,这是为了避免被defaultServleHandler预先处理"/**" -> "org.springframework.web.servlet.resource.DefaultServletHttpRequestHandler#0"

MyController 如下所示:

@org.springframework.stereotype.Controller
public class MyController implements Controller {
   @Override
   public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
       System.out.println(request.getParameterMap());
       System.out.println(response.getStatus());
       ModelAndView modelAndView = new ModelAndView();
       modelAndView.setViewName("success");
       return modelAndView;
   }
}


接下来我们看一下SimpleControllerHandlerAdapter源码,如下所示其异常简单,简洁干脆实现了HandlerAdapterd的三个方法,没有额外方法。

public class SimpleControllerHandlerAdapter implements HandlerAdapter {
  // 判断是否支持当前handler --判断handler是否为Controller类型
  @Override
  public boolean supports(Object handler) {
    return (handler instanceof Controller);
  }
  // 直接调用handler的handleRequest方法
  @Override
  @Nullable
  public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
      throws Exception {
    return ((Controller) handler).handleRequest(request, response);
  }
  // 如果是LastModified则调用其getLastModified方法,否则返回-1
  @Override
  public long getLastModified(HttpServletRequest request, Object handler) {
    if (handler instanceof LastModified) {
      return ((LastModified) handler).getLastModified(request);
    }
    return -1L;
  }
}


【3】HttpRequestHandlerAdapter


该适配器也很简单,主要是为HttpRequestHandler服务。比如DefaultServletHttpRequestHandler–默认处理器,ResourceHttpRequestHandler静态资源处理。

public class HttpRequestHandlerAdapter implements HandlerAdapter {
// 判断handler是否为HttpRequestHandler类型
  @Override
  public boolean supports(Object handler) {
    return (handler instanceof HttpRequestHandler);
  }
// 直接调用handlerd的handleRequest方法
  @Override
  @Nullable
  public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
      throws Exception {
    ((HttpRequestHandler) handler).handleRequest(request, response);
    return null;
  }
  // 如果是LastModified则调用其getLastModified方法,否则返回-1  @Override
  public long getLastModified(HttpServletRequest request, Object handler) {
    if (handler instanceof LastModified) {
      return ((LastModified) handler).getLastModified(request);
    }
    return -1L;
  }
}

这里我们看一下DefaultServletHttpRequestHandler的handleRequest方法,仅仅只是转发了请求。

@Override
public void handleRequest(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException {
  Assert.state(this.servletContext != null, "No ServletContext set");
  RequestDispatcher rd = this.servletContext.getNamedDispatcher(this.defaultServletName);
  if (rd == null) {
    throw new IllegalStateException("A RequestDispatcher could not be located for the default servlet '" +
        this.defaultServletName + "'");
  }
  rd.forward(request, response);
}


【4】SimpleServletHandlerAdapter

最基础也很必要的一个适配器,为Servlet服务,处理请求时直接调用servlet的service方法。

public class SimpleServletHandlerAdapter implements HandlerAdapter {
// 判断handler是否为Servlet
  @Override
  public boolean supports(Object handler) {
    return (handler instanceof Servlet);
  }
// 调用Servlet的service方法
  @Override
  @Nullable
  public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
      throws Exception {
    ((Servlet) handler).service(request, response);
    return null;
  }
// 直接返回-1
  @Override
  public long getLastModified(HttpServletRequest request, Object handler) {
    return -1;
  }
}

【5】HandlerFunctionAdapter

Spring5.2版本后新增的,主要为HandlerFunction服务。

// 判断是否为HandlerFunction类型
@Override
public boolean supports(Object handler) {
  return handler instanceof HandlerFunction;
}
@Nullable
@Override
public ModelAndView handle(HttpServletRequest servletRequest,
    HttpServletResponse servletResponse,
    Object handler) throws Exception {
  HandlerFunction<?> handlerFunction = (HandlerFunction<?>) handler;
  ServerRequest serverRequest = getServerRequest(servletRequest);
  ServerResponse serverResponse = handlerFunction.handle(serverRequest);
  return serverResponse.writeTo(servletRequest, servletResponse,
      new ServerRequestContext(serverRequest));
}
// 直接返回-1
public long getLastModified(HttpServletRequest request, Object handler) {
  return -1L;
}




目录
相关文章
|
容器
SpringMVC常见组件之HandlerExceptionResolver分析-2
SpringMVC常见组件之HandlerExceptionResolver分析-2
76 0
|
7月前
|
设计模式
SpringMVC常见组件之DataBinder数据绑定器分析
SpringMVC常见组件之DataBinder数据绑定器分析
332 0
|
XML 缓存 Java
SpringMVC常见组件之ViewResolver分析
本文我们尝试总结分析SpringMVC体系中的视图解析器-ViewResolver。其根据name解析视图View,通常鼓励实现类考虑国际化策略实现。
112 0
|
7月前
|
XML 存储 Java
SpringMVC常见组件之HandlerMapping分析
SpringMVC常见组件之HandlerMapping分析
168 0
|
XML 前端开发 Java
SpringMVC常见组件之View分析
SpringMVC常见组件之View分析
100 0
|
JSON 前端开发 Java
SpringMVC常见组件之HandlerExceptionResolver分析-1
SpringMVC常见组件之HandlerExceptionResolver分析-1
211 0
|
XML 存储 前端开发
解析 SpringMVC 父子容器及九大内置组件加载过程
解析 SpringMVC 父子容器及九大内置组件加载过程
121 1
java202304java学习笔记第六十二天-ssm-springMvc中组件分析
java202304java学习笔记第六十二天-ssm-springMvc中组件分析
45 0
|
存储 设计模式 前端开发
浅谈SpringMVC五大组件以及对执行原理的分析。
Spring MVC是包含在spring中的一个基于MVC设计思想的Web应用程序框架,目的是简化开发工作,提高开发效率。
浅谈SpringMVC五大组件以及对执行原理的分析。
|
前端开发 Java 程序员
CORS跨域资源共享(三):@CrossOrigin/CorsFilter处理跨域请求示例及原理分析【享学Spring MVC】(下)
CORS跨域资源共享(三):@CrossOrigin/CorsFilter处理跨域请求示例及原理分析【享学Spring MVC】(下)
CORS跨域资源共享(三):@CrossOrigin/CorsFilter处理跨域请求示例及原理分析【享学Spring MVC】(下)