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;
}
如图所示,加载的顺序是
- 基于注解的解析器;
- 基于Type类型的解析器;
- 自定义解析器;
且由于本方法是私有方法,也无法继承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进去。