Springboot 之 HandlerMethodReturnValueHandler 运用

简介: Springboot 之 HandlerMethodReturnValueHandler 运用

简介

现在项目中大部分采用前后端分离的架构,采用这种架构的项目,在返回数据时,几乎都是采用返回 json 格式的数据。而 spring 中返回 json 格式的数据一般采用 @RestController 或者 @ResponseBody 注解。代码样例

@ResponseBody
@RequestMapping("/reqBody")
public ResultInfo<Map<String, Object>> reqBody(){
    ResultInfo<Map<String, Object>> resultInfo = new ResultInfo<>();
    resultInfo.setCode(200);
    resultInfo.setMessage("success");
    Map<String, Object> map = new HashMap<>();
    map.put("userId", 100);
    map.put("tenantId", 1001);
    map.put("userName", "bug弄潮儿");
    resultInfo.setBody(map);
    return resultInfo;
  }

今天定义一个注解读返回的 json 进行加密,来运用 HandlerMethodReturnValueHandler

pom.xml 文件引入依赖

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.olive</groupId>
  <artifactId>springmvc-response-body</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <packaging>jar</packaging>
  <name>springmvc-response-body</name>
  <url>http://maven.apache.org</url>
  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.5.14</version>
    <relativePath /> <!-- lookup parent from repository -->
  </parent>
  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>8</maven.compiler.source>
    <maven.compiler.target>8</maven.compiler.target>
  </properties>
  <dependencies>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-test</artifactId>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
      <groupId>com.alibaba.fastjson2</groupId>
      <artifactId>fastjson2</artifactId>
      <version>2.0.14</version>
    </dependency>
  </dependencies>
</project>

定义加密注解

package com.olive.annotation;
import java.lang.annotation.*;
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Encrypted {
boolean value() default true;
}

Encrypted 注解,该注解是一个标识注解;如果打上该注解标识加密

统一返回定义

主要包含 code、message 和 body 属性定义

package com.olive.dto;
import lombok.Data;
import java.io.Serializable;
@Data
public class ResultInfo<T> implements Serializable {
public  int code;
public String message;
private T body;
private boolean encrypt;
}

自定义 ResponseBodyHandler

该类实现 HandlerMethodReturnValueHandler 类,主要对 @RestController 或者 @ResponseBody 注解进行解析

package com.olive.config;
import com.alibaba.fastjson2.JSON;
import com.olive.annotation.Encrypted;
import com.olive.dto.ResultInfo;
import org.springframework.core.MethodParameter;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.util.Base64Utils;
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 java.nio.charset.StandardCharsets;
public class ResponseBodyHandler implements HandlerMethodReturnValueHandler {
protected final HandlerMethodReturnValueHandler handlerMethodReturnValueHandler;
public ResponseBodyHandler(HandlerMethodReturnValueHandler handlerMethodReturnValueHandler){
this.handlerMethodReturnValueHandler = handlerMethodReturnValueHandler;
    }
@Override
public boolean supportsReturnType(MethodParameter returnType) {
//如果被@ResponseBody注解修饰的 返回true
return (AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ResponseBody.class) || returnType.hasMethodAnnotation(ResponseBody.class))
                && returnType.hasMethodAnnotation(Encrypted.class);
    }
@Override
public void handleReturnValue(Object returnValue,
                                  MethodParameter returnType,
                                  ModelAndViewContainer mavContainer,
                                  NativeWebRequest webRequest) throws Exception {
if(returnValue instanceof ResultInfo){
            ResultInfo<?> resultInfo = (ResultInfo<?>)returnValue;
            ResultInfo<String> newResultInfo = new ResultInfo<>();
            newResultInfo.setCode(resultInfo.getCode());
            newResultInfo.setMessage(resultInfo.getMessage());
            newResultInfo.setEncrypt(true);
            newResultInfo.setBody(Base64Utils.encodeToString(JSON.toJSONString(resultInfo.getBody()).getBytes(StandardCharsets.UTF_8)));
//ResponseBody注解执行器
            handlerMethodReturnValueHandler.handleReturnValue(newResultInfo,
                    returnType, mavContainer, webRequest);
        }else{
            handlerMethodReturnValueHandler.handleReturnValue(returnValue,
                    returnType, mavContainer,  webRequest);
        }
    }
}

注册 ResponseBodyHandler 到 controller 返回值处理器里,即添加自己的返回值处理器

