上一篇介绍了springmvc参数绑定的原理
现在让我们尝试自己定义一个参数转换器
先定义一个比较简单的处理器,在写接口的时候,经常需要对分页参数进行处理,比如下面这样
String offset =request.getParameter("offset");
String size = request.getParameter("pageSize");
//拼装成bean或者直接使用
这样会导致很多重复代码,参数处理器可以很好的解决这个问题
首先先定义一个分页参数的bean
public class Pageable implements Serializable {
private Integer offset; // 起始行号
private Integer size; // 每页大小
public Pageable() {
}
public Pageable(Integer offset, Integer size) {
this.offset = offset;
this.size = size;
}
//省略get,set
}
定义参数转换器
public class PageableMethodArgumentResolver implements HandlerMethodArgumentResolver {
private final String DEFAULT_OFFSET = "0";
private final String DEFAULT_SIZE = "10";
@Override
public boolean supportsParameter(MethodParameter methodParameter) {
return Pageable.class.isAssignableFrom(methodParameter.getParameterType());
}
@Override
public Object resolveArgument(MethodParameter methodParameter, ModelAndViewContainer modelAndViewContainer, NativeWebRequest nativeWebRequest, WebDataBinderFactory webDataBinderFactory) throws Exception {
HttpServletRequest request = (HttpServletRequest) nativeWebRequest.getNativeRequest();
String offset = request.getParameter("offset");
String size = request.getParameter("pageSize");
if (offset.isEmpty()||size.isEmpty()) {
offset = DEFAULT_OFFSET;
size = DEFAULT_SIZE;
}
return new Pageable(Integer.valueOf(offset), Integer.valueOf(size));
}
}
最后在配置文件注册该处理器(这是spring4.0以下的注册方式,4.0以上注册方式参考下面的)
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
<property name="customArgumentResolvers">
<list>
<bean class="com.catfish.PageableMethodArgumentResolver"/>
</list>
</property>
<property name="order" value="-1"/>
</bean>
下面是定义一个比较复杂的转换器
需求:将json格式的请求在参数中获取值,类似于
用到的jar包除了spring的还有jsonpath原来解析json
<dependency>
<groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path</artifactId>
<version>2.2.0</version>
</dependency>
因为是json格式的报文,所有我们只需要模仿@RequestBody
注解的处理,查看源码可知@RequestBody
注解对应的处理器为RequestResponseBodyMethodProcessor
,
对json处理核心方法将json转换为bean
readWithMessageConverters
方法,
该方法是由其父类AbstractMessageConverterMethodArgumentResolver
实现,对应uml图
所以我们只允许继承AbstractMessageConverterMethodArgumentResolver
并重写supportsParameter
,readWithMessageConverters
方法即可实现我们需要的功能
分析结束,现在开始写具体的实现,首先我们需要一个注解用来标注参数,对注解不了解的可以看java注解笔记
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
@Documented
public @interface JsonRequest {
String value() default "";
boolean required() default true;
}
然后写对应的参数转换器,这边只写核心的方法,具体的查看github,地址在文章末尾
protected <T> Object readWithMessageConverters(HttpInputMessage inputMessage, MethodParameter param, Type targetType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException {
try {
inputMessage = new EmptyBodyCheckingHttpInputMessage(inputMessage);
InputStream inputStream = inputMessage.getBody();
String json;
if (inputStream == null) {
json = threadLocal.get();
} else {
json = StreamUtils.copyToString(inputStream, Charset.defaultCharset());
threadLocal.set(json);
}
JsonRequest jsonRequest = param.getParameterAnnotation(JsonRequest.class);
return JsonPath.read(json, jsonRequest.value());
} catch (PathNotFoundException ex) {
return null;
}
}
在配置文件添加配置
<mvc:annotation-driven>
<mvc:argument-resolvers>
<bean class="com.lialzm.spring.support.JsonRequestMethodArgumentResolver">
<constructor-arg>
<list>
<!--只是用来防止报错,目前并没有使用到消息转换器-->
<ref bean="byteArrayHttpMessageConverter"/>
</list>
</constructor-arg>
</bean>
</mvc:argument-resolvers>
<mvc:message-converters>
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="supportedMediaTypes">
<list>
<value>application/json;charset=UTF-8</value>
</list>
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
添加一个controller
@RequestMapping("/getUserByJson2")
@ResponseBody
public String getUserByJson2(@JsonRequest("$.id") String id, @JsonRequest("$.name") String name) {
return id + "," + name;
}
测试发送参数
{
"id":"1",
"name":"qqq"
}
返回
"1,qqq"
验证成功
这时将参数id类型改成Long类型发现报错
Request processing failed; nested exception is java.lang.IllegalStateException: argument type mismatch
参数类型不匹配,因为resolveArgument返回的类型和请求的参数类型是相同的,发送的是string类型用Long类型承接自然就报错了,查看AbstractNamedValueMethodArgumentResolver
的源码,里面有个binder.convertIfNecessary方法,就是原来处理类型转换的,这个类型转换下一篇再写
源码地址:
https://github.com/lialzmChina/javaeeLearn.git
spring002