SpringMVC内容协商-扩展参数解析-2
前
在上一篇SpringMVC内容协商-扩展参数解析中利用SpringBoot提供的钩子,实现了请求入参和返参的数据解析,但是可以看到我们依赖的是RequestResponseBodyMethodProcessor
,这就要求我们在方法体上和参数上添加@ResponseBody
和@RequestBody
标签,但是如果我们不依赖这两个标签要如何实现呢?
过程
按照之前的思路,我们应该直接去扩展HandlerMethodArgumentResolver
和 HandlerMethodReturnValueHandler
而不依赖于SpringMVC
为我们提供的类,但是如何扩展?
在上一篇中,我们是利用SpringBoot
提供的SpringMVC
自动配置来完成,我们可以在去查看一下是否有提供类似的可配置的钩子。我们到WebMvcConfigurer
里查看,发现确实有一个这样的函数。
default void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {}
default void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> handlers{}
但是在使用时我们可以发现这两个函数的注释
does not override the built-in support for resolving handler method arguments.
这说明我们无法覆盖内建的解析器,意思是假使我们添加了自己解析器,但是解析器的顺序无法排在框架里自带的解析器。举个例子,如果我们实现了一个Json
数据解析器,但是使用这个配置添加到了SpringMVC
中,在匹配合适的解析器的时候还是会使用自带的,因为自带的排在我们自己编写类的前面,所有优先选择。现在我们的目标是扩展编写Properties
的解析类,但是Properties
可以被Json
数据解析器解析,所以我们就不能使用这种方式。
怎么做?我们目标就是把框架里的解析器列表取出来,然后把我们自己编写的解析器放到第一个,之后就不会匹配到自带的了。
- 首先我们先要知道这个解析器列表在那里,所以我们断点在
DispatcherServlet
的HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
中查看是哪一个子类适配。运行之后发现是RequestMappingHandlerAdapter
。 - 之后我们进入
RequestMappingHandlerAdapter
可以发现里面提供了get/setArgumentResolvers
和get/setReturnValueHandlers
两个方法,所以我们可以取出来之后把我们的实现类设置到第一个
实现
接着我们上篇的代码
创建自己的入参和返参解析类,解析类只要实现HandlerMethodArgumentResolver
和 HandlerMethodReturnValueHandler
两个接口即可。
- 入参
package com.hwk.springmvc.web.servlet.handler;
import com.hwk.springmvc.http.converter.properties.PropertiesHttpMessageConverter;
import org.springframework.core.MethodParameter;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;
import javax.servlet.http.HttpServletRequest;
import java.util.Properties;
public class PeopertiesHandlerMethodArgumentResolver implements HandlerMethodArgumentResolver {
@Override
public boolean supportsParameter(MethodParameter parameter) {
//说明支持的类型
return Properties.class.equals(parameter.getParameterType());
}
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
//在这里做处理
ServletWebRequest servletWebRequest = (ServletWebRequest) webRequest;
//Servlet Request API 获取到原生的请求
HttpServletRequest request = servletWebRequest.getRequest();
PropertiesHttpMessageConverter converter = new PropertiesHttpMessageConverter();
HttpInputMessage inputMessage = new ServletServerHttpRequest(request);
return converter.read(null, null, inputMessage);
}
}
- 返参
package com.hwk.springmvc.web.servlet.handler;
import com.hwk.springmvc.http.converter.properties.PropertiesHttpMessageConverter;
import org.springframework.core.MethodParameter;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.MediaType;
import org.springframework.http.server.ServletServerHttpResponse;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
import org.springframework.web.method.support.ModelAndViewContainer;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Properties;
public class PropertiesHandlerMethodReturnValueHandler implements HandlerMethodReturnValueHandler {
@Override
public boolean supportsReturnType(MethodParameter returnType) {
return Properties.class.equals(returnType.getParameterType());
}
@Override
public void handleReturnValue(Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer,
NativeWebRequest webRequest) throws Exception {
Properties properties = (Properties) returnValue;
PropertiesHttpMessageConverter converter = new PropertiesHttpMessageConverter();
//在这里做处理
ServletWebRequest servletWebRequest = (ServletWebRequest) webRequest;
//Servlet Request API 获取到原生的请求
HttpServletRequest request = servletWebRequest.getRequest();
//获取媒体类型
String contentType = request.getHeader("Content-Type");
MediaType mediaType = MediaType.parseMediaType(contentType);
//获取Response
HttpServletResponse response = servletWebRequest.getResponse();
HttpOutputMessage message = new ServletServerHttpResponse(response);
converter.write(properties, mediaType, message);
//通知SpringMVC 已经处理完成
mavContainer.setRequestHandled(true);
}
}
接下去就是如何配置到SpringMVC
中了。由于RequestMappingHandlerAdapter
也是一个Bean
,所以我们直接使用自动注入的方式取到,然后获取解析列表,添加我们自己的解析器就可以了。
package com.hwk.springmvc.config;
import com.hwk.springmvc.web.servlet.handler.PeopertiesHandlerMethodArgumentResolver;
import com.hwk.springmvc.http.converter.properties.PropertiesHttpMessageConverter;
import com.hwk.springmvc.web.servlet.handler.PropertiesHandlerMethodReturnValueHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.List;
@Configuration
public class RestWebMvcConfigurer implements WebMvcConfigurer {
@Autowired
private RequestMappingHandlerAdapter requestMappingHandlerAdapter;
@PostConstruct
public void init(){
//获取当前RequestMappingHandlerAdapter
List<HandlerMethodArgumentResolver> resolvers = requestMappingHandlerAdapter.getArgumentResolvers();
//因为resolvers是不可变的 所有需要创建一个容器在添加我们自定义的Resolver
List<HandlerMethodArgumentResolver> newResolvers = new ArrayList<>(resolvers.size() + 1);
//添加自定义Resolver
newResolvers.add(new PeopertiesHandlerMethodArgumentResolver());
//添加之前容器存在的Resolver
newResolvers.addAll(resolvers);
requestMappingHandlerAdapter.setArgumentResolvers(newResolvers);
List<HandlerMethodReturnValueHandler> returnValueHandlers = requestMappingHandlerAdapter.getReturnValueHandlers();
List<HandlerMethodReturnValueHandler> newReturnValues = new ArrayList<>();
newReturnValues.add(new PropertiesHandlerMethodReturnValueHandler());
newReturnValues.addAll(returnValueHandlers);
requestMappingHandlerAdapter.setReturnValueHandlers(newReturnValues);
}
}
之后我们取到两个注解在此运行也是可以成功的。