千家信息网

SpringMVC中如何利用@ControllerAdvice和ResponseBodyAdvice接口统一处理返回值

发表于:2024-10-16 作者:千家信息网编辑
千家信息网最后更新 2024年10月16日,SpringMVC中如何利用@ControllerAdvice和ResponseBodyAdvice接口统一处理返回值,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这
千家信息网最后更新 2024年10月16日SpringMVC中如何利用@ControllerAdvice和ResponseBodyAdvice接口统一处理返回值

SpringMVC中如何利用@ControllerAdvice和ResponseBodyAdvice接口统一处理返回值,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。

在我们进行Java的Web应用开发时,如何写更少的代码,做更多的事情。如何让开发更容易上手,更专注于业务层面,不需要太关心底层的实现。这里就分享一些我平时在搭建基础框架时候的一些心得体验。

统一处理返回值

在web应用中,通常前后端会定义一个统一的对象来封装返回值,一般除了业务数据之外,可能会包含一些请求相关的数据

例如以下这个对象

  • code来标识整个请求的结果

  • msg用于返回错误信息

  • data用于返回实际的业务数据。

{        "code": 0,        "msg": "success",        "data": {}}

统一封装的好处就是前端可以使用统一的逻辑进行请求处理,能够编写通用代码来处理返回值。

当然这也需要后端做一定的开发。通常我们都是直接写在代码里面,手动去创建一个封装对象,然后将数据set进去,或者是封装类添加一些静态方法之类的。 在大部分情况下,这些工作都是重复的。

ResponseBodyAdvice 的执行流程

今天介绍的这个接口, ResponseBodyAdvice, 这是由SpringMvc提供的一个接口,在消息转换前处理返回值,源码如下:

public interface ResponseBodyAdvice{        boolean supports(MethodParameter returnType, Class> converterType);        T beforeBodyWrite(T body, MethodParameter returnType, MediaType selectedContentType,                        Class> selectedConverterType,                        ServerHttpRequest request, ServerHttpResponse response);}

这个接口在返回值被消息转换器写回前端之前进行处理, 大致处理流程如下:

我们实现这个接口的代码主要在这个方法里被调用 RequestResponseBodyAdviceChain.processBody, 可以看到这一段逻辑很简单

先执行ResponseBodyAdvice.supports看当前切面类是否支持,如果支持再调用ResponseBodyAdvice.beforeBodyWrite方法并返回

返回值会被 HttpMessageConverter.write 接口在进行最终的转换(例如转JSON),然后写回前端

private  Object processBody(@Nullable Object body, MethodParameter returnType, MediaType contentType,                Class> 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;}

ResponseBodyAdvice 的初始化

SpringMVC在初始化的时候, 会调用RequestMappingHandlerAdapter.initControllerAdviceCache,将ResponseBodyAdvice初始化到容器中

里面会调用ControllerAdviceBean.findAnnotatedBeans ,获取所有带有 @ControllerAdvice 注解的类

将所有实现了 ResponseBodyAdvice 接口的Bean放入requestResponseBodyAdviceBeans中, 在之前介绍到的 getAdvice() 方法取得就是该对象。

//代码片段public static List findAnnotatedBeans(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 */@ControllerAdvicepublic class FastMvcResponseBodyAwareAdvice implements ResponseBodyAdvice {    private final Map supportsCache = new WeakHashMap<>();    private final String [] basePackages;    private ObjectMapper objectMapper = new ObjectMapper();    public FastMvcResponseBodyAwareAdvice(String [] basePackages) {        this.basePackages = basePackages;    }    @Override    public boolean supports(MethodParameter returnType, Class> converterType) {        if (supportsCache.containsKey(returnType.getMethod())) {            return supportsCache.get(returnType.getMethod());        }        boolean isSupport = getIsSupport(returnType);        supportsCache.put(returnType.getMethod(), isSupport);        return isSupport;    }    private boolean getIsSupport(MethodParameter returnType) {        Class declaringClass = returnType.getMember().getDeclaringClass();        IgnoreAware classIgnore = declaringClass.getAnnotation(IgnoreAware.class);        IgnoreAware methodIgnore = returnType.getMethod().getAnnotation(IgnoreAware.class);        if (classIgnore != null || methodIgnore != null || FastResult.class.equals(returnType.getGenericParameterType())) {            return false;        }        for (int i = 0; i < basePackages.length; i++) {            if (declaringClass.getPackage().getName().startsWith(basePackages[i])) {                return true;            }        }        return false;    }    @Override    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,                                  Class> selectedConverterType, ServerHttpRequest request,                                  ServerHttpResponse response) {        FastResult result = new FastResult<>();        result.setData(body);        if (returnType.getGenericParameterType().equals(String.class)) {            try {                response.getHeaders().set("Content-Type", "application/json;charset=utf-8");                return objectMapper.writeValueAsString(result);            } catch (JsonProcessingException e) {                e.printStackTrace();            }        }        return result;    }}

看完上述内容,你们掌握SpringMVC中如何利用@ControllerAdvice和ResponseBodyAdvice接口统一处理返回值的方法了吗?如果还想学到更多技能或想了解更多相关内容,欢迎关注行业资讯频道,感谢各位的阅读!

接口 处理 统一 代码 方法 封装 数据 业务 对象 前端 更多 开发 内容 就是 时候 注解 流程 消息 片段 逻辑 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 建立安全风险清单和数据库 云服务器数据会被监控吗 靠谱网络安全运维有哪些 视频会议软件开发服务号 scum雨尚初晨服务器 安庆师范大学数据库期末考试试卷 苹果id建议显示连接服务器出错 双合软件开发公司电话 云数据库备份 数据库配置密码加密方式 宿州电力软件开发定制公司 移动ipv6 dns服务器 底部计算机网络安全股票 软件开发突破 肃南县网络安全审核 阿里云租服务器哪里租 使命召唤游戏服务器老是中断 辽宁软件开发技术服务哪家好 成都大数据软件开发定制费用 软件开发公司需要资质 日本 互联网科技有限公司 交通银行福建省分行软件开发 政务外网网络安全管理办法 宿迁网络安全准入控制系统多少钱 网络安全中文的书籍推荐 股票软件开发需要认证吗 软件开发人才外包价位 苹果国行服务器在哪里 网络安全qs大学排名 辽宁电子软件开发中心
0