今天在使用其他传输协议的时候,踩了点坑,记录一下。
主要是针对Protobuf的应用时,不想去写手动转换字节的方式,这种方法太low了。
另外问题也会比较多。
像网上一些针对protobuf对象的转化,都是读取字节然后重新去构造一个对象。
代码如下,这是从网上看到的例子。 每次都要去转化,有点麻烦。
@Controller
@RequestMapping("/pbtest")
public class TestController {
@RequestMapping("upload")
public void upload(HttpServletRequest request, HttpServletResponse response) throws IOException {
InputStream inputStream = request.getInputStream();
AddressBook addressBook = AddressBook.parseFrom(inputStream);
inputStream.close();
System.out.println(addressBook);
}
}
如何解决这个转化的问题。其实Spring本身已经提供了解决方案。就是mvc的消息转化器支持拓展。
springmvc自动装配的入口
具体源码如下,首先还是回到springmvc自动装配的入口
@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
@AutoConfigureAfter({ DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class,
ValidationAutoConfiguration.class })
public class WebMvcAutoConfiguration {
。。。内容省略
}
从上面的源码来看,有一个注解@ConditionalOnMissingBean(WebMvcConfigurationSupport.class),这个注解就是提供拓展的关键,如果容器里面没有这个类型的bean,就默认使用WebMvcConfigurationSupport.class。
提供拓展的类WebMvcConfigurationSupport
public class WebMvcConfigurationSupport implements ApplicationContextAware, ServletContextAware {
@Nullable
private List<HttpMessageConverter<?>> messageConverters;
/**
* Override this method to add custom {@link HttpMessageConverter HttpMessageConverters}
* to use with the {@link RequestMappingHandlerAdapter} and the
* {@link ExceptionHandlerExceptionResolver}.
* <p>Adding converters to the list turns off the default converters that would
* otherwise be registered by default. Also see {@link #addDefaultHttpMessageConverters}
* for adding default message converters.
* @param converters a list to add message converters to (initially an empty list)
*/
protected void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
}
}
里面的这个方法,其实就是springmvc给我们提供用来拓展消息转化器的方法。
如何实现
@Configuration
public class CustomWebMvcConfigurationSupport extends WebMvcConfigurationSupport {
//添加protobuf转化器
@Override
protected void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(new ProtobufHttpMessageConverter());
}
}
另外controller中还是跟以前一样,切记@RequestBody与@ResponseBody都不能丢,否则入参不会被消息转化器处理。
@Controller
@RequestMapping("/demo")
public class DemoController {
@Autowired
private PersonApiService personApiService;
@PostMapping("/addPerson")
@ResponseBody
public Response addPerson(@RequestBody AddPerson addPerson) throws IOException {
return personApiService.addPerson(addPerson);
}
}
ProtobufHttpMessageConverter如何处理对应的请求里的参数?
public class ProtobufHttpMessageConverter extends AbstractHttpMessageConverter<Message> {
@Override
protected boolean supports(Class<?> clazz) {
return Message.class.isAssignableFrom(clazz);
}
@Override
public boolean canRead(Class<?> clazz, @Nullable MediaType mediaType) {
return supports(clazz) && canRead(mediaType);
}
@Override
public boolean canWrite(Class<?> clazz, @Nullable MediaType mediaType) {
return supports(clazz) && canWrite(mediaType);
}
}
从上面方法来看,能不能读或者写,完全是看入参是不是Message类型。Message是protobuf对象的一个通用类型。
原理
需要用到的类
HttpMessageConverter
消息转化器,其实就是用于处理http请求中传输的内容,根据协议进行转化。
常见的有包括处理json,xml,以及其他各种协议的消息转化器
public interface HttpMessageConverter<T> {
boolean canRead(Class<?> clazz, @Nullable MediaType mediaType);
boolean canWrite(Class<?> clazz, @Nullable MediaType mediaType);
List<MediaType> getSupportedMediaTypes();
T read(Class<? extends T> clazz, HttpInputMessage inputMessage)
throws IOException, HttpMessageNotReadableException;
void write(T t, @Nullable MediaType contentType, HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotWritableException;
}
AbstractHttpMessageConverter
进行了部分实现
@Override
public boolean canRead(Class<?> clazz, @Nullable MediaType mediaType) {
return supports(clazz) && canRead(mediaType);
}
protected boolean canRead(@Nullable MediaType mediaType) {
if (mediaType == null) {
return true;
}
通过请求头判断能不能读
for (MediaType supportedMediaType : getSupportedMediaTypes()) {
if (supportedMediaType.includes(mediaType)) {
return true;
}
}
return false;
}
@Override
public boolean canWrite(Class<?> clazz, @Nullable MediaType mediaType) {
return supports(clazz) && canWrite(mediaType);
}
protected boolean canWrite(@Nullable MediaType mediaType) {
if (mediaType == null || MediaType.ALL.equalsTypeAndSubtype(mediaType)) {
return true;
}
通过请求头判断能不能写
for (MediaType supportedMediaType : getSupportedMediaTypes()) {
if (supportedMediaType.isCompatibleWith(mediaType)) {
return true;
}
}
return false;
}
暴露给子类去实现的类型判断
protected abstract boolean supports(Class<?> clazz);
HandlerMethodArgumentResolver
这个类是用于解析请求的入参,主要提供了2方法,一个是判断是否支持解析,其次就是如何解析。
public interface HandlerMethodArgumentResolver {
/**
* Whether the given {@linkplain MethodParameter method parameter} is
* supported by this resolver.
* @param parameter the method parameter to check
* @return {@code true} if this resolver supports the supplied parameter;
* {@code false} otherwise
*/
boolean supportsParameter(MethodParameter parameter);
/**
* Resolves a method parameter into an argument value from a given request.
* A {@link ModelAndViewContainer} provides access to the model for the
* request. A {@link WebDataBinderFactory} provides a way to create
* a {@link WebDataBinder} instance when needed for data binding and
* type conversion purposes.
* @param parameter the method parameter to resolve. This parameter must
* have previously been passed to {@link #supportsParameter} which must
* have returned {@code true}.
* @param mavContainer the ModelAndViewContainer for the current request
* @param webRequest the current request
* @param binderFactory a factory for creating {@link WebDataBinder} instances
* @return the resolved argument value, or {@code null} if not resolvable
* @throws Exception in case of errors with the preparation of argument values
*/
@Nullable
Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception;
}
看源码接口结构比较简单,可以看看其对应的一些实现类。
RequestResponseBodyMethodProcessor就是其对应的一个实现类。专门用于处理@RequestBody以及@ResponseBody。这个可以后面再说
AbstractMessageConverterMethodArgumentResolver
这个类的作用就是定义了如何利用消息转化器来解析参数
public abstract class AbstractMessageConverterMethodArgumentResolver implements HandlerMethodArgumentResolver {
protected final List<HttpMessageConverter<?>> messageConverters;
protected <T> Object readWithMessageConverters(HttpInputMessage inputMessage, MethodParameter parameter,
Type targetType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException {
内容暂时省略
}
}
HandlerMethodArgumentResolverComposite
复合参数解析器,就是一个组合的参数解析器
public class HandlerMethodArgumentResolverComposite implements HandlerMethodArgumentResolver {
private final List<HandlerMethodArgumentResolver> argumentResolvers = new LinkedList<>();
private final Map<MethodParameter, HandlerMethodArgumentResolver> argumentResolverCache =
new ConcurrentHashMap<>(256);
}
HandlerMethodReturnValueHandler
这个类则是专门用于处理返回结果。RequestResponseBodyMethodProcessor的对于@ResponseBody处理的实现,也是基于此接口。
public interface HandlerMethodReturnValueHandler {
boolean supportsReturnType(MethodParameter returnType);
void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception;
}
AbstractMessageConverterMethodProcessor
这个类则是一个抽象类,具备了参数解析的功能(通过继承AbstractMessageConverterMethodArgumentResolver),以及处理返回结果的功能,至于如何处理返回结果则是通过定义writeWithMessageConverters方法。
public abstract class AbstractMessageConverterMethodProcessor extends AbstractMessageConverterMethodArgumentResolver
implements HandlerMethodReturnValueHandler {
protected <T> void writeWithMessageConverters(T value, MethodParameter returnType, NativeWebRequest webRequest)
throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
ServletServerHttpRequest inputMessage = createInputMessage(webRequest);
ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);
writeWithMessageConverters(value, returnType, inputMessage, outputMessage);
}
@SuppressWarnings({"rawtypes", "unchecked"})
protected <T> void writeWithMessageConverters(@Nullable T value, MethodParameter returnType,
ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage)
throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
。。。代码暂时忽略,实现内容有点多。主要就是通过消息转化器来处理返回结果。
}
}
RequestResponseBodyMethodProcessor
这个类则是专门用于处理@RequestBody以及@ResponseBody。从结构看一目了然。它继承的抽象类,本身定义好了对于入参的解析,以及响应返回值的处理。
public class RequestResponseBodyMethodProcessor extends AbstractMessageConverterMethodProcessor {
@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.hasParameterAnnotation(RequestBody.class);
}
@Override
public boolean supportsReturnType(MethodParameter returnType) {
return (AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ResponseBody.class) ||
returnType.hasMethodAnnotation(ResponseBody.class));
}
}
public abstract class AbstractMessageConverterMethodProcessor extends AbstractMessageConverterMethodArgumentResolver
implements HandlerMethodReturnValueHandler {
}
HandlerMethod
这个类看上去内容挺多的,但是其实是对于一个bean如何处理的封装。
bean以及对应的处理方法。
public class HandlerMethod {
protected final Log logger = LogFactory.getLog(getClass());
private final Object bean;
@Nullable
private final BeanFactory beanFactory;
private final Class<?> beanType;
private final Method method;
private final Method bridgedMethod;
private final MethodParameter[] parameters;
@Nullable
private HttpStatus responseStatus;
@Nullable
private String responseStatusReason;
@Nullable
private HandlerMethod resolvedFromHandlerMethod;
@Nullable
private volatile List<Annotation[][]> interfaceParameterAnnotations;
private final String description;
/**
* Create an instance from a bean instance and a method.
*/
public HandlerMethod(Object bean, Method method) {
Assert.notNull(bean, "Bean is required");
Assert.notNull(method, "Method is required");
this.bean = bean;
this.beanFactory = null;
this.beanType = ClassUtils.getUserClass(bean);
this.method = method;
this.bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
this.parameters = initMethodParameters();
evaluateResponseStatus();
this.description = initDescription(this.beanType, this.method);
}
}
InvocableHandlerMethod
这个类是HandlerMethod的子类,主要是提供执行方法的功能。而其父类则是关注对象与方法的封装。
public class InvocableHandlerMethod extends HandlerMethod {
复合的参数解析器
private HandlerMethodArgumentResolverComposite resolvers = new HandlerMethodArgumentResolverComposite();
@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);
}
解析请求中的参数
protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
MethodParameter[] parameters = getMethodParameters();
if (ObjectUtils.isEmpty(parameters)) {
return EMPTY_ARGS;
}
Object[] args = new Object[parameters.length];
for (int i = 0; i < parameters.length; i++) {
。。。省略部分代码
判断参数解析器中是否有可以解析该参数的
if (!this.resolvers.supportsParameter(parameter)) {
throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
}
try {
对参数进行解析
args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
}
catch (Exception ex) {
// Leave stack trace for later, exception may actually be resolved and handled...
if (logger.isDebugEnabled()) {
String exMsg = ex.getMessage();
if (exMsg != null && !exMsg.contains(parameter.getExecutable().toGenericString())) {
logger.debug(formatArgumentError(parameter, exMsg));
}
}
throw ex;
}
}
return args;
}
}
ServletInvocableHandlerMethod
这个类其实挺复杂的,但是功能上主要是涵盖了解析请求入参,以及生成响应结果。
public class ServletInvocableHandlerMethod extends InvocableHandlerMethod{
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
//父类中的实现,InvocableHandlerMethod中。
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
setResponseStatus(webRequest);
if (returnValue == null) {
if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
disableContentCachingIfNecessary(webRequest);
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;
}
}}
源码的执行过程
首先一个请求被处理的入口还是在DispatcherServlet,代码如下
public class DispatcherServlet extends FrameworkServlet {
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
找到请求匹配的处理器链(拦截器 过滤器 controller对应的执行方法)
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
没有找到对应的处理器直接返回
noHandlerFound(processedRequest, response);
return;
}
将Controller中的执行方法转化处理器适配器
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
String method = request.getMethod();
...
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
通过适配器进行处理,返回视图,这里就包括了参数的解析,以及响应的生成,所以接下来看里面的实现即可
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
....
}
catch (Exception ex) {
dispatchException = ex;
}
catch (Throwable err) {
dispatchException = new NestedServletException("Handler dispatch failed", err);
}
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Throwable err) {
....
}
finally {
...省略代码
}
}
}
那么HandlerAdapter如何去进行请求入参的解析,以及响应的生成?
public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter
implements BeanFactoryAware, InitializingBean {
@Nullable
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ServletWebRequest webRequest = new ServletWebRequest(request, response);
try {
...省略
ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
...省略若干代码
这里看到,其实是HandlerMethod的子类,ServletInvocableHandlerMethod 去执行的。
前面说到这个类,是用于解析请求入参,以及生成响应结果。那么接下来看看如何处理。
invocableMethod.invokeAndHandle(webRequest, mavContainer);
if (asyncManager.isConcurrentHandlingStarted()) {
return null;
}
return getModelAndView(mavContainer, modelFactory, webRequest);
}
finally {
webRequest.requestCompleted();
}
}
}
由于ServletInvocableHandlerMethod中解析参数以及生成响应结果的操作是在父类中去实现的。
所以接下来直接看父类InvocableHandlerMethod如何实现的。
public class InvocableHandlerMethod extends HandlerMethod {
复合解析器
private HandlerMethodArgumentResolverComposite resolvers = new HandlerMethodArgumentResolverComposite();
protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
MethodParameter[] parameters = getMethodParameters();
if (ObjectUtils.isEmpty(parameters)) {
return EMPTY_ARGS;
}
Object[] args = new Object[parameters.length];
for (int i = 0; i < parameters.length; i++) {
...省略代码
通过复合解析器判断是否有支持的参数解析器,若无则报错
if (!this.resolvers.supportsParameter(parameter)) {
throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
}
try {
通过复合解析器去进行参数的解析
args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
}
catch (Exception ex) {
// Leave stack trace for later, exception may actually be resolved and handled...
if (logger.isDebugEnabled()) {
String exMsg = ex.getMessage();
if (exMsg != null && !exMsg.contains(parameter.getExecutable().toGenericString())) {
logger.debug(formatArgumentError(parameter, exMsg));
}
}
throw ex;
}
}
return args;
}
}
复合解析器如何去判断是否支持参数的解析
public class HandlerMethodArgumentResolverComposite implements HandlerMethodArgumentResolver {
@Override
public boolean supportsParameter(MethodParameter parameter) {
return getArgumentResolver(parameter) != null;
}
@Nullable
private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {
HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);
缓存不存在则进行遍历
if (result == null) {
for (HandlerMethodArgumentResolver resolver : this.argumentResolvers) {
参数解析器链中,那么@RqesutBody的参数以及@ResponseBody的响应如何判断是否支持?
if (resolver.supportsParameter(parameter)) {
result = resolver;
this.argumentResolverCache.put(parameter, result);
break;
}
}
}
return result;
}
}
那么@RqesutBody的参数以及@ResponseBody的响应如何判断是否支持?
其实就是判断是否有对应的注解修饰。
public class RequestResponseBodyMethodProcessor extends AbstractMessageConverterMethodProcessor {
@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.hasParameterAnnotation(RequestBody.class);
}
@Override
public boolean supportsReturnType(MethodParameter returnType) {
return (AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ResponseBody.class) ||
returnType.hasMethodAnnotation(ResponseBody.class));
}
}
从上面的代码看到被@RequestBody以及@ResponseBody注解修饰的参数,都会被RequestResponseBodyMethodProcessor 处理。
那么RequestResponseBodyMethodProcessor如何解析@RequsetBody修饰的参数?
public class RequestResponseBodyMethodProcessor extends AbstractMessageConverterMethodProcessor {
@Override
protected <T> Object readWithMessageConverters(NativeWebRequest webRequest, MethodParameter parameter,
Type paramType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException {
HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
Assert.state(servletRequest != null, "No HttpServletRequest");
ServletServerHttpRequest inputMessage = new ServletServerHttpRequest(servletRequest);
通过消息转化器对参数进行解析,在其父类中已经进行了实现。
Object arg = readWithMessageConverters(inputMessage, parameter, paramType);
if (arg == null && checkRequired(parameter)) {
throw new HttpMessageNotReadableException("Required request body is missing: " +
parameter.getExecutable().toGenericString(), inputMessage);
}
return arg;
}
}
public abstract class AbstractMessageConverterMethodArgumentResolver implements HandlerMethodArgumentResolver {
@SuppressWarnings("unchecked")
@Nullable
protected <T> Object readWithMessageConverters(HttpInputMessage inputMessage, MethodParameter parameter,
Type targetType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException {
省略部分代码....
Object body = NO_VALUE;
EmptyBodyCheckingHttpInputMessage message;
try {
message = new EmptyBodyCheckingHttpInputMessage(inputMessage);
最终发现是通过消息转换器去进行转化
for (HttpMessageConverter<?> converter : this.messageConverters) {
Class<HttpMessageConverter<?>> converterType = (Class<HttpMessageConverter<?>>) converter.getClass();
GenericHttpMessageConverter<?> genericConverter =
(converter instanceof GenericHttpMessageConverter ? (GenericHttpMessageConverter<?>) converter : null);
判断消息转换器是否能读取。一个是通过参数的类型,另外一个通过请求头ContentType。
if (genericConverter != null ? genericConverter.canRead(targetType, contextClass, contentType) :
(targetClass != null && converter.canRead(targetClass, contentType))) {
if (message.hasBody()) {
HttpInputMessage msgToUse =
getAdvice().beforeBodyRead(message, parameter, targetType, converterType);
使用消息转化器的read方法,进行参数的读取
body = (genericConverter != null ? genericConverter.read(targetType, contextClass, msgToUse) :
((HttpMessageConverter<T>) converter).read(targetClass, msgToUse));
body = getAdvice().afterBodyRead(body, msgToUse, parameter, targetType, converterType);
}
else {
body = getAdvice().handleEmptyBody(null, message, parameter, targetType, converterType);
}
break;
}
}
}
catch (IOException ex) {
throw new HttpMessageNotReadableException("I/O error while reading input message", ex, inputMessage);
}
if (body == NO_VALUE) {
if (httpMethod == null || !SUPPORTED_METHODS.contains(httpMethod) ||
(noContentType && !message.hasBody())) {
return null;
}
throw new HttpMediaTypeNotSupportedException(contentType, this.allSupportedMediaTypes);
}
...省略代码
return body;
}
}
那么由于我测试使用的协议是protobuf,那么Protobuf的消息转化器如何起作用?
public class ProtobufHttpMessageConverter extends AbstractHttpMessageConverter<Message> {
从代码中看出,能不能读除了类型判断,还有contenttype的判断。
需要类型为Message&mediaType为application/x-protobuf
@Override
public boolean canRead(Class<?> clazz, @Nullable MediaType mediaType) {
return supports(clazz) && canRead(mediaType);
}
@Override
protected boolean supports(Class<?> clazz) {
为Message的子类
return Message.class.isAssignableFrom(clazz);
}
@Override
protected Message readInternal(Class<? extends Message> clazz, HttpInputMessage inputMessage)
throws IOException, HttpMessageNotReadableException {
MediaType contentType = inputMessage.getHeaders().getContentType();
if (contentType == null) {
contentType = PROTOBUF;
}
Charset charset = contentType.getCharset();
if (charset == null) {
charset = DEFAULT_CHARSET;
}
Message.Builder builder = getMessageBuilder(clazz);
if (PROTOBUF.isCompatibleWith(contentType)) {
builder.mergeFrom(inputMessage.getBody(), this.extensionRegistry);
}
else if (TEXT_PLAIN.isCompatibleWith(contentType)) {
InputStreamReader reader = new InputStreamReader(inputMessage.getBody(), charset);
TextFormat.merge(reader, this.extensionRegistry, builder);
}
else if (this.protobufFormatSupport != null) {
this.protobufFormatSupport.merge(
inputMessage.getBody(), charset, contentType, this.extensionRegistry, builder);
}
返回参数
return builder.build();
}
}
public abstract class AbstractHttpMessageConverter<T> implements HttpMessageConverter<T> {
protected boolean canRead(@Nullable MediaType mediaType) {
if (mediaType == null) {
return true;
}
for (MediaType supportedMediaType : getSupportedMediaTypes()) {
if (supportedMediaType.includes(mediaType)) {
return true;
}
}
return false;
}
}
前面的内容基本就大概知道@ReqeustBody是如何起作用的了。那么对于@ResponseBody的处理是如何的?
@ResponseBody的处理
先回到InvocableHandlerMethod,源码如下
public class InvocableHandlerMethod extends HandlerMethod {
private HandlerMethodArgumentResolverComposite resolvers = new HandlerMethodArgumentResolverComposite();
@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));
}
让controller的方法invoke执行,最后返回结果
return doInvoke(args);
}
}
public class ServletInvocableHandlerMethod extends InvocableHandlerMethod {
复合处理器
@Nullable
private HandlerMethodReturnValueHandlerComposite returnValueHandlers;
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
解析入参,并且Invoke controller的处理方法,获得返回值
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
省略代码。。。
try {
最后对结果进行处理。
this.returnValueHandlers.handleReturnValue(
returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
}
catch (Exception ex) {
if (logger.isTraceEnabled()) {
logger.trace(formatErrorForReturnValue(returnValue), ex);
}
throw ex;
}
}
}
从上面的代码可以看到,其实是通过returnValueHandlers,这个复合处理器来处理返回结果的。
那么这个复合处理器自然包括我们前面提到的RequestResponseBodyMethodProcessor。
那么这个复合处理器如何工作的?
public class HandlerMethodReturnValueHandlerComposite implements HandlerMethodReturnValueHandler {
维护了一个处理器链,处理器链包含RequestResponseBodyMethodProcessor
private final List<HandlerMethodReturnValueHandler> returnValueHandlers = new ArrayList<>();
@Override
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
选择合适的处理器
HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
if (handler == null) {
throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName());
}
处理器对返回值进行处理
handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
}
@Nullable
private HandlerMethodReturnValueHandler selectHandler(@Nullable Object value, MethodParameter returnType) {
boolean isAsyncValue = isAsyncReturnValue(value, returnType);
for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) {
if (isAsyncValue && !(handler instanceof AsyncHandlerMethodReturnValueHandler)) {
continue;
}
if (handler.supportsReturnType(returnType)) {
return handler;
}
}
return null;
}
}
public class RequestResponseBodyMethodProcessor extends AbstractMessageConverterMethodProcessor {
@Override
public boolean supportsReturnType(MethodParameter returnType) {
return (AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ResponseBody.class) ||
returnType.hasMethodAnnotation(ResponseBody.class));
}
}
RequestResponseBodyMethodProcessor如何处理返回值
public class RequestResponseBodyMethodProcessor extends AbstractMessageConverterMethodProcessor {
@Override
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
mavContainer.setRequestHandled(true);
ServletServerHttpRequest inputMessage = createInputMessage(webRequest);
ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);
通过消息转化器来转化响应结果
writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
}
}
public abstract class AbstractMessageConverterMethodProcessor extends AbstractMessageConverterMethodArgumentResolver
implements HandlerMethodReturnValueHandler {
@SuppressWarnings({"rawtypes", "unchecked"})
protected <T> void writeWithMessageConverters(@Nullable T value, MethodParameter returnType,
ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage)
throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
Object body;
Class<?> valueType;
Type targetType;
if (value instanceof CharSequence) {
body = value.toString();
valueType = String.class;
targetType = String.class;
}
else {
body = value;
返回值的类型
valueType = getReturnValueType(body, returnType);
targetType = GenericTypeResolver.resolveType(getGenericType(returnType), returnType.getContainingClass());
}
if (selectedMediaType != null) {
selectedMediaType = selectedMediaType.removeQualityValue();
for (HttpMessageConverter<?> converter : this.messageConverters) {
通过消息转化器对返回值进行处理
GenericHttpMessageConverter genericConverter = (converter instanceof GenericHttpMessageConverter ?
(GenericHttpMessageConverter<?>) converter : null);
先判断是否消息转化器是否匹配,由于我在debug的时候,返回的是Message类型,即Protobuf指定的类型。所以序列化当然也是以Protobuf的消息转化器去做。
if (genericConverter != null ?
((GenericHttpMessageConverter) converter).canWrite(targetType, valueType, selectedMediaType) :
converter.canWrite(valueType, selectedMediaType)) {
body = getAdvice().beforeBodyWrite(body, returnType, selectedMediaType,
(Class<? extends HttpMessageConverter<?>>) converter.getClass(),
inputMessage, outputMessage);
if (body != null) {
Object theBody = body;
addContentDispositionHeader(inputMessage, outputMessage);
if (genericConverter != null) {
genericConverter.write(body, targetType, selectedMediaType, outputMessage);
}
else {
protobuf消息转换器的写方法,进行序列化
((HttpMessageConverter) converter).write(body, selectedMediaType, outputMessage);
}
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Nothing to write: null body");
}
}
return;
}
}
}
}
}
最后Protobuf消息处理器写入数据
public class ProtobufHttpMessageConverter extends AbstractHttpMessageConverter<Message> {
@SuppressWarnings("deprecation")
@Override
protected void writeInternal(Message message, HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotWritableException {
MediaType contentType = outputMessage.getHeaders().getContentType();
if (contentType == null) {
contentType = getDefaultContentType(message);
Assert.state(contentType != null, "No content type");
}
Charset charset = contentType.getCharset();
if (charset == null) {
charset = DEFAULT_CHARSET;
}
if (PROTOBUF.isCompatibleWith(contentType)) {
setProtoHeader(outputMessage, message);
CodedOutputStream codedOutputStream = CodedOutputStream.newInstance(outputMessage.getBody());
序列化的同时,写入到对应的流中
message.writeTo(codedOutputStream);
codedOutputStream.flush();
}
else if (TEXT_PLAIN.isCompatibleWith(contentType)) {
OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputMessage.getBody(), charset);
TextFormat.print(message, outputStreamWriter); // deprecated on Protobuf 3.9
outputStreamWriter.flush();
outputMessage.getBody().flush();
}
else if (this.protobufFormatSupport != null) {
this.protobufFormatSupport.print(message, outputMessage.getBody(), contentType, charset);
outputMessage.getBody().flush();
}
}
}
总结:
请求的处理大致可以分为3步。
1.对于请求入参的解析。无非是通过复合参数解析器中的参数解析器链表,根据入参的类型来判断。比如RequestResponseBodyMethodProcessor的实现,针对@RequestBody的处理,便是通过入参是否被@ReqeustBody修饰,判断能否被处理。其次便是参数解析器中的消息转化器来根据入参的类型以及请求头Contenttype决定是否能被对应的消息转化器处理。如利用Protobuf转化协议,那么入参类型必为Message的子类且contentType=x-protobuf。最后被protobuf的消息转化器进行反序列化。
2.controller中的方法进行invoke生成返回值
3.将controller方法中返回的结果进行序列化。那么这里也是通过复合返回值处理器进行处理。比如RequestResponseBodyMethodProcessor针对@ResponseBody的处理。首先是判断返回类型是否被@ResponseBody注解修饰,其次再通过其内部的消息转化器进行处理。那么消息转化器的处理,也是判断返回类型,比如ProtobufHttpMessageConverter也是判断返回的对象类型是否为Message的子类且contentType=x-protobuf,最后才会进行反序列化,写入响应对应的输出流中。
网友评论