Spring MVC参数处理器的注册与顺序
到这里,一个不落的把Spring MVC内置提供的参数处理器ArgumentResolver说了个遍。
前面我有提到过:参数处理对处理器的顺序是敏感的,因此我们需要关注Spring MVC最终的执行顺序,这时候我们的聚合容器HandlerMethodArgumentResolverComposite就出场了:
public class HandlerMethodArgumentResolverComposite implements HandlerMethodArgumentResolver { private final List<HandlerMethodArgumentResolver> argumentResolvers = new LinkedList<>(); // 具有缓存 private final Map<MethodParameter, HandlerMethodArgumentResolver> argumentResolverCache = new ConcurrentHashMap<>(256); ... // @since 4.3 木有任何地方调用 public void clear() { this.argumentResolvers.clear(); } // getArgumentResolver()方法是本文的核心 @Override public boolean supportsParameter(MethodParameter parameter) { return getArgumentResolver(parameter) != null; } @Override @Nullable public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception { // 这里是关键:每个参数最多只会被一个处理器处理 HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter); if (resolver == null) { throw new IllegalArgumentException("Unsupported parameter type [" + parameter.getParameterType().getName() + "]." + " supportsParameter should be called first."); } return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory); } ... // 这块逻辑保证了每个parameter参数最多只会被一个处理器处理 // 这个从缓存的数据结构中也能够看出来的 @Nullable private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) { HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter); if (result == null) { for (HandlerMethodArgumentResolver methodArgumentResolver : this.argumentResolvers) { if (methodArgumentResolver.supportsParameter(parameter)) { result = methodArgumentResolver; this.argumentResolverCache.put(parameter, result); break; } } } return result; } }
缺省情况Spring MVC注册的处理器(顺序)如下:
它初始化处的代码如下:
RequestMappingHandlerAdapter: @Override public void afterPropertiesSet() { ... // 26个,详见方法getDefaultArgumentResolvers if (this.argumentResolvers == null) { List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers(); this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers); } // 12个 详见方法getDefaultInitBinderArgumentResolvers if (this.initBinderArgumentResolvers == null) { List<HandlerMethodArgumentResolver> resolvers = getDefaultInitBinderArgumentResolvers(); this.initBinderArgumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers); } ... }
注意:这里面initBinderArgumentResolvers最终只会有12个处理器,因为它的注册方法如下截图(也是这个顺序):
前面有提到过说标注有@InitBInder注解里也可以写很多类型的参数,但因为它只会有12个处理器,所以有些参数它是不能写的(比如@RequestBody、Errors等等这种都是不能写的),不用一一枚举,做到心中有数就成。
总结
本文介绍的处理内容,其实还是比较重要的,因为它和消息转换器HttpMessageConverter有关,毕竟它是我们目前主流的使用方式,希望可以帮助到大家理解。
这里可以提前预告一下:下篇文章将非常重要,因为我将结合实际场景,演示一个我们通过自定义HandlerMethodArgumentResolver实现的特殊场景的解决方案,非常的优雅和值得推广,有兴趣者可持续关注