@InitBinder and ModelAttributeMethodProcessor 处理 @ModelAttribute 和兜底无注解

发布时间 2023-11-23 17:25:52作者: YangDanMua

参考:springmvc--8-ServletModelAttributeMethodProcessor兜底处理@ModelAttribute注解和无注解

image

继承关系比较简单,两个接口处理 Controller 参数和返回值的,ModelAttributeMethodProcessor 本身也不是一个抽象类。

构造函数和字段

annotationNotRequired 为 true 则支持不使用 @ModelAttribut 参数时的非简单参数/返回值的处理

public class ServletModelAttributeMethodProcessor extends ModelAttributeMethodProcessor {
  public ServletModelAttributeMethodProcessor(boolean annotationNotRequired) {
    super(annotationNotRequired);
  }
}

public class ModelAttributeMethodProcessor implements HandlerMethodArgumentResolver, HandlerMethodReturnValueHandler {
  private final boolean annotationNotRequired;

  public ModelAttributeMethodProcessor(boolean annotationNotRequired) {
    this.annotationNotRequired = annotationNotRequired;
  }
}

支持的参数和返回值

使用 ModelAttribute 注解 or annotationNotRequired 为 true 时的非简单类型(BeanUtils 判断,不仅仅是基本数据类型)

public boolean supportsParameter(MethodParameter parameter) {
  return (parameter.hasParameterAnnotation(ModelAttribute.class) ||
      (this.annotationNotRequired && !BeanUtils.isSimpleProperty(parameter.getParameterType())));
}

public boolean supportsReturnType(MethodParameter returnType) {
  return (returnType.hasMethodAnnotation(ModelAttribute.class) ||
      (this.annotationNotRequired && !BeanUtils.isSimpleProperty(returnType.getParameterType())));
}

处理返回值

@Override
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
    ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {

  if (returnValue != null) {
    // name 大概是处理成 com.example.Demo ---> demo 这样
    String name = ModelFactory.getNameForReturnValue(returnValue, returnType);
    // 设置 model, k-v
    mavContainer.addAttribute(name, returnValue);
  }
}

处理请求参数

调用栈

resolveArgument:129, ModelAttributeMethodProcessor
resolveArgument:122, HandlerMethodArgumentResolverComposite
getMethodArgumentValues:179, InvocableHandlerMethod
invokeForRequest:146, InvocableHandlerMethod
invokeAndHandle:117, ServletInvocableHandlerMethod
invokeHandlerMethod:895, RequestMappingHandlerAdapter
handleInternal:808, RequestMappingHandlerAdapter
handle:87, AbstractHandlerMethodAdapter
doDispatch:1067, DispatcherServlet
public final Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
    NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
  // 参数名称
  String name = ModelFactory.getNameForParameter(parameter);
  ModelAttribute ann = parameter.getParameterAnnotation(ModelAttribute.class);
  if (ann != null) {
    // 是否禁用绑定配置, 不知道干啥的
    mavContainer.setBinding(name, ann.binding());
  }

  Object attribute = null;
  BindingResult bindingResult = null;
  
  // 已经有这个名称了
  if (mavContainer.containsAttribute(name)) {
    attribute = mavContainer.getModel().get(name);
  }
  else {
    // 没有则创建
    attribute = createAttribute(name, parameter, binderFactory, webRequest);
    
  }

  if (bindingResult == null) {
    // 绑定器
    WebDataBinder binder = binderFactory.createBinder(webRequest, attribute, name);
    // 上面的 attribute 就是 target
    if (binder.getTarget() != null) {
      // 没有被禁, 则绑定
      if (!mavContainer.isBindingDisabled(name)) {
        bindRequestParameters(binder, webRequest);
      }
      // 绑定后的 valid 校验
      validateIfApplicable(binder, parameter);
      // 有错误是否抛出绑定错误
      if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {
        throw new BindException(binder.getBindingResult());
      }
    }
    // 尝试进行类型转换, attribute 是否为此类的实例
    if (!parameter.getParameterType().isInstance(attribute)) {
      attribute = binder.convertIfNecessary(binder.getTarget(), parameter.getParameterType(), parameter);
    }
    bindingResult = binder.getBindingResult();
  }

  // 绑定结果
  Map<String, Object> bindingResultModel = bindingResult.getModel();
  mavContainer.removeAttributes(bindingResultModel);
  mavContainer.addAllAttributes(bindingResultModel);

  return attribute;
}
createAttribute
// 在 ModelAttributeMethodProcessor 就是尝试创建实例, 有构造函数参数则尝试从请求参数中获取
// ServletModelAttributeMethodProcessor 
//  - 先尝试从 UriTemplateVariables 即 @PathVariable 中找值, 存在则尝试创建实例(并转换类型)
//  - 不行则调用 ModelAttributeMethodProcessor 的

