前言
上一篇文章介绍了SpringMvc的RequestMappingHandlerMapping,自定义了Controller和RequestMapping。
这里再介绍一下HandlerAdapter和HttpMessageConverter,并通过自定义注解来实现RequestBody和ResponseBody。HttpMessageConverter最常见的应用就是json的decode和encode。
RequestBody和ResponseBody
上一篇文章介绍了RequestMappingHandlerMapping在DispatcherServlet的作用。
RequestMappingHandlerMapping扫描了RequestMapping注释的HttpRequest对应的处理方法,并通过实现HandlerMapping的接口代理对应的方法。
而HandlerAdapter则是封装了HandlerMapping的方法,并围绕HandlerMapping实现一些嵌入操作。
RequestMappingHandlerAdapter是其中一个典型的例子,这个类包含HandlerMethodArgumentResolver,HandlerMethodReturnValueHandler的一些实现类来处理RequestMapping的参数和返回值。
private HandlerMethodArgumentResolverComposite argumentResolvers;
private HandlerMethodReturnValueHandlerComposite returnValueHandlers;
RequestResponseBodyMethodProcessor是HandlerAdapter的内部一个重要的类,这个类同时实现了HandlerMethodArgumentResolver,HandlerMethodReturnValueHandler。
其中HandlerMethodArgumentResolver接口有两个方法。
public interface HandlerMethodArgumentResolver {
boolean supportsParameter(MethodParameter parameter);
Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception;
}
HandlerMethodReturnValueHandler接口同样也有两个方法。
public interface HandlerMethodReturnValueHandler {
boolean supportsReturnType(MethodParameter returnType);
void handleReturnValue(Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception;
}
所以从RequestResponseBodyMethodProcessor实现的方法,就可以看出来这个类,会处理被@RequestBody注解的参数,和@ResponseBody注解的返回值。
@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.hasParameterAnnotation(RequestBody.class);
}
@Override
public boolean supportsReturnType(MethodParameter returnType) {
return (AnnotationUtils.findAnnotation(returnType.getContainingClass(), ResponseBody.class) != null ||
returnType.getMethodAnnotation(ResponseBody.class) != null);
}
接下来就介绍一下自定义ResponseBody和RequestBody的使用方法。
自定义ResponseBody和RequestBody
- 自定义注解,因为都是附加的注解,就不需要再加上@Component的注解了。
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyResponseBody {
}
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyRequestBody {
}
- 定义数据结构,简便起见,这里只decode和encode特定的类。RequestData是@MyRequestBody修饰的类,ResponseData是@MyResponseBody修饰的类。
public static class RequestData {
private Map data;
public Map getData() {
return data;
}
public void setData(Map data) {
this.data = data;
}
public String toString() {
return "{\"data\":" + data + "}";
}
}
public static class ResponseData {
private Map data;
public Map getData() {
return data;
}
public void setData(Map data) {
this.data = data;
}
}
- 定义controller
@Controller
public static class MyController {
@RequestMapping
@MyResponseBody
public ResponseData index(@MyRequestBody RequestData requestData) {
System.out.println(requestData);
ResponseData responseData = new ResponseData();
responseData.setData(requestData.getData());
return responseData;
}
}
- 注入HandlerAdapter,并加入了两个MyResolver,只不过一个是setCustomArgumentResolvers,另一个是setCustomReturnValueHandlers。
MyResolver都加入了DataMessageConvert,这个实现了HttpMessageConverter,稍后会介绍到。
@Configuration
public static class MyWebMvcConfigurationSupport extends WebMvcConfigurationSupport {
@Bean
public RequestMappingHandlerAdapter requestMappingHandlerAdapter() {
RequestMappingHandlerAdapter requestMappingHandlerAdapter = super.requestMappingHandlerAdapter();
List> converters = new ArrayList>();
converters.add(new DataMessageConvert());
List argumentResolvers = new ArrayList();
argumentResolvers.add(new MyResolver(converters));
requestMappingHandlerAdapter.setCustomArgumentResolvers(argumentResolvers);
List returnValueHandlers = new ArrayList();
returnValueHandlers.add(new MyResolver(converters));
requestMappingHandlerAdapter.setCustomReturnValueHandlers(returnValueHandlers);
return requestMappingHandlerAdapter;
}
}
- MyResolver继承了AbstractMessageConverterMethodProcessor,并自定义了supportsParameter和supportsReturnType来加载自定义的注解。resolveArgument和handleReturnValue是沿用RequestResponseBodyMethodProcessor的方法,来调用HttpMessageConverter的处理方法。
public static class MyResolver extends AbstractMessageConverterMethodProcessor {
public MyResolver(List> converters) {
super(converters);
}
public boolean supportsParameter(MethodParameter parameter) {
return parameter.hasParameterAnnotation(MyRequestBody.class);
}
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
Object arg = readWithMessageConverters(webRequest, parameter, parameter.getGenericParameterType());
String name = Conventions.getVariableNameForParameter(parameter);
WebDataBinder binder = binderFactory.createBinder(webRequest, arg, name);
if (arg != null) {
validateIfApplicable(binder, parameter);
if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {
throw new MethodArgumentNotValidException(parameter, binder.getBindingResult());
}
}
mavContainer.addAttribute(BindingResult.MODEL_KEY_PREFIX + name, binder.getBindingResult());
return arg;
}
public boolean supportsReturnType(MethodParameter returnType) {
return returnType.getMethodAnnotation(MyResponseBody.class) != null;
}
public void handleReturnValue(Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
mavContainer.setRequestHandled(true);
writeWithMessageConverters(returnValue, returnType, webRequest);
}
}
- HttpMessageConverter是处理RequestMapping的注释的方法的参数和返回值的接口类。自定义HttpMessageConverter,继承了AbstractGenericHttpMessageConverter来实现一些公用的方法。
实现了canRead方法,只解码RequestData这个类,同样实现了canWrite了方法,只编码ResponseData这个类。
简便起见这里只编码和解码Map,方法也很简单,key和value直接用','链接,不同的entry之间用';'连接。
public static class DataMessageConvert extends AbstractGenericHttpMessageConverter
- 程序入口,跟上一篇文章类似。
@Configuration
public class CustomizeResponseBodyTest {
public static void main(String[] args) throws ServletException, IOException {
MockServletContext mockServletContext = new MockServletContext();
MockServletConfig mockServletConfig = new MockServletConfig(mockServletContext);
AnnotationConfigWebApplicationContext annotationConfigWebApplicationContext = new AnnotationConfigWebApplicationContext();
annotationConfigWebApplicationContext.setServletConfig(mockServletConfig);
annotationConfigWebApplicationContext.register(CustomizeResponseBodyTest.class);
DispatcherServlet dispatcherServlet = new DispatcherServlet(annotationConfigWebApplicationContext);
dispatcherServlet.init(mockServletConfig);
MockHttpServletResponse response = new MockHttpServletResponse();
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/");
request.addHeader("Accept", "application/json");
request.setContent(("result,hello world;date," + Calendar.getInstance().getTimeInMillis()).getBytes());
dispatcherServlet.service(request, response);
System.out.println(new String(response.getContentAsByteArray()));
}
...
}
结语
这里主要介绍了HandlerAdapter和HttpMessageConverter。HandlerAdapter封装了HandlerMapping的方法,而HttpMessageConverter则是转换Request和Response的内容的接口,比如json的encode和decode。接下来会介绍更多关于Mvc的内容。