package com.olive.config;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
import org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor;
import java.util.ArrayList;
import java.util.List;
@Configuration
public class WebConfig implements InitializingBean {
@Autowired
private RequestMappingHandlerAdapter adapter;
@Override
public void afterPropertiesSet() throws Exception {
        List<HandlerMethodReturnValueHandler> unmodifiableList = adapter.getReturnValueHandlers();
        List<HandlerMethodReturnValueHandler> list = new ArrayList<>(unmodifiableList.size());
for (HandlerMethodReturnValueHandler returnValueHandler : unmodifiableList) {
if (returnValueHandler instanceof RequestResponseBodyMethodProcessor) {
//将RequestResponseBodyMethodProcessor 实际返回值替换为自定义的,实际执行为RequestResponseBodyMethodProcessor
//重要
HandlerMethodReturnValueHandler handler = new ResponseBodyHandler(returnValueHandler);
                list.add(handler);
            } else {
                list.add(returnValueHandler);
            }
        }
        adapter.setReturnValueHandlers(list);
    }
}

测试

编写 Springboot 启动引导类

package com.olive;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
 * https://cloud.tencent.com/developer/article/1616704
 *
 * @author 2230
 *
 */
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class);
    }
}

编写测试 Controller

package com.olive.controller;
import java.util.HashMap;
import java.util.Map;
import com.olive.annotation.Encrypted;
import com.olive.dto.ResultInfo;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class TestController {
  @Encrypted
  @RequestMapping("/reqBody")
  public ResultInfo<Map<String, Object>> reqBody(){
    ResultInfo<Map<String, Object>> resultInfo = new ResultInfo<>();
    resultInfo.setCode(200);
    resultInfo.setMessage("success");
    Map<String, Object> map = new HashMap<>();
    map.put("userId", 100);
    map.put("tenantId", 1001);
    map.put("userName", "bug弄潮儿");
    resultInfo.setBody(map);
    return resultInfo;
  }
}

通过 HandlerMethodReturnValueHandler 可以对返回的数据进行进一步的封装,减少在业务代码中进行重复的返回值处理。例如,文章中的对返回数据进行统一加密。

相关文章
|
SQL 关系型数据库 MySQL
TiDB亿级数据亚秒响应查询将MySql数据全量迁移到TiDB
TiDB亿级数据亚秒响应查询将MySql数据全量迁移到TiDB
615 0
|
Java Apache 数据格式
httpclient 解决 connection reset 问题
  httpclient 解决 connection reset 问题   错误如下:   java.net.SocketException: Connection reset at java.
9058 0
|
安全 Java 应用服务中间件
Spring Boot 实现程序的优雅退出
Spring Boot 实现程序的优雅退出
|
JavaScript
Vue3加载中(Spin)
该加载组件 `Spin.vue` 可高度自定义加载状态,包括是否显示加载 (`spinning`)、尺寸 (`size`)、描述文案 (`tip`)、加载指示符 (`indicator`) 及其颜色 (`color`) 和其他细节参数。支持多种加载样式如点、线、圆环等,并允许调整动画速度和初始旋转状态。组件基于 Vue 构建,易于集成到任何项目中,提供丰富的视觉反馈。
225 5
Vue3加载中(Spin)
|
前端开发 JavaScript
HTML+JavaScript+CSS DIY 分隔条splitter
HTML+JavaScript+CSS DIY 分隔条splitter
|
域名解析 存储 缓存
在Linux中,DNS进行域名解析的过程是什么?
在Linux中,DNS进行域名解析的过程是什么?
|
流计算 计算机视觉 索引
使用ffmpeg将视频转成HLS(m3u8)格式
HLS (HTTP Live Streaming)是苹果推出的视频流协议,HLS格式的视频包含一个m3u8文本文件,以及众多的.ts的视频片段,而m3u8文本文件的作用就是将这些ts片段索引起来。 因为HLS协议是将视频切分成很多小的ts片段,这些小片段很适合放到cdn上,有很多视频文章都使用了hls格式传输视频。今天我在这里教大家如何用ffmpeg将mp4格式的视频转为HLS(m3u8)格式。
1143 0
|
IDE 程序员 开发工具
IDEA插件-Material Theme UI/IDEA最强主题插件/IDEA图标美化
"Material Theme UI" 是一个用于 JetBrains IDE(如 IntelliJ IDEA、WebStorm、Android Studio 等)的插件,它将原始外观改为 Material Design 风格,并提供丰富的选项来根据个人喜好配置 IDE。
4583 0
IDEA插件-Material Theme UI/IDEA最强主题插件/IDEA图标美化
|
前端开发 JavaScript 数据安全/隐私保护
从0到1开发一个自己的npm包完整过程
创建自己的 npm 包涉及六个步骤:1) 注册 npm 账号;2) 使用 `npm init` 初始化项目,确保 package.json 的 name 唯一且 private 为 false;3) 开发项目,可封装 UI 组件、函数库或命令行工具;4) 本地调试,通过 `npm link` 在项目中测试;5) `npm login` 登录账号,可能需切换至官方仓库;6) 使用 `npm publish` 发布项目。注意版本号递增,无意义的包不建议发布。
从0到1开发一个自己的npm包完整过程
|
存储 SQL 关系型数据库
技术好文:TiDB架构及设计实现
技术好文:TiDB架构及设计实现
1025 0
下一篇
开通oss服务