HandlerMethodReturnValueHandler
和HandlerMethodArgumentResolver
面向的是控制器返回值和方法入参类型,即当请求一个控制器方法时,SpringMVC根据请求的控制器的方法入参类型决定使用哪个HandlerMethodArgumentResolver
,例如下面的代码
@PostMapping(value = "/properties",
consumes = "text/properties;charset=utf-8")
public Properties properties(@RequestBody Properties properties){
return properties;
}
由于入参参数存在@RequestBody
注解,因此参数处理器使用的是RequestResponseBodyMethodProcessor
,而Rest应用存在@ResponseBody
注解,因此返回值的处理器也是RequestResponseBodyMethodProcessor
不同的处理器在处理参数和返回值时方法不同,但大多的套路都是:
- 使用
MessageConverters
对进来的参数进行解析拿到值 - 解析参数对应的参数名称
- 使用
WebDataBinder
进行参数名称与参数值的绑定
MessageConverters
的作用是对传进来的参数进行解析,不同的转换器支持不同的媒体类型,在解析时会遍历已注册的转换器进行尝试解析
下面在基于RequestResponseBodyMethodProcessor
的情况下自定义新的HttpMessageConverter
用于解析我们自定义的媒体类型text/Properties
由于是Rest风格,使用的是RequestResponseBodyMethodProcessor
,我们模仿jackson的convert进行仿写
public class PropertiesConvert extends AbstractGenericHttpMessageConverter<Properties> {
/**
* 支持的处理类型
*/
public PropertiesConvert() {
super(new MediaType("text","properties"));
}
/**
* 返回值处理
*/
@Override
protected void writeInternal(Properties properties, Type type, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
HttpHeaders httpHeaders = outputMessage.getHeaders();
Charset charset = httpHeaders.getContentType().getCharset();
charset = charset == null ? Charset.forName("utf-8") : charset;
OutputStream outputStream = outputMessage.getBody();
Writer writer = new OutputStreamWriter(outputStream,charset);
properties.store(writer,"my convert");
}
/**
* 入参转换
*/
@Override
protected Properties readInternal(Class<? extends Properties> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
HttpHeaders httpHeaders = inputMessage.getHeaders();
Charset charset = httpHeaders.getContentType().getCharset();
charset = charset == null ? Charset.forName("utf-8") : charset;
InputStream inputStream = inputMessage.getBody();
InputStreamReader reader = new InputStreamReader(inputStream,charset);
Properties properties = new Properties();
properties.load(reader);
return properties;
}
@Override
public Properties read(Type type, Class<?> contextClass, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
return readInternal(null,inputMessage);
}
}
@Configuration
public class MvcConfig extends WebMvcConfigurerAdapter {
@Override
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
//由于json可以处理properties,如果不指定位置添加是添加到末尾,在选择convert处理时会优先选择json的而不是我们自定义的
converters.add(0,new PropertiesConvert());
}
}
我们没有指定返回媒体类型,因此是所有的都可以。但由于我们自定义的添加到了第一个位置,所以会用完没自定义的这个convert进行转换返回