Spring 使用 Jackson 和带有 Gson 的外部库启动

Spring Boot using Jackson and an external Library with Gson

我正在使用 Jackson 的 Spring 引导应用程序(版本 2.4.5)。我可以添加一个使用 gson 的 fat jar 而不影响应用程序的 Jackson 反序列化吗?

当我将 jar 添加到我的应用程序时,该应用程序到处都使用 gson 来反序列化 JSON。但是由于我使用了 Jackson 的一些特殊功能,所以失败了。

很遗憾,我无法在库中更改它。

我使用Gradle在依赖项中添加这样的fat jar:

implementation fileTree(dir: 'libs', include: ['*.jar'])

我正在像这样构建 Jacksons 对象映射器:

private static ObjectMapper getMapper() {
  ObjectMapper mapper = new ObjectMapper();
  mapper.registerModule(new Jdk8Module());
  mapper.registerModule(new JavaTimeModule());

  return mapper;
}

下面几行会引发错误:

List<User> users = objectMapper.readValue(getReader("users.json"), new TypeReference<>() {});
java.lang.ClassCastException: class java.util.LinkedHashMap cannot be cast to class User (java.util.LinkedHashMap is in module java.base of loader 'bootstrap'; onek.books.backend.model.book.Book is in unnamed module of loader org.springframework.boot.devtools.restart.classloader.RestartClassLoader @445d3853)

并且 Gson 被添加到具有以下 Maven 依赖项的 fat jar 中:

<dependency>
  <groupId>com.google.code.gson</groupId>
  <artifactId>gson</artifactId>
  <version>${gson-version}</version>
</dependency>
<dependency>
  <groupId>io.gsonfire</groupId>
  <artifactId>gson-fire</artifactId>
  <version>${gson-fire-version}</version>
</dependency>

编辑

正如所建议的那样,这不是由于 gson,我试图缩小范围并一一添加所有依赖项,none 本身导致了错误。

但是我可能找到了原因,但我不知道如何解决这个问题。这是 Jacksons typeFactory 构造它的类型的方式。对于行:

List<Book> books = objectMapper.readValue(getReader("books.json"), new TypeReference<>() {});

如果 jar 不存在,构造的类型如下所示:

result = {CollectionType@13667} "[collection type; class java.util.List, contains [simple type, class onek.books.backend.model.book.Book]]"
 _elementType = {SimpleType@13669} "[simple type, class onek.books.backend.model.book.Book]"
 _superClass = null
 _superInterfaces = {JavaType[1]@13670} 
 _bindings = {TypeBindings@13671} "<Lonek/books/backend/model/book/Book;>"
 _canonicalName = null
 _class = {Class@352} "interface java.util.List"
 _hash = -95724352
 _valueHandler = null
 _typeHandler = null
 _asStatic = false

这是我所期望的,因为 Book-Class 属于这种类型。但是如果我添加罐子,构造的类型看起来像:

result = {SimpleType@11757} "[simple type, class java.lang.Object]"
 _superClass = null
 _superInterfaces = null
 _bindings = {TypeBindings@11759} "<>"
 _canonicalName = null
 _class = {Class@595} "class java.lang.Object"
 _hash = 1063877011
 _valueHandler = null
 _typeHandler = null
 _asStatic = false

并且 jackson 将其反序列化为具有此类型的 LinkedHashmap,从而导致 ClassCastException。

但我真的不知道如何从这里继续。外部 jar 可以改变 Jackson TypeFactory 的某些部分吗?

当 Jackson 尝试反序列化未提供类型信息的对象时,它使用 LinkedHashMap 来存储对象属性值。异常可能与此有关。

我猜 fat jar 可能包含与您在项目中包含的版本不同的 Jackson 版本,并且由于某种原因,这种行为有所不同。

我不知道这是否只是问题的不准确,但为了解决问题,请尝试在 TypeReference:

中提供实际类型
List<Book> books = objectMapper.readValue(getReader("books.json"), new TypeReference<List<Book>>() {});

请考虑阅读 this related article and

问题很可能出在“胖 JAR”的创建上。

创建fat JAR时注意以下事项:

  • META-INF/MANIFEST.MF 应忽略依赖项提供的
  • module-info.classMETA-INF/versions/<i><version></i>/module-info.class 提供者依赖关系应该被忽略(后者被Multi-Release JARs使用)
  • 应合并 META-INF/services/ 下的文件内容(它们声明 providers for services
  • 可选:如果其中一个依赖项是 Multi-Release JARs,您可以在胖 JAR 的 MANIFEST.MF 中设置 Multi-Release: true 以允许 JRE 使用这些版本特定的 类
  • 对于所有其他文件,如何处理冲突可能是特定于应用程序的

如果您使用插件生成胖 JAR,那么它可能会提供这些转换的设置(或者可能默认执行它们)。