一、背景
一位同学写了个自定义参数处理类,具体是继承AbstractNamedValueMethodArgumentResolver,即将json字符串参数转换为一个Object,
使用如下:
代码语言:javascript复制 @ResponseBody
@RequestMapping("/test")
public Object test(@RequestJsonParam("jsonKey")UserVo userVo){
//具体业务逻辑处理
}
请求时参数如下:
就一个参数,名为jsonKey,值为:
代码语言:javascript复制{"id":"123","name":"edward"}
即加了RequestJsonParam注解后,会将请求中这个key对应的值转为相应的对象,上例中转成UserVo,这个Vo有两个属性,一个是id,另一个是name。
Xml配置如下:
代码语言:javascript复制
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
<property name="messageConverters">
<list>
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter" />
</list>
</property>
</bean>
<!-- 基于注解驱动 的MVC -->
<mvc:annotation-driven>
<mvc:argument-resolvers>
<bean class="com.oneplus.common.web.mvc.bind.RequestJsonParamMethodArgumentResolver"/>
</mvc:argument-resolvers>
</mvc:annotation-driven>
结果是死活不生效,即参数里的值收到请求后全为空。
二、问题解决
经过调试,修改如下问题解决:
代码语言:javascript复制<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
<property name="messageConverters">
<list>
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter" />
</list>
</property>
<property name="customArgumentResolvers">
<list>
<bean class="com.oneplus.common.web.mvc.bind.RequestJsonParamMethodArgumentResolver"></bean>
</list>
</property>
</bean>
或者把上面的bean去掉也是以解决的。
三、问题分析
为什么不加自定义的bean或在上面自定义Bean中加上customArgumentResolvers才能生效呢,还是要看Spring MVC请求处理流程,我们知道Spring Mvc有个Servelet接管所有请求,它就是DispatcherServlet,它在初始化时,有初步化HandlerAdapter:
代码语言:javascript复制private void initHandlerAdapters(ApplicationContext context) {
this.handlerAdapters = null;
if (this.detectAllHandlerAdapters) {
//从容器中获取相应bean的实现,就包括下面要讲的RequestMappingHandlerAdapter
Map<String, HandlerAdapter> matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerAdapter.class, true, false);
if (!matchingBeans.isEmpty()) {
this.handlerAdapters = new ArrayList<>(matchingBeans.values());
// We keep HandlerAdapters in sorted order.
AnnotationAwareOrderComparator.sort(this.handlerAdapters);
}
}
//省略无关代码
}
这个Adapter后面是用来真正处理请求的,下面是处理请求的doDispatch方法代码:
代码语言:javascript复制protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// Determine handler for the current request.
mappedHandler = getHandler(processedRequest);
// Determine handler adapter for the current request.
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
//省略无关代码
// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
//省略无关代码
}
即先找到相应的HandleAdapter,然后调用它的handle方法,其中有一个就是我们上面说的RequestMappingHandlerAdapter,后者最终又会调到RequestMappingHandlerAdapter的invokeHandlerMethod:
代码语言:javascript复制protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ServletWebRequest webRequest = new ServletWebRequest(request, response);
try {
WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
//加入自定义参数处理器
if (this.argumentResolvers != null) {
invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
}
}
如果我们不加入自定义的Bean,即只有下面的配置:
代码语言:javascript复制 <mvc:annotation-driven>
<mvc:argument-resolvers>
<bean class="com.oneplus.common.web.mvc.bind.RequestJsonParamMethodArgumentResolver"/>
</mvc:argument-resolvers>
</mvc:annotation-driven>
则上面DispatcherServlet的initHandlerAdapters方法就会取系统的初始化RequestMappingHandlerAdapter,在这里会把customArgumentResolvers参数传进来。
而如果我们加入RequestMappingHandlerAdapter bean的配置:
代码语言:javascript复制<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
<property name="messageConverters">
<list>
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter" />
</list>
</property>
</bean>
则DispatcherServlet就会用这个的bean,因此需要自己设置好自定义参数处理器,即加上customArgumentResolvers属性。