【Spring】java 单体式应用微服务拆分过程 bug 修复:泛型函数

84 阅读3分钟

💡简介

  • 将单体式 Spring 应用拆分为微服务式应用过程中,遇到了一系列有关 Spring 框架的问题,将修复过程及相关问题信息分析过程记录下来,便于以后解决同类问题,也顺便提高自己对 Spring 框架的理解。

🧠问题

  • 存在一个泛型函数,其输入参数和返回值都是泛型:
@RestController
@RequestMapping("/DTOUtil")
public class DTOUtil {

    private static ModelMapper MAPPER = null;

    private static ModelMapper getMapper() {
        if (MAPPER == null) {
            MAPPER = new ModelMapper();
            MAPPER.getConfiguration().setMatchingStrategy(MatchingStrategies.STRICT);
        }

        return MAPPER;
    }

    @RequestMapping(value = "/map", method = RequestMethod.POST)
    @ResponseBody
    public static <S, T> T map(@RequestBody DTORequestParams<S, T> dtoRequestParams) {
        S source = dtoRequestParams.getSource();
        Class<T> targetClass = dtoRequestParams.getTargetClass();
        return getMapper().map(source, targetClass);
    }
}
  • 当使用 feign 进行远程调用时,会报以下错误:
Caused by: feign.FeignException: status 400 reading DTOUtilRealClient#map(DTORequestParams); content:
{"timestamp":1694347613202,"status":400,"error":"Bad Request","exception":"org.springframework.http.converter.HttpMessageNotReadableException","message":"Bad Request","path":"/DTOUtil/map"}

📚信息搜集

泛型原理[1]

  • 泛型的本质是对类型进行参数化,在代码逻辑不关注具体的数据类型时使用。例如:实现一个通用的排序算法,此时关注的是算法本身,而非排序的对象的类型。

  • 基本原理:

    • 泛型本质是将数据类型参数化,它通过擦除的方式来实现。
      • 声明了泛型的 .java 源代码,在编译生成 .class 文件之后,泛型相关的信息就消失了。可以认为,源代码中泛型相关的信息,就是提供给编译器用的。泛型信息对 Java 编译器可以见,对 Java 虚拟机不可见。
    • Java 编译器通过如下方式实现擦除:
      • 用 Object 或者界定类型替代泛型,产生的字节码中只包含了原始的类,接口和方法;
      • 在恰当的位置插入强制转换代码来确保类型安全;
      • 在继承了泛型类或接口的类中插入桥接方法来保留多态性。

Feign与泛型

  • 个人分析:

    • 基于上一节原理,在源码编译后泛型相关信息就已经被翻译为具体类型。
      • 因此当被调用的泛型类被单独封装在一个微服务中时,编译器无法推断应当翻译为什么类(每个微服务单独编译,不知道会被谁调用)。
      • 这种情况下似乎只能放弃使用泛型类,而是增加更具体的接口。考虑增加一个转换器类作为中介来进行类型的转换。
  • 经过广泛调研,看到多个“当返回值为泛型数据”[2][3]的解决方案,猜测“当参数为泛型数据”的被调用泛型类是无法实现的。

🔨解决

  • 通过增加中介转换类实现推理。
  • (非常不优雅,但从原理分析似乎也很合理,只是代码很丑)
  • (如果有其他优雅的方案,欢迎在留言区告诉我)

🏥反思

  1. 黑猫白猫,能抓耗子就是好猫。和花一周找一个优雅的方案相比(甚至可能一周也找不到),用一个稍显粗鲁的方案也不失为某种意义上的“优雅”。


  • 希望这篇博客对你有帮助!如果你有任何问题或需要进一步的帮助,请随时提问。
  • 如果你喜欢这篇文章,欢迎动动小手给我一个follow或star。

🗺参考文献

[1] Java 中泛型的实现原理

[2] FeignClient使用泛型接收数据

[3] Feign 踩坑指南 (接口返回泛型设置属性为null)