程序员首页投稿(暂停使用,暂停投稿)

深入Spring:自定义ResponseBody

2016-05-10  本文已影响8907人  wcong

前言

上一篇文章介绍了SpringMvcRequestMappingHandlerMapping,自定义了ControllerRequestMapping
这里再介绍一下HandlerAdapterHttpMessageConverter,并通过自定义注解来实现RequestBodyResponseBodyHttpMessageConverter最常见的应用就是json的decode和encode。

RequestBody和ResponseBody

上一篇文章介绍了RequestMappingHandlerMappingDispatcherServlet的作用。
RequestMappingHandlerMapping扫描了RequestMapping注释的HttpRequest对应的处理方法,并通过实现HandlerMapping的接口代理对应的方法。
HandlerAdapter则是封装了HandlerMapping的方法,并围绕HandlerMapping实现一些嵌入操作。
RequestMappingHandlerAdapter是其中一个典型的例子,这个类包含HandlerMethodArgumentResolverHandlerMethodReturnValueHandler的一些实现类来处理RequestMapping的参数和返回值。

    private HandlerMethodArgumentResolverComposite argumentResolvers;
    private HandlerMethodReturnValueHandlerComposite returnValueHandlers;

RequestResponseBodyMethodProcessorHandlerAdapter的内部一个重要的类,这个类同时实现了HandlerMethodArgumentResolverHandlerMethodReturnValueHandler
其中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

  1. 自定义注解,因为都是附加的注解,就不需要再加上@Component的注解了。
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyResponseBody {
}
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyRequestBody {
}
  1. 定义数据结构,简便起见,这里只decode和encode特定的类。RequestData@MyRequestBody修饰的类,ResponseData@MyResponseBody修饰的类。
    public static class RequestData {
        private Map<String, String> data;
        public Map<String, String> getData() {
            return data;
        }
        public void setData(Map<String, String> data) {
            this.data = data;
        }
        public String toString() {
            return "{\"data\":" + data + "}";
        }
    }
   public static class ResponseData {
       private Map<String, String> data;
       public Map<String, String> getData() {
           return data;
       }
       public void setData(Map<String, String> data) {
           this.data = data;
       }
   }
  1. 定义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;
        }
    }
  1. 注入HandlerAdapter,并加入了两个MyResolver,只不过一个是setCustomArgumentResolvers,另一个是setCustomReturnValueHandlers
    MyResolver都加入了DataMessageConvert,这个实现了HttpMessageConverter,稍后会介绍到。
    @Configuration
    public static class MyWebMvcConfigurationSupport extends WebMvcConfigurationSupport {
        @Bean
        public RequestMappingHandlerAdapter requestMappingHandlerAdapter() {
            RequestMappingHandlerAdapter requestMappingHandlerAdapter = super.requestMappingHandlerAdapter();
            List<HttpMessageConverter<?>> converters = new ArrayList<HttpMessageConverter<?>>();
            converters.add(new DataMessageConvert());
            List<HandlerMethodArgumentResolver> argumentResolvers = new ArrayList<HandlerMethodArgumentResolver>();
            argumentResolvers.add(new MyResolver(converters));
            requestMappingHandlerAdapter.setCustomArgumentResolvers(argumentResolvers);
            List<HandlerMethodReturnValueHandler> returnValueHandlers = new ArrayList<HandlerMethodReturnValueHandler>();
            returnValueHandlers.add(new MyResolver(converters));
            requestMappingHandlerAdapter.setCustomReturnValueHandlers(returnValueHandlers);
            return requestMappingHandlerAdapter;
        }
    }
  1. MyResolver继承了AbstractMessageConverterMethodProcessor,并自定义了supportsParametersupportsReturnType来加载自定义的注解。resolveArgumenthandleReturnValue是沿用RequestResponseBodyMethodProcessor的方法,来调用HttpMessageConverter的处理方法。
    public static class MyResolver extends AbstractMessageConverterMethodProcessor {
        public MyResolver(List<HttpMessageConverter<?>> 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);
        }
    }
  1. HttpMessageConverter是处理RequestMapping的注释的方法的参数和返回值的接口类。自定义HttpMessageConverter,继承了AbstractGenericHttpMessageConverter来实现一些公用的方法。
    实现了canRead方法,只解码RequestData这个类,同样实现了canWrite了方法,只编码ResponseData这个类。
    简便起见这里只编码和解码Map<String, String>,方法也很简单,key和value直接用','链接,不同的entry之间用';'连接。
    public static class DataMessageConvert extends AbstractGenericHttpMessageConverter<Object> {
        @Override
        public boolean canRead(Type type, Class<?> contextClass, MediaType mediaType) {
            return ((Class) type).isAssignableFrom(RequestData.class);
        }
        @Override
        public boolean canWrite(Type type, Class<?> clazz, MediaType mediaType) {
            return ((Class) type).isAssignableFrom(ResponseData.class);
        }
        public List<MediaType> getSupportedMediaTypes() {
            return Collections.singletonList(MediaType.ALL);
        }
        protected boolean supports(Class<?> clazz) {
            return clazz.isAssignableFrom(Map.class);
        }
        public Object readInternal(Class<?> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
            return readMap(inputMessage);
        }
        private Object readMap(HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
            Charset cs = Charset.forName("UTF-8");
            StringBuilder stringBuilder = new StringBuilder();
            InputStream inputStream = inputMessage.getBody();
            byte[] b = new byte[1024];
            int length;
            while ((length = inputStream.read(b)) != -1) {
                ByteBuffer byteBuffer = ByteBuffer.allocate(length);
                byteBuffer.put(b, 0, length);
                byteBuffer.flip();
                stringBuilder.append(cs.decode(byteBuffer).array());
            }
            String[] list = stringBuilder.toString().split(";");
            Map<String, String> map = new HashMap<String, String>(list.length);
            for (String entry : list) {
                String[] keyValue = entry.split(",");
                map.put(keyValue[0], keyValue[1]);
            }
            RequestData requestData = new RequestData();
            requestData.setData(map);
            return requestData;
        }
        public void writeInternal(Object o, Type type, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
            StringBuilder stringBuilder = new StringBuilder();
            Map<String, String> map = ((ResponseData) o).getData();
            for (Map.Entry<String, String> entry : map.entrySet()) {
                stringBuilder.append(entry.getKey()).append(",").append(entry.getValue()).append(";");
            }
            stringBuilder.deleteCharAt(stringBuilder.length() - 1);
            outputMessage.getBody().write(stringBuilder.toString().getBytes());
        }
        public Object read(Type type, Class<?> contextClass, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
            return readMap(inputMessage);
        }
    }
  1. 程序入口,跟上一篇文章类似。
    @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()));
        }
        ...
    }

结语

这里主要介绍了HandlerAdapterHttpMessageConverterHandlerAdapter封装了HandlerMapping的方法,而HttpMessageConverter则是转换Request和Response的内容的接口,比如json的encode和decode。接下来会介绍更多关于Mvc的内容。

上一篇下一篇

猜你喜欢

热点阅读