目录
一、简单使用
二、存在的问题描述
三、优点
一、简单使用
统一的数据返回格式使用 @ControllerAdvice 和 ResponseBodyAdvice 的方式实现 @ControllerAdvice 表示控制器通知类。
添加类 ResponseAdvice , 实现 ResponseBodyAdvice 接口,并在类上添加 @ControllerAdvice 注解。
import com.example.demo.model.Result;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.ControllerAdvice;
import
org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
@ControllerAdvice
public class ResponseAdvice implements ResponseBodyAdvice {
@Override
public boolean supports(MethodParameter returnType, Class converterType) {
return true;
}
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType,
MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest
request, ServerHttpResponse response) {
return Result.success(body);
}
}
supports方法: 判断是否要执行beforeBodyWrite方法。true为执行,false不执行。 通过该方法可以选择哪些类或哪些方法的response要进行处理, 其他的不进行处理。
从returnType获取类名和方法名:
//获取执行的类
Class<?> declaringClass = returnType.getMethod().getDeclaringClass();
//获取执行的方法
Method method = returnType.getMethod();
beforeBodyWrite方法: 对response方法进行具体操作处理。
二、存在的问题描述
SpringMVC默认会注册一些自带的
HttpMessageConverter (从先后顺序排列分别为):
- ByteArrayHttpMessageConverter
- StringHttpMessageConverter
- SourceHttpMessageConverter
- SourceHttpMessageConverter
- AllEncompassingFormHttpMessageConverter
public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter
implements BeanFactoryAware, InitializingBean {
//...
public RequestMappingHandlerAdapter() {
this.messageConverters = new ArrayList<>(4);
this.messageConverters.add(new ByteArrayHttpMessageConverter());
this.messageConverters.add(new StringHttpMessageConverter());
if (!shouldIgnoreXml) {
try {
this.messageConverters.add(new SourceHttpMessageConverter<>());
}
catch (Error err) {
// Ignore when no TransformerFactory implementation is available
}
}
this.messageConverters.add(new AllEncompassingFormHttpMessageConverter());
}
//...
}
其中AllEncompassingFormHttpMessageConverter 会根据项目依赖情况添加对应的HttpMessageConverter:
public AllEncompassingFormHttpMessageConverter() {
if (!shouldIgnoreXml) {
try {
addPartConverter(new SourceHttpMessageConverter<>());
}
catch (Error err) {
// Ignore when no TransformerFactory implementation is available
}
if (jaxb2Present && !jackson2XmlPresent) {
addPartConverter(new Jaxb2RootElementHttpMessageConverter());
}
}
if (kotlinSerializationJsonPresent) {
addPartConverter(new KotlinSerializationJsonHttpMessageConverter());
}
if (jackson2Present) {
addPartConverter(new MappingJackson2HttpMessageConverter());
}
else if (gsonPresent) {
addPartConverter(new GsonHttpMessageConverter());
}
else if (jsonbPresent) {
addPartConverter(new JsonbHttpMessageConverter());
}
if (jackson2XmlPresent && !shouldIgnoreXml) {
addPartConverter(new MappingJackson2XmlHttpMessageConverter());
}
if (jackson2SmilePresent) {
addPartConverter(new MappingJackson2SmileHttpMessageConverter());
}
}
在依赖中引入jackson包后,容器会把
MappingJackson2HttpMessageConverter 自
动注册到messageConverters 链的末尾。
Spring会根据返回的数据类型, 从
messageConverters
链选择合适的 HttpMessageConverter :
- 当返回的数据是非字符串时, 使用的 MappingJackson2HttpMessageConverter 写入返回对象。
- 当返回的数据是字符串时,StringHttpMessageConverter 会先被遍历到,这时会认为 StringHttpMessageConverter 可以使用。
public abstract class AbstractMessageConverterMethodProcessor extends
AbstractMessageConverterMethodArgumentResolver
implements HandlerMethodReturnValueHandler {
//...代码省略
protected <T> void writeWithMessageConverters(@Nullable T value,
MethodParameter returnType,
ServletServerHttpRequest inputMessage, ServletServerHttpResponse
outputMessage)
throws IOException, HttpMediaTypeNotAcceptableException,
HttpMessageNotWritableException {
//...代码省略
if (selectedMediaType != null) {
selectedMediaType = selectedMediaType.removeQualityValue();
for (HttpMessageConverter<?> converter : this.messageConverters) {
GenericHttpMessageConverter genericConverter = (converter
instanceof GenericHttpMessageConverter ?
(GenericHttpMessageConverter<?>) converter : null);
if (genericConverter != null ?
((GenericHttpMessageConverter)
converter).canWrite(targetType, valueType, selectedMediaType) :
converter.canWrite(valueType, selectedMediaType)) {
//getAdvice().beforeBodyWrite 执⾏之后, body转换成了Result类型的结果
body = getAdvice().beforeBodyWrite(body, returnType,
selectedMediaType,
(Class<? extends HttpMessageConverter<?>>)
converter.getClass(),
inputMessage, outputMessage);
if (body != null) {
Object theBody = body;
LogFormatUtils.traceDebug(logger, traceOn ->"Writing [" + LogFormatUtils.formatValue(theBody, !traceOn) + "]");
addContentDispositionHeader(inputMessage, outputMessage);
if (genericConverter != null) {
genericConverter.write(body, targetType,
selectedMediaType, outputMessage);
}
else {
//此时cover为StringHttpMessageConverter
((HttpMessageConverter) converter).write(body,
selectedMediaType, outputMessage);
}
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Nothing to write: null body");
}
}
return;
}
}
}
//...代码省略
}
//...代码省略
}
在
((HttpMessageConverter) converter).write(body, selectedMediaType, outputMessage) 的处理中, 调用父类的write方法。由于 StringHttpMessageConverter
重写了addDefaultHeaders方法,所以会执行子类的方法。
然而子类
StringHttpMessageConverter
的addDefaultHeaders方法定义接收参数为String,此时 t 为 Result 类型,,所以出现类型不匹配"Result cannot be cast to java.lang.String"的异常。
三、优点
- 方便前端程序员更好的接收和解析后端数据接口返回的数据;
- 降低前端程序员和后端程序员的沟通成本, 按照某个格式实现就可以了, 因为所有接口都是这样返回的;
- 有利于项目统一数据的维护和修改;
- 有利于后端技术部门的统一规范的标准制定, 不会出现稀奇古怪的返回内容。