探索 SpringBoot (五)SpringMvc Rest 处理流程源码分析
文章目录
1 缘起
接着上文 以 rest 请求为例子, 继续分析 springmvc 流程处理,本文建立上篇《探索 SpringBoot (四)SpringMvc 深度解析》的基础上继续分析
2 参数和返回值的解析转换
重点涉及 InvocableHandlerMethod,HandlerMethodArgumentResolver,HandlerMethodReturnValueHandler
3 HandlerMethodArgumentResolver 参数解析者
HandlerMethodArgumentResolver, 其实就是一个接口,主要定义了两个方法
判断该解析者是否支持该方法
boolean supportsParameter(MethodParameter parameter);
解析参数,返回 object对象
@Nullable
Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception;
4 InvocableHandlerMethod 请求方法执行者
InvocableHandlerMethod, 主要作用是解析请求参数,获取 Object 的返回值
@Nullable
public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
if (logger.isTraceEnabled()) {
logger.trace("Arguments: " + Arrays.toString(args));
}
return doInvoke(args);
}
我们先看看 getMethodArgumentValues 干了什么
...
protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
MethodParameter[] parameters = getMethodParameters();
Object[] args = new Object[parameters.length];
for (int i = 0; i < parameters.length; i++) {
MethodParameter parameter = parameters[i];
parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
args[i] = resolveProvidedArgument(parameter, providedArgs);
if (args[i] != null) {
continue;
}
if (this.argumentResolvers.supportsParameter(parameter)) {
try {
args[i] = this.argumentResolvers.resolveArgument(
parameter, mavContainer, request, this.dataBinderFactory);
continue;
}
catch (Exception ex) {
// Leave stack trace for later, e.g. AbstractHandlerExceptionResolver
if (logger.isDebugEnabled()) {
String message = ex.getMessage();
if (message != null && !message.contains(parameter.getExecutable().toGenericString())) {
logger.debug(formatArgumentError(parameter, message));
}
}
throw ex;
}
}
if (args[i] == null) {
throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
}
}
return args;
}
其实主要就是干了这两件事
1 判断是否支持该参数
2 解析该参数
this.argumentResolvers.supportsParameter(parameter)
args[i] = this.argumentResolvers.resolveArgument(
parameter, mavContainer, request, this.dataBinderFactory);
我们再看看 supportsParameter,resolveArgument具体实现逻辑是啥。
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);
if (resolver == null) {
throw new IllegalArgumentException(
"Unsupported parameter type [" + parameter.getParameterType().getName() + "]." +
" supportsParameter should be called first.");
}
return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
}
这里的 resolver 就是 RequestResponseBodyMethodProcessor
5 HandlerMethodReturnValueHandler 处理方法返回值
ServletInvocableHandlerMethod
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
// 上面讲解过的 执行方法,并且获取返回值
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
setResponseStatus(webRequest);
if (returnValue == null) {
if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
mavContainer.setRequestHandled(true);
return;
}
}
else if (StringUtils.hasText(getResponseStatusReason())) {
mavContainer.setRequestHandled(true);
return;
}
mavContainer.setRequestHandled(false);
Assert.state(this.returnValueHandlers != null, "No return value handlers");
try {
this.returnValueHandlers.handleReturnValue(
returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
}
catch (Exception ex) {
if (logger.isTraceEnabled()) {
logger.trace(formatErrorForReturnValue(returnValue), ex);
}
throw ex;
}
}
关键在这里 处理了返回值
this.returnValueHandlers.handleReturnValue(
returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
我们接着往下看
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
// 选择了最佳处理者,这里是RequestResponseBodyMethodProcessor,rest 判断了方法上是否有ResponseBody注解
HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
if (handler == null) {
throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName());
}
handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
}
handleReturnValue 值得我们看看
这里面选择了指定的http消息转换者去转换输出信息 HttpMessageConverter,我们看看这个接口的部分代码
这里获取者支持的媒体类型
List<MediaType> getSupportedMediaTypes();
// 服务端写出到客户端的时候进行返回值的处理
void write(T t, @Nullable MediaType contentType, HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotWritableException;
// 客户端传入到服务端进行的参数处理
protected abstract T readInternal(Class<? extends T> clazz, HttpInputMessage inputMessage)
throws IOException, HttpMessageNotReadableException;
6 通过 AbstractGenericHttpMessageConverter 自定义参数解析的处理
-
6.1 需求场景,有时候我们需要对用户传入的参数进行自定义的解析和返回处理
-
6.2
-- 请求
post:localhost:8080/add/props
Content-Type:text/properties
Accept:text/properties
body:
id:1
username:jun
-- 接口
@PostMapping(value = "/add/props")
public Properties addProperties( @RequestBody Properties properties) {
return properties;
}
6.3 实现 PropertiesHttpMessageConverter 继承 AbstractGenericHttpMessageConverter
public class PropertiesHttpMessageConverter extends AbstractGenericHttpMessageConverter<Properties> {
public PropertiesHttpMessageConverter() {
// 设置支持的 MediaType
super(new MediaType("text", "properties"));
}
@Override
protected void writeInternal(Properties properties, Type type, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
// Properties -> String
// OutputStream -> Writer
HttpHeaders httpHeaders = outputMessage.getHeaders();
MediaType mediaType = httpHeaders.getContentType();
// 获取字符编码
Charset charset = mediaType.getCharset();
// 当 charset 不存在时,使用 UTF-8
charset = charset == null ? Charset.forName("UTF-8") : charset;
// 字节输出流
OutputStream outputStream = outputMessage.getBody();
// 字符输出流
Writer writer = new OutputStreamWriter(outputStream, charset);
// Properties 写入到字符输出流
properties.store(writer,"From PropertiesHttpMessageConverter");
}
@Override
protected Properties readInternal(Class<? extends Properties> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
// 字符流 -> 字符编码
// 从 请求头 Content-Type 解析编码
HttpHeaders httpHeaders = inputMessage.getHeaders();
MediaType mediaType = httpHeaders.getContentType();
// 获取字符编码
Charset charset = mediaType.getCharset();
// 当 charset 不存在时,使用 UTF-8
charset = charset == null ? Charset.forName("UTF-8") : charset;
// 字节流
InputStream inputStream = inputMessage.getBody();
InputStreamReader reader = new InputStreamReader(inputStream, charset);
Properties properties = new Properties();
// 加载字符流成为 Properties 对象
properties.load(reader);
return properties;
}
@Override
public Properties read(Type type, Class<?> contextClass, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
return readInternal(null, inputMessage);
}
}
这样 mvc 就能自动解析和处理我们的返回值了
自定义参数解析和返回值处理
我们上面的自定义解析还是依赖与 @RequestBody,我们如何不依赖呢?
1 自定义 HandlerMethodArgumentResolver
public class PropertiesHandlerMethodArgumentResolver 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 {
// 复用 PropertiesHttpMessageConverter
PropertiesHttpMessageConverter converter = new PropertiesHttpMessageConverter();
ServletWebRequest servletWebRequest = (ServletWebRequest) webRequest;
// Servlet Request API
HttpServletRequest request = servletWebRequest.getRequest();
HttpInputMessage httpInputMessage = new ServletServerHttpRequest(request);
return converter.read(null, null, httpInputMessage);
}
}
2 自定义 HandlerMethodReturnValueHandler
public class PropertiesHandlerMethodReturnValueHandler implements HandlerMethodReturnValueHandler {
@Override
public boolean supportsReturnType(MethodParameter returnType) {
// 判断方法的返回类型,是否与 Properties 类型匹配
return Properties.class.equals(returnType.getMethod().getReturnType());
}
@Override
public void handleReturnValue(Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
// 强制装换
Properties properties = (Properties) returnValue;
// 复用 PropertiesHttpMessageConverter
PropertiesHttpMessageConverter converter = new PropertiesHttpMessageConverter();
ServletWebRequest servletWebRequest = (ServletWebRequest) webRequest;
// Servlet Request API
HttpServletRequest request = servletWebRequest.getRequest();
String contentType = request.getHeader("Content-Type");
// 获取请求头 Content-Type 中的媒体类型
MediaType mediaType = MediaType.parseMediaType(contentType);
// 获取 Servlet Response 对象
HttpServletResponse response = servletWebRequest.getResponse();
HttpOutputMessage message = new ServletServerHttpResponse(response);
// 通过 PropertiesHttpMessageConverter 输出
converter.write(properties, mediaType, message);
// 告知 Spring Web MVC 当前请求已经处理完毕
mavContainer.setRequestHandled(true);
}
}
3 向 requestMappingHandlerAdapter 适配者中添加解析者
@Configuration
public class RestWebMvcConfigurer implements WebMvcConfigurer {
@Autowired
private RequestMappingHandlerAdapter requestMappingHandlerAdapter;
@PostConstruct
public void init() {
// 获取当前 RequestMappingHandlerAdapter 所有的 Resolver 对象
List<HandlerMethodArgumentResolver> resolvers = requestMappingHandlerAdapter.getArgumentResolvers();
List<HandlerMethodArgumentResolver> newResolvers = new ArrayList<>(resolvers.size() + 1);
// 添加 PropertiesHandlerMethodArgumentResolver 到集合首位
newResolvers.add(new PropertiesHandlerMethodArgumentResolver());
// 添加 已注册的 Resolver 对象集合
newResolvers.addAll(resolvers);
// 重新设置 Resolver 对象集合
requestMappingHandlerAdapter.setArgumentResolvers(newResolvers);
// 获取当前 HandlerMethodReturnValueHandler 所有的 Handler 对象
List<HandlerMethodReturnValueHandler> handlers = requestMappingHandlerAdapter.getReturnValueHandlers();
List<HandlerMethodReturnValueHandler> newHandlers = new ArrayList<>(handlers.size() + 1);
// 添加 PropertiesHandlerMethodReturnValueHandler 到集合首位
newHandlers.add(new PropertiesHandlerMethodReturnValueHandler());
// 添加 已注册的 Handler 对象集合
newHandlers.addAll(handlers);
// 重新设置 Handler 对象集合
requestMappingHandlerAdapter.setReturnValueHandlers(newHandlers);
}
}
4 控制层写法
@Controller
public class PropertiesRestController {
@PostMapping(value = "/add/props")
public Properties addProperties(Properties properties) {
return properties;
}
}