当前位置: 首页 > article >正文

装饰SpringMVC的适配器实现响应自动包装

文章目录

    • 1.common-tool-starter
        • 1.目录结构
        • 2.ResultWrapper.java
    • 2.common-web-starter
        • 1.目录结构
        • 2.IgnoredResultWrapper.java 自定义注解,忽略对返回结果的自动包装
        • 3.ReturnValueHandlersDecorator.java 对适配器进行扩展的装饰器
        • 4.WebAutoConfiguration.java 将装饰器注入容器
    • 3.common-web-starter-demo
        • 1.WebController.java 测试三种使用方式
        • 2.测试
          • 1.第一种:直接使用自动包装成功结果
          • 2.第二种:使用 @IgnoredResultWrapper注解忽略掉自动包装
          • 3.第三种:直接使用ResultWrapper来自己封装结果

1.common-tool-starter

1.目录结构

CleanShot 2025-01-06 at 18.36.32@2x

2.ResultWrapper.java
package com.sunxiansheng.tool.response;

import lombok.Data;

import java.io.Serializable;

/**
 * Description: 通用响应封装类,通过枚举来获取code和message
 *
 * @Author sun
 * @Create 2024/10/11
 * @Version 1.0
 */
@Data
public class ResultWrapper<T> implements Serializable {

    private static final long serialVersionUID = 1L;

    // 是否成功
    private boolean success;

    // 响应代码
    private int code;

    // 响应消息
    private String message;

    // 响应数据
    private T data;

    // 私有构造器,防止外部直接创建
    private ResultWrapper() {
    }

    // 使用内部建造者类进行对象构建
    public static class Builder<T> {

        private boolean success;

        private int code;

        private String message;

        private T data;

        // ============================== 链式调用设置建造者对象 ==============================
        public Builder<T> success(boolean success) {
            this.success = success;
            return this;
        }

        public Builder<T> code(int code) {
            this.code = code;
            return this;
        }

        public Builder<T> message(String message) {
            this.message = message;
            return this;
        }

        public Builder<T> data(T data) {
            this.data = data;
            return this;
        }
        // ============================== 链式调用设置建造者对象 ==============================

        // ============================== 构建ResultWrapper对象 ==============================
        public ResultWrapper<T> build() {
            ResultWrapper<T> result = new ResultWrapper<>();
            result.success = this.success;
            result.code = this.code;
            result.message = this.message;
            result.data = this.data;
            return result;
        }
        // ============================== 构建ResultWrapper对象 ==============================
    }

    // ============================== 快捷方法 ==============================
    public static <T> ResultWrapper<T> ok() {
        return new Builder<T>()
                .success(true)
                .code(RespBeanEnum.SUCCESS.getCode())
                .message(RespBeanEnum.SUCCESS.getMessage())
                .build();
    }

    public static <T> ResultWrapper<T> ok(T data) {
        return new Builder<T>()
                .success(true)
                .code(RespBeanEnum.SUCCESS.getCode())
                .message(RespBeanEnum.SUCCESS.getMessage())
                .data(data)
                .build();
    }

    public static <T> ResultWrapper<T> ok(T data, String message) {
        return new Builder<T>()
                .success(true)
                .code(RespBeanEnum.SUCCESS.getCode())
                .message(message)
                .data(data)
                .build();
    }

    public static <T> ResultWrapper<T> fail() {
        return new Builder<T>()
                .success(false)
                .code(RespBeanEnum.ERROR.getCode())
                .message(RespBeanEnum.ERROR.getMessage())
                .build();
    }

    public static <T> ResultWrapper<T> fail(String message) {
        return new Builder<T>()
                .success(false)
                .code(RespBeanEnum.ERROR.getCode())
                .message(message)
                .build();
    }

    public static <T> ResultWrapper<T> fail(int code, String message) {
        return new Builder<T>()
                .success(false)
                .code(code)
                .message(message)
                .build();
    }

