JAVA进阶

SpringBoot2.x之HandlerMethodArgum

2021-12-03  本文已影响0人  小胖学编程

SpringBoot2.x之HandlerMethodArgumentResolver实战中,我们完成自定义解析器的编写。那么如何去定义解析器的优先级呢?

1. 解决方案

@Slf4j
@Configuration
public class WebMvcConfiguration implements WebMvcConfigurer {
    @Autowired
    private RequestMappingHandlerAdapter requestMappingHandlerAdapter;

    @Autowired
    private RequestMappingHandlerAdapter requestMappingHandlerAdapter;

    @PostConstruct
    public void init() {
        //获取到自定义requestMappingHandlerAdapter的属性(只读)
        List<HandlerMethodArgumentResolver> resolvers = requestMappingHandlerAdapter.getArgumentResolvers();
        //重新创建集合对象
        List<HandlerMethodArgumentResolver> newResolvers =
                new ArrayList<>(resolvers.size() + 1);
        // 添加 自定义解析器 到集合首位
        newResolvers.add(new UserInfoArgumentResolver(tokenService));
        // 添加 已注册的 Resolver 对象集合
        newResolvers.addAll(resolvers);
        // 重新设置 Resolver 对象集合
        requestMappingHandlerAdapter.setArgumentResolvers(newResolvers);
    }
}

2. 源码分析

2.1 难点:顺序是定死的

image.png

源码位置:org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#getDefaultArgumentResolvers

    /**
     * Return the list of argument resolvers to use including built-in resolvers
     * and custom resolvers provided via {@link #setCustomArgumentResolvers}.
     */
    private List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers() {
        List<HandlerMethodArgumentResolver> resolvers = new ArrayList<>();

        // 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());

        // Custom arguments
        if (getCustomArgumentResolvers() != null) {
            resolvers.addAll(getCustomArgumentResolvers());
        }

        // Catch-all
        resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true));
        resolvers.add(new ServletModelAttributeMethodProcessor(true));

        return resolvers;
    }

如图所示,加载的顺序是

  1. 基于注解的解析器;
  2. 基于Type类型的解析器;
  3. 自定义解析器;

且由于本方法是私有方法,也无法继承RequestMappingHandlerAdapter后进行重写。

2.2 遇见的问题:自定义注解无法获取Map参数

若是在自定义解析器中,声明一个基于注解的解析器,但是方法参数是Map,那么会优先MapMethodProcessor解析器。

源码位置:org.springframework.web.method.support.HandlerMethodArgumentResolverComposite#getArgumentResolver

    /**
     * Find a registered {@link HandlerMethodArgumentResolver} that supports the given method parameter.
     */
    @Nullable
    private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {
        //在缓存中获取parameter的解析器
        HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);
         //第一次肯定为空
        if (result == null) {
            // 遍历所有的解析器,寻找合适的解析器
            for (HandlerMethodArgumentResolver methodArgumentResolver : this.argumentResolvers) {
                if (logger.isTraceEnabled()) {
                    logger.trace("Testing if argument resolver [" + methodArgumentResolver + "] supports [" +
                            parameter.getGenericParameterType() + "]");
                }
                if (methodArgumentResolver.supportsParameter(parameter)) {
                    result = methodArgumentResolver;
                    this.argumentResolverCache.put(parameter, result);
                    break;
                }
            }
        }
        return result;
    }

解决方案如1中所示,即get到所有解析器后,然后再次set进去。

上一篇下一篇

猜你喜欢

热点阅读