WebDataBinderFactory 和 WebDataBinder

WebDataBinderFactory
在这创建:RequestMappingHandlerAdapter#invokeHandlerMethod
创建时依赖 HandlerMethod。

private WebDataBinderFactory getDataBinderFactory(HandlerMethod handlerMethod) throws Exception {
  // Controller(或被代理过的)
  Class<?> handlerType = handlerMethod.getBeanType();
  // @InitBinder 注解的方法, 这里是缓存
  Set<Method> methods = this.initBinderCache.get(handlerType);
  // 没有则尝试找, 找过的没有会是 empty, 【MethodIntrospector】
  if (methods == null) {
    methods = MethodIntrospector.selectMethods(handlerType, INIT_BINDER_METHODS);
    this.initBinderCache.put(handlerType, methods);
  }
  List<InvocableHandlerMethod> initBinderMethods = new ArrayList<>();
  // Global methods first
  // ControllerAdvice 的 @InitBinder
  this.initBinderAdviceCache.forEach((controllerAdviceBean, methodSet) -> {
    if (controllerAdviceBean.isApplicableToBeanType(handlerType)) {
      Object bean = controllerAdviceBean.resolveBean();
      for (Method method : methodSet) {
        initBinderMethods.add(createInitBinderMethod(bean, method));
      }
    }
  });
  // createInitBinderMethod 生成 InvocableHandlerMethod, 封装后感觉有点和 HandlerMethod 一样了, 方法参数也是可以在请求参数里查找的
  for (Method method : methods) {
    Object bean = handlerMethod.getBean();
    initBinderMethods.add(createInitBinderMethod(bean, method));
  }
  return createDataBinderFactory(initBinderMethods);
}


private InvocableHandlerMethod createInitBinderMethod(Object bean, Method method) {
  InvocableHandlerMethod binderMethod = new InvocableHandlerMethod(bean, method);
  // 每个 InvocableHandlerMethod 都能得所有得参数解析器
  if (this.initBinderArgumentResolvers != null) {
    binderMethod.setHandlerMethodArgumentResolvers(this.initBinderArgumentResolvers);
  }
  // 数据绑定器 & 绑定器得初始化(含转换器、校验器等)
  binderMethod.setDataBinderFactory(new DefaultDataBinderFactory(this.webBindingInitializer));
  // 参数名称解析器
  binderMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
  return binderMethod;
}

protected InitBinderDataBinderFactory createDataBinderFactory(List<InvocableHandlerMethod> binderMethods)
    throws Exception {
  return new ServletRequestDataBinderFactory(binderMethods, getWebBindingInitializer());
}

WebDataBinder的创建
DefaultDataBinderFactory#createBinder

public final WebDataBinder createBinder(
    NativeWebRequest webRequest, @Nullable Object target, String objectName) throws Exception {
  // 目标实例, 实例名称, 请求
  WebDataBinder dataBinder = createBinderInstance(target, objectName, webRequest);
  // factory 创建时得到的各种 Conversion、validator 等
  if (this.initializer != null) {
    this.initializer.initBinder(dataBinder, webRequest);
  }
  // @InitBinder 调用
  initBinder(dataBinder, webRequest);
  return dataBinder;
}



public void initBinder(WebDataBinder dataBinder, NativeWebRequest request) throws Exception {
  for (InvocableHandlerMethod binderMethod : this.binderMethods) {
    if (isBinderMethodApplicable(binderMethod, dataBinder)) {
      // 不能有返回值
      // 传了 WebDataBinder 可以向其中加一些东西, 比如
      Object returnValue = binderMethod.invokeForRequest(request, null, dataBinder);
      if (returnValue != null) {
        throw new IllegalStateException(
            "@InitBinder methods must not return a value (should be void): " + binderMethod);
      }
    }
  }
}


@InitBinder
public void initBinder(WebDataBinder binder){
  // Date 类型转换
  binder.registerCustomEditor(Date.class, new PropertyEditorSupport(){
    @Override
    public void setAsText(String text){
        setValue(DateUtils.parseDate(text));
    }
  });
}

ModelFactory 的创建
同样是在这里 RequestMappingHandlerAdapter#invokeHandlerMethod

private ModelFactory getModelFactory(HandlerMethod handlerMethod, WebDataBinderFactory binderFactory) {
  // @SessionAttributes
  SessionAttributesHandler sessionAttrHandler = getSessionAttributesHandler(handlerMethod);
  Class<?> handlerType = handlerMethod.getBeanType();
  Set<Method> methods = this.modelAttributeCache.get(handlerType);
  // 没有 @RequestMapping 但是有 @ModelAttribute 的方法, 这种方法在每个实际 Controller 请求之前会被调用此役
  if (methods == null) {
    methods = MethodIntrospector.selectMethods(handlerType, MODEL_ATTRIBUTE_METHODS);
    this.modelAttributeCache.put(handlerType, methods);
  }
  List<InvocableHandlerMethod> attrMethods = new ArrayList<>();
  // Global methods first
  // 存粹的 ControllerAdvice 方法 ?
  this.modelAttributeAdviceCache.forEach((controllerAdviceBean, methodSet) -> {
    if (controllerAdviceBean.isApplicableToBeanType(handlerType)) {
      Object bean = controllerAdviceBean.resolveBean();
      for (Method method : methodSet) {
        attrMethods.add(createModelAttributeMethod(binderFactory, bean, method));
      }
    }
  });
  
  // @ModelAttribute 方法
  for (Method method : methods) {
    Object bean = handlerMethod.getBean();
    attrMethods.add(createModelAttributeMethod(binderFactory, bean, method));
  }

  // 按顺序应该是先 ControllerAdvice ? 然后 @ModelAttribute
  // ModelFactory 里面会拆分出依赖的 @ModelAttribute(需要在 Controller 方法执行之前执行)
  return new ModelFactory(attrMethods, binderFactory, sessionAttrHandler);
}

大概看下外层的构建
RequestMappingHandlerAdapter#invokeHandlerMethod

@Nullable
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
    HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
  // 封装一下
  ServletWebRequest webRequest = new ServletWebRequest(request, response);
  try {
    // DataBinder, 处理了 @InitBinder
    WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
    // 处理了 @ModelAttribute
    ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
    // 再封装一层
    ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
    // 参数解析
    if (this.argumentResolvers != null) {
      invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
    }
    // 返回值处理
    if (this.returnValueHandlers != null) {
      invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
    }
    // 
    invocableMethod.setDataBinderFactory(binderFactory);
    invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
    // ModelAndViewContainer
    ModelAndViewContainer mavContainer = new ModelAndViewContainer();
    mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
    // @ModelAttribute 方法在内部已经被调用了
    modelFactory.initModel(webRequest, mavContainer, invocableMethod);
    mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
    // 调用
    invocableMethod.invokeAndHandle(webRequest, mavContainer);
    // ModelAndView
    return getModelAndView(mavContainer, modelFactory, webRequest);
  }
  finally {
    // 请求处理结束
    webRequest.requestCompleted();
  }
}

调用概略

RequestMappingHandlerAdapter#invokeHandlerMethod
  -> ServletInvocableHandlerMethod#invokeAndHandle
    -> InvocableHandlerMethod#invokeForRequest
      -> InvocableHandlerMethod#getMethodArgumentValues
        -> 进行参数解析
      -> InvocableHandlerMethod#doInvoke
        -> 方法调用

ServletInvocableHandlerMethod
说是封装 HandlerMethod,实际上封装了:@InitBinder、@ModelAttribute、ExceptionResolver