    public static <T> ResultWrapper<T> fail(int code, String message, T data) {
        return new Builder<T>()
                .success(false)
                .code(code)
                .message(message)
                .data(data)
                .build();
    }

    public static <T> ResultWrapper<T> fail(RespBeanEnum respBeanEnum) {
        return new Builder<T>()
                .success(false)
                .code(respBeanEnum.getCode())
                .message(respBeanEnum.getMessage())
                .build();
    }
    // ============================== 快捷方法 ==============================
}

2.common-web-starter

1.目录结构

CleanShot 2025-01-06 at 18.37.27@2x

2.IgnoredResultWrapper.java 自定义注解,忽略对返回结果的自动包装
package com.sunxiansheng.web.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * Description: 忽略对返回结果的自动包装
 *
 * @Author sun
 * @Create 2025/1/6 15:58
 * @Version 1.0
 */
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface IgnoredResultWrapper {

}
3.ReturnValueHandlersDecorator.java 对适配器进行扩展的装饰器
package com.sunxiansheng.web.config;

import com.sunxiansheng.tool.response.ResultWrapper;
import com.sunxiansheng.web.annotation.IgnoredResultWrapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.core.MethodParameter;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
import org.springframework.web.method.support.ModelAndViewContainer;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
import org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

/**
 * Description: 装饰器
 *
 * @Author sun
 * @Create 2025/1/6 14:40
 * @Version 1.0
 */
@Slf4j
public class ReturnValueHandlersDecorator implements InitializingBean {

    /**
     * 注入适配器,可以获取
     */
    @Resource
    private RequestMappingHandlerAdapter requestMappingHandlerAdapter;

    @Override
    public void afterPropertiesSet() {
        List<HandlerMethodReturnValueHandler> returnValueHandlers = requestMappingHandlerAdapter.getReturnValueHandlers();
        // 因为默认List<HandlerMethodReturnValueHandler>是不可修改列表,所以需要给他转换成一个可修改的列表
        assert returnValueHandlers != null;
        List<HandlerMethodReturnValueHandler> handlers = new ArrayList<>(returnValueHandlers);
        // 装饰一下然后重新设置
        this.decorateHandlers(handlers);
        // 将装饰后的handlers替换原来的不可变列表
        requestMappingHandlerAdapter.setReturnValueHandlers(handlers);
    }

    private void decorateHandlers(List<HandlerMethodReturnValueHandler> returnValueHandlers) {
        for (HandlerMethodReturnValueHandler returnValueHandler : returnValueHandlers) {
            if (returnValueHandler instanceof RequestResponseBodyMethodProcessor) {
                // 找到RequestResponseBodyMethodProcessor
                ControllerReturnValueHandler controllerReturnValueHandler = new ControllerReturnValueHandler(returnValueHandler);
                // 找到在原列表中的索引位置
                int index = returnValueHandlers.indexOf(returnValueHandler);
                // 将装饰后的HandlerMethodReturnValueHandler放到原来的位置
                returnValueHandlers.set(index, controllerReturnValueHandler);
            }
        }
    }

    private static class ControllerReturnValueHandler implements HandlerMethodReturnValueHandler {

        // 拿到被装饰的对象
        private final HandlerMethodReturnValueHandler handler;

        public ControllerReturnValueHandler(HandlerMethodReturnValueHandler handler) {
            this.handler = handler;
        }

        /**
         * 保持跟原生的一致
         *
         * @param returnType
         * @return
         */
        @Override
        public boolean supportsReturnType(MethodParameter returnType) {
            return AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ResponseBody.class) || returnType.hasMethodAnnotation(ResponseBody.class);
        }

