十年网站开发经验 + 多家企业客户 + 靠谱的建站团队
量身定制 + 运营维护+专业推广+无忧售后,网站问题一站解决
SpringMVC中如何利用@ControllerAdvice和ResponseBodyAdvice接口统一处理返回值,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。
创新互联公司拥有一支富有激情的企业网站制作团队,在互联网网站建设行业深耕10余年,专业且经验丰富。10余年网站优化营销经验,我们已为成百上千家中小企业提供了成都网站设计、成都做网站、外贸网站建设解决方案,按需求定制网站,设计满意,售后服务无忧。所有客户皆提供一年免费网站维护!
在我们进行Java的Web应用开发时,如何写更少的代码,做更多的事情。如何让开发更容易上手,更专注于业务层面,不需要太关心底层的实现。这里就分享一些我平时在搭建基础框架时候的一些心得体验。
在web应用中,通常前后端会定义一个统一的对象来封装返回值,一般除了业务数据之外,可能会包含一些请求相关的数据
例如以下这个对象
code
来标识整个请求的结果
msg
用于返回错误信息
data
用于返回实际的业务数据。
{ "code": 0, "msg": "success", "data": {} }
统一封装的好处就是前端可以使用统一的逻辑进行请求处理,能够编写通用代码来处理返回值。
当然这也需要后端做一定的开发。通常我们都是直接写在代码里面,手动去创建一个封装对象,然后将数据set进去,或者是封装类添加一些静态方法之类的。 在大部分情况下,这些工作都是重复的。
今天介绍的这个接口, ResponseBodyAdvice
, 这是由SpringMvc提供的一个接口,在消息转换前处理返回值,源码如下:
public interface ResponseBodyAdvice{ boolean supports(MethodParameter returnType, Class extends HttpMessageConverter>> converterType); T beforeBodyWrite(T body, MethodParameter returnType, MediaType selectedContentType, Class extends HttpMessageConverter>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response); }
这个接口在返回值被消息转换器写回前端之前进行处理, 大致处理流程如下:
我们实现这个接口的代码主要在这个方法里被调用 RequestResponseBodyAdviceChain.processBody
, 可以看到这一段逻辑很简单
先执行ResponseBodyAdvice.supports
看当前切面类是否支持,如果支持再调用ResponseBodyAdvice.beforeBodyWrite
方法并返回
返回值会被 HttpMessageConverter.write
接口在进行最终的转换(例如转JSON),然后写回前端
privateObject processBody(@Nullable Object body, MethodParameter returnType, MediaType contentType, Class extends HttpMessageConverter>> converterType, ServerHttpRequest request, ServerHttpResponse response) { for (ResponseBodyAdvice> advice : getMatchingAdvice(returnType, ResponseBodyAdvice.class)) { if (advice.supports(returnType, converterType)) { body = ((ResponseBodyAdvice ) advice).beforeBodyWrite((T) body, returnType, contentType, converterType, request, response); } } return body; }
SpringMVC在初始化的时候, 会调用RequestMappingHandlerAdapter.initControllerAdviceCache
,将ResponseBodyAdvice
初始化到容器中
里面会调用ControllerAdviceBean.findAnnotatedBeans
,获取所有带有 @ControllerAdvice
注解的类
将所有实现了 ResponseBodyAdvice
接口的Bean放入requestResponseBodyAdviceBeans
中, 在之前介绍到的 getAdvice()
方法取得就是该对象。
//代码片段 public static ListfindAnnotatedBeans(ApplicationContext context) { return Arrays.stream(BeanFactoryUtils.beanNamesForTypeIncludingAncestors(context, Object.class)) .filter(name -> context.findAnnotationOnBean(name, ControllerAdvice.class) != null) .map(name -> new ControllerAdviceBean(name, context)) .collect(Collectors.toList()); } // 代码片段 for (ControllerAdviceBean adviceBean : adviceBeans) { Class> beanType = adviceBean.getBeanType(); if (beanType == null) { throw new IllegalStateException("Unresolvable type for ControllerAdviceBean: " + adviceBean); } Set attrMethods = MethodIntrospector.selectMethods(beanType, MODEL_ATTRIBUTE_METHODS); if (!attrMethods.isEmpty()) { this.modelAttributeAdviceCache.put(adviceBean, attrMethods); } Set binderMethods = MethodIntrospector.selectMethods(beanType, INIT_BINDER_METHODS); if (!binderMethods.isEmpty()) { this.initBinderAdviceCache.put(adviceBean, binderMethods); } if (RequestBodyAdvice.class.isAssignableFrom(beanType)) { requestResponseBodyAdviceBeans.add(adviceBean); } if (ResponseBodyAdvice.class.isAssignableFrom(beanType)) { requestResponseBodyAdviceBeans.add(adviceBean); } }
了解到这些,我们实现一个通用的返回值处理就很简单了, 只需要实现 ResponseBodyAdvice
接口,并且加上 @ControllerAdvice
注解就可以了
这是我实现的一个,统一封装返回值的实现, 大家可以参考一下,根据自己的业务需求来进行修改
package com.diamondfsd.fast.mvc.advice; import com.diamondfsd.fast.mvc.annotations.IgnoreAware; import com.diamondfsd.fast.mvc.entity.FastResult; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.core.MethodParameter; import org.springframework.http.MediaType; import org.springframework.http.converter.HttpMessageConverter; 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; import java.lang.reflect.Method; import java.util.Map; import java.util.WeakHashMap; /** * 统一返回数据封装 * @author Diamond */ @ControllerAdvice public class FastMvcResponseBodyAwareAdvice implements ResponseBodyAdvice
看完上述内容,你们掌握SpringMVC中如何利用@ControllerAdvice和ResponseBodyAdvice接口统一处理返回值的方法了吗?如果还想学到更多技能或想了解更多相关内容,欢迎关注创新互联行业资讯频道,感谢各位的阅读!