上一篇介紹了springmvc參數(shù)綁定的原理
現(xiàn)在讓我們嘗試自己定義一個(gè)參數(shù)轉(zhuǎn)換器
先定義一個(gè)比較簡(jiǎn)單的處理器,在寫(xiě)接口的時(shí)候,經(jīng)常需要對(duì)分頁(yè)參數(shù)進(jìn)行處理,比如下面這樣
String offset =request.getParameter("offset");
String size = request.getParameter("pageSize");
//拼裝成bean或者直接使用
這樣會(huì)導(dǎo)致很多重復(fù)代碼,參數(shù)處理器可以很好的解決這個(gè)問(wèn)題
首先先定義一個(gè)分頁(yè)參數(shù)的bean
public class Pageable implements Serializable {
private Integer offset; // 起始行號(hào)
private Integer size; // 每頁(yè)大小
public Pageable() {
}
public Pageable(Integer offset, Integer size) {
this.offset = offset;
this.size = size;
}
//省略get,set
}
定義參數(shù)轉(zhuǎn)換器
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));
}
}
最后在配置文件注冊(cè)該處理器(這是spring4.0以下的注冊(cè)方式,4.0以上注冊(cè)方式參考下面的)
<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>
下面是定義一個(gè)比較復(fù)雜的轉(zhuǎn)換器
需求:將json格式的請(qǐng)求在參數(shù)中獲取值,類(lèi)似于

用到的jar包除了spring的還有jsonpath原來(lái)解析json
<dependency>
<groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path</artifactId>
<version>2.2.0</version>
</dependency>
因?yàn)槭莏son格式的報(bào)文,所有我們只需要模仿@RequestBody注解的處理,查看源碼可知@RequestBody注解對(duì)應(yīng)的處理器為RequestResponseBodyMethodProcessor,
對(duì)json處理核心方法將json轉(zhuǎn)換為bean
readWithMessageConverters方法,
該方法是由其父類(lèi)AbstractMessageConverterMethodArgumentResolver實(shí)現(xiàn),對(duì)應(yīng)uml圖

所以我們只允許繼承AbstractMessageConverterMethodArgumentResolver并重寫(xiě)supportsParameter,readWithMessageConverters方法即可實(shí)現(xiàn)我們需要的功能
分析結(jié)束,現(xiàn)在開(kāi)始寫(xiě)具體的實(shí)現(xiàn),首先我們需要一個(gè)注解用來(lái)標(biāo)注參數(shù),對(duì)注解不了解的可以看java注解筆記
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
@Documented
public @interface JsonRequest {
String value() default "";
boolean required() default true;
}
然后寫(xiě)對(duì)應(yīng)的參數(shù)轉(zhuǎn)換器,這邊只寫(xiě)核心的方法,具體的查看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>
<!--只是用來(lái)防止報(bào)錯(cuò),目前并沒(méi)有使用到消息轉(zhuǎn)換器-->
<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>
添加一個(gè)controller
@RequestMapping("/getUserByJson2")
@ResponseBody
public String getUserByJson2(@JsonRequest("$.id") String id, @JsonRequest("$.name") String name) {
return id + "," + name;
}
測(cè)試發(fā)送參數(shù)
{
"id":"1",
"name":"qqq"
}
返回
"1,qqq"
驗(yàn)證成功
這時(shí)將參數(shù)id類(lèi)型改成Long類(lèi)型發(fā)現(xiàn)報(bào)錯(cuò)
Request processing failed; nested exception is java.lang.IllegalStateException: argument type mismatch
參數(shù)類(lèi)型不匹配,因?yàn)閞esolveArgument返回的類(lèi)型和請(qǐng)求的參數(shù)類(lèi)型是相同的,發(fā)送的是string類(lèi)型用Long類(lèi)型承接自然就報(bào)錯(cuò)了,查看AbstractNamedValueMethodArgumentResolver的源碼,里面有個(gè)binder.convertIfNecessary方法,就是原來(lái)處理類(lèi)型轉(zhuǎn)換的,這個(gè)類(lèi)型轉(zhuǎn)換下一篇再寫(xiě)
源碼地址:
https://github.com/lialzmChina/javaeeLearn.git
spring002