        @Override
        public void handleReturnValue(Object o, MethodParameter methodParameter, ModelAndViewContainer modelAndViewContainer, NativeWebRequest nativeWebRequest) throws Exception {
            IgnoredResultWrapper methodAnnotation = methodParameter.getMethodAnnotation(IgnoredResultWrapper.class);
            // 如果有IgnoredResultWrapper注解,就不进行包装,走原来的逻辑
            if (Objects.nonNull(methodAnnotation)) {
                handler.handleReturnValue(o, methodParameter, modelAndViewContainer, nativeWebRequest);
                return;
            }
            // 如果返回值是ResultWrapper类型,也不进行包装,走原来的逻辑
            if (o instanceof ResultWrapper) {
                handler.handleReturnValue(o, methodParameter, modelAndViewContainer, nativeWebRequest);
                return;
            }
            // 其余情况,进行包装
            log.info("Controller返回值已被自动包装,如果上传文件,请加@IgnoredResultWrapper注解取消自动包装!");
            ResultWrapper<Object> ok = ResultWrapper.ok(o);
            handler.handleReturnValue(ok, methodParameter, modelAndViewContainer, nativeWebRequest);
        }
    }
}
4.WebAutoConfiguration.java 将装饰器注入容器
    /**
     * 注入对返回结果增强的装饰器
     *
     * @return
     */
    @Bean
    @ConditionalOnMissingBean
    public ReturnValueHandlersDecorator returnValueHandlersDecorator() {
        return new ReturnValueHandlersDecorator();
    }

3.common-web-starter-demo

1.WebController.java 测试三种使用方式
package com.sunxiansheng.web.controller;

import com.sunxiansheng.tool.response.ResultWrapper;
import com.sunxiansheng.web.annotation.IgnoredResultWrapper;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * Description: 测试
 *
 * @Author sun
 * @Create 2024/10/4 22:56
 * @Version 1.0
 */
@RestController
public class WebController {

    /**
     * 第一种方式:直接使用自动包装成功结果
     *
     * @return
     */
    @RequestMapping("/method1")
    public String method1() {
        return "method1";
    }

    /**
     * 第二种方式:使用 @IgnoredResultWrapper注解忽略掉自动包装
     *
     * @return
     */
    @IgnoredResultWrapper
    @RequestMapping("/method2")
    public String method2() {
        return "method2";
    }

    /**
     * 第三种方式:直接使用ResultWrapper来自己封装结果
     *
     * @return
     */
    @RequestMapping("/method3")
    public ResultWrapper<String> method3() {
        return ResultWrapper.fail("method3");
    }
}
2.测试
1.第一种:直接使用自动包装成功结果

CleanShot 2025-01-06 at 18.40.09@2x

2.第二种:使用 @IgnoredResultWrapper注解忽略掉自动包装

CleanShot 2025-01-06 at 18.40.48@2x

3.第三种:直接使用ResultWrapper来自己封装结果

CleanShot 2025-01-06 at 18.41.18@2x


http://www.kler.cn/a/522707.html

相关文章:

  • 记忆力训练day07
  • 机器学习(三)
  • Niagara学习笔记
  • 可爱狗狗的404动画页面HTML源码
  • 制造企业的成本核算
  • 开关电路汇总
  • 每日一题 429. N 叉树的层序遍历
  • WebPages 表单:设计与实现指南
  • react-bn-面试
  • 使用国内镜像加速器解决 Docker Hub 拉取镜像慢或被屏蔽的问题
  • 学习第七十六行
  • 备份与恢复管理系统深度解析及代码实践
  • GitHub 仓库的 Archived 功能详解:中英双语
  • 炫酷JavaScript文本时钟
  • 跨平台物联网漏洞挖掘算法评估框架设计与实现项目后期研究方案
  • 008 mybatis缓存
  • 全志 视频输入组件的使用
  • 强化学习在自动驾驶中的实现与挑战
  • RocketMQ优势剖析-性能优化
  • 安卓入门四十二 过渡动画
  • RAG制作客服机器人,文档用表格还是QA问答对?
  • python 一个组合问题:
  • LeetCode100之全排列(46)--Java
  • 无公网IP外网访问开源笔记 Logseq
  • 使用EVE-NG-锐捷实现OSPF
  • ZooKeeper-3.8.3-会话