美文网首页
Spring - 关于Controller类方法里的实体参数解释

Spring - 关于Controller类方法里的实体参数解释

作者: 爱蛇 | 来源:发表于2018-12-18 18:25 被阅读0次

前言

为了统一使用下滑线风格进行设计接口的请求参数以及输出参数,
最近想实现一个把 下划线格式的get请求参数转换成对应的驼峰命名风格实体类。
当然会有朋友说使用Jackson配置里的命名策略就可以了。
对,是没错,但jackson 只支持@ResponseBody 以及@RequestBody,
而对于没带任何注解的实体类参数是没法处理的,所以就想怎么在解释实体参数时可以自定义解释逻辑,

@RestController()
@RequestMapping("/v1/booking/room")
public class BookingRoomController  {

    //这里的request对象不支持像jackson那样的命名策略由下划线格式转换成驼峰实体类
   //附:这里的request参数没加任何注解,spring会当做成@ModelAttribute处理
    public PagingResult<BookingRoomDTO> list(BookingRoomSearchRequest request) {
        return roomSearchService.searchBookingRooms(request);
    }


    //这里有@RequestBody会被 Jackson根据命名策略进行处理
    public PagingResult<BookingRoomDTO> list2(@RequestBody BookingRoomSearchRequest request) {
        return roomSearchService.searchBookingRooms(request);
    }
}

关键的类或接口

  1. HandlerMethodArgumentResolver
  2. ModelAttributeMethodProcessor(spring boot 中默认加载的子类ServletModelAttributeMethodProcessor)
  3. WebDataBinder(实现类有ServletRequestDataBinder)
  4. 注解@ModelAttribute

官方有一个表格描述了controller方法参数里可以支持哪些类型:

https://docs.spring.io/spring/docs/5.1.3.RELEASE/spring-framework-reference/web.html#mvc-ann-arguments

Controller方法参数表格-1 Controller方法参数表格-2 Controller方法参数表格-3

请留意 【@ModelAttribute】以及【Any other argument】 这两行;
【Any other argument】 里说到如果判定是 Simple类型的话就当作 @RequestParam处理, 否则当作成 @ModelAttribute 处理。

Simple类型解释:

Check if the given type represents a "simple" property: a primitive, a String or other CharSequence, a Number, a Date, a URI, a URL, a Locale, a Class, or a corresponding array.

当当作@ModelAttribute处理时候,会使用ModelAttributeMethodProcessor处理。
ModelAttributeMethodProcessor实现了HandlerMethodArgumentResolver接口:

public interface HandlerMethodArgumentResolver {

    /**
     * Whether the given {@linkplain MethodParameter method parameter} is
     * supported by this resolver.
     * @param parameter the method parameter to check
     * @return {@code true} if this resolver supports the supplied parameter;
     * {@code false} otherwise
     */
    boolean supportsParameter(MethodParameter parameter);

    /**
     * Resolves a method parameter into an argument value from a given request.
     * A {@link ModelAndViewContainer} provides access to the model for the
     * request. A {@link WebDataBinderFactory} provides a way to create
     * a {@link WebDataBinder} instance when needed for data binding and
     * type conversion purposes.
     * @param parameter the method parameter to resolve. This parameter must
     * have previously been passed to {@link #supportsParameter} which must
     * have returned {@code true}.
     * @param mavContainer the ModelAndViewContainer for the current request
     * @param webRequest the current request
     * @param binderFactory a factory for creating {@link WebDataBinder} instances
     * @return the resolved argument value, or {@code null} if not resolvable
     * @throws Exception in case of errors with the preparation of argument values
     */
    @Nullable
    Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
            NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception;

}

其中方法resolveArgument 会将当前上下文参数进行转换成Controller方法上对应的参数
ModelAttributeMethodProcessor具体实现:


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

@Override
@Nullable
public final Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
        NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {

    Assert.state(mavContainer != null, "ModelAttributeMethodProcessor requires ModelAndViewContainer");
    Assert.state(binderFactory != null, "ModelAttributeMethodProcessor requires WebDataBinderFactory");

    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 {
        // Create attribute instance
        try {
            attribute = createAttribute(name, parameter, binderFactory, webRequest);
        }
        catch (BindException ex) {
            if (isBindExceptionRequired(parameter)) {
                // No BindingResult parameter -> fail with BindException
                throw ex;
            }
            // Otherwise, expose null/empty value and associated BindingResult
            if (parameter.getParameterType() == Optional.class) {
                attribute = Optional.empty();
            }
            bindingResult = ex.getBindingResult();
        }
    }

    if (bindingResult == null) {
        // Bean property binding and validation;
        // skipped in case of binding failure on construction.
        WebDataBinder binder = binderFactory.createBinder(webRequest, attribute, name);
        if (binder.getTarget() != null) {
            if (!mavContainer.isBindingDisabled(name)) {
                bindRequestParameters(binder, webRequest);
            }
            validateIfApplicable(binder, parameter);
            if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {
                throw new BindException(binder.getBindingResult());
            }
        }
        // Value type adaptation, also covering java.util.Optional
        if (!parameter.getParameterType().isInstance(attribute)) {
            attribute = binder.convertIfNecessary(binder.getTarget(), parameter.getParameterType(), parameter);
        }
        bindingResult = binder.getBindingResult();
    }

    // Add resolved attribute and BindingResult at the end of the model
    Map<String, Object> bindingResultModel = bindingResult.getModel();
    mavContainer.removeAttributes(bindingResultModel);
    mavContainer.addAllAttributes(bindingResultModel);

    return attribute;
}

相关文章

网友评论

      本文标题:Spring - 关于Controller类方法里的实体参数解释

      本文链接:https://www.haomeiwen.com/subject/zevlkqtx.html