Jackson Dependency Issue in Spring Boot with Maven Build
最近在Spring Boot项目中与Jackson一起工作时,我遇到了一个问题想与大家分享。
Jacksonis当前是Java中解析JSON的首选方法。 Jackson库由三个组件组成:Jackson Databind,Core和Annotation。 Jackson Databind在内部依赖于Jackson Core和Annotation。因此,将Jackson Databind添加到您的Maven POM依赖项列表中还将包括其他依赖项。要使用最新的Jackson库,您需要在Maven POM中添加以下依赖项:
1 2 3 4 5 6 7 | . . . <dependency> <groupId>com.fasterxml.jackson.core</groupId> jackson-databind</artifactId> <version>2.7.4</version> </dependency> . . . |
上面的依赖关系在其他Java项目中效果很好,但是不幸的是,在Spring Boot 1.3.x应用程序中,您可能会发现以下错误:
您可能会看到几个不同的错误。以下是一些其他示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 | java.lang.NoSuchMethodError: com.fasterxml.jackson.annotation.JsonFormat$Value.empty()Lcom/fasterxml/jackson/annotation/JsonFormat$Value; at com.fasterxml.jackson.databind.cfg.MapperConfig.<clinit>(MapperConfig.java:50) at com.fasterxml.jackson.databind.ObjectMapper.<init>(ObjectMapper.java:543) at com.fasterxml.jackson.databind.ObjectMapper.<init>(ObjectMapper.java:460) at guru.springframework.blog.jsonwithjackson.jsonreader.JsonNodeDemo.<init>(JsonNodeDemo.java:19) at guru.springframework.blog.jsonwithjackson.jsonreader.JsonNodeDemoTest.testReadJsonWithJsonNode(JsonNodeDemoTest.java:15) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:497) at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50) at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47) at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17) at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57) at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290) at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71) at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288) at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58) at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268) at org.junit.runners.ParentRunner.run(ParentRunner.java:363) at org.junit.runners.Suite.runChild(Suite.java:128) at org.junit.runners.Suite.runChild(Suite.java:27) at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290) at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71) at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288) at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58) at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268) at org.junit.runners.ParentRunner.run(ParentRunner.java:363) at org.junit.runner.JUnitCore.run(JUnitCore.java:137) at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:119) at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:42) at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:234) at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:74) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:497) at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144) java.lang.NoClassDefFoundError: Could not initialize class com.fasterxml.jackson.databind.SerializationConfig at com.fasterxml.jackson.databind.ObjectMapper.<init>(ObjectMapper.java:543) at com.fasterxml.jackson.databind.ObjectMapper.<init>(ObjectMapper.java:460) at guru.springframework.blog.jsonwithjackson.jsonreader.ObjectMapperDemo.readJsonWithObjectMapper(ObjectMapperDemo.java:13) at guru.springframework.blog.jsonwithjackson.jsonreader.ObjectMapperDemoTest.testReadJson(ObjectMapperDemoTest.java:13) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:497) at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50) at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47) at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17) at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57) at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290) at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71) at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288) at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58) at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268) at org.junit.runners.ParentRunner.run(ParentRunner.java:363) at org.junit.runners.Suite.runChild(Suite.java:128) at org.junit.runners.Suite.runChild(Suite.java:27) at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290) at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71) at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288) at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58) at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268) at org.junit.runners.ParentRunner.run(ParentRunner.java:363) at org.junit.runner.JUnitCore.run(JUnitCore.java:137) at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:119) at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:42) at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:234) at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:74) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:497) at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144) java.lang.NoClassDefFoundError: Could not initialize class com.fasterxml.jackson.databind.SerializationConfig at com.fasterxml.jackson.databind.ObjectMapper.<init>(ObjectMapper.java:543) at com.fasterxml.jackson.databind.ObjectMapper.<init>(ObjectMapper.java:460) at guru.springframework.blog.jsonwithjackson.jsonreader.ObjectMapperToMapDemo.readJsonWithObjectMapper(ObjectMapperToMapDemo.java:15) at guru.springframework.blog.jsonwithjackson.jsonreader.ObjectMapperToMapDemoTest.testReadJsonWithObjectMapper(ObjectMapperToMapDemoTest.java:12) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:497) at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50) at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47) at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17) at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57) at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290) at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71) at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288) at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58) at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268) at org.junit.runners.ParentRunner.run(ParentRunner.java:363) at org.junit.runners.Suite.runChild(Suite.java:128) at org.junit.runners.Suite.runChild(Suite.java:27) at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290) at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71) at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288) at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58) at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268) at org.junit.runners.ParentRunner.run(ParentRunner.java:363) at org.junit.runner.JUnitCore.run(JUnitCore.java:137) at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:119) at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:42) at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:234) at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:74) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:497) at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144) |
由于Jackson依赖性冲突而发生此错误。我们正在开发一个Spring Boot项目,它是从Spring Boot父POM(包括Jackson)继承而来的。在项目POM中没有任何Jackson依赖关系时,让我们打印Maven依赖关系树以查看内置的Jackson依赖关系:
1 | mvn dependency:tree -Dincludes=com.fasterxml.jackson.core |
输出是这样的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | [INFO] ------------------------------------------------------------------------ [INFO] Building Blog Posts 0.0.1-SNAPSHOT [INFO] ------------------------------------------------------------------------ [INFO] [INFO] --- maven-dependency-plugin:2.10:tree (default-cli) @ blogposts --- [INFO] guru.springframework:blogposts:jar:0.0.1-SNAPSHOT [INFO] \- com.fasterxml.jackson.core:jackson-databind:jar:2.6.5:compile [INFO] +- com.fasterxml.jackson.core:jackson-annotations:jar:2.6.5:compile [INFO] \- com.fasterxml.jackson.core:jackson-core:jar:2.6.5:compile [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 2.053 s [INFO] Finished at: 2016-05-18T07:32:59-04:00 [INFO] Final Memory: 19M/309M [INFO] ------------------------------------------------------------------------ |
从上面可以看到,Spring Boot的父POM使用了较旧的Jackson版本(2.6.5)。
现在,如果使用以下版本将Jackson依赖项添加到我们的Maven POM中:
1 2 3 4 5 6 7 | . . . <dependency> <groupId>com.fasterxml.jackson.core</groupId> jackson-databind</artifactId> <version>2.7.4</version> </dependency> . . . |
Maven将引入较旧版本的Jackson注释和Jackson-core,并覆盖较新的版本。我们可以通过再次运行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | [INFO] ------------------------------------------------------------------------ [INFO] Building Blog Posts 0.0.1-SNAPSHOT [INFO] ------------------------------------------------------------------------ [INFO] [INFO] --- maven-dependency-plugin:2.10:tree (default-cli) @ blogposts --- [INFO] guru.springframework:blogposts:jar:0.0.1-SNAPSHOT [INFO] \- com.fasterxml.jackson.core:jackson-databind:jar:2.7.4:compile [INFO] +- com.fasterxml.jackson.core:jackson-annotations:jar:2.6.5:compile [INFO] \- com.fasterxml.jackson.core:jackson-core:jar:2.6.5:compile [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 1.977 s [INFO] Finished at: 2016-05-18T07:35:09-04:00 [INFO] Final Memory: 19M/309M [INFO] ------------------------------------------------------------------------ |
我没想到Maven在依赖关系解析中会表现出这种方式。主Jackson工件的POM确实要求使用正确的版本。但是,这似乎已被Spring Boot父POM中显式指定的版本所取代。
理想情况下,在使用Spring Boot时,我们可以利用Spring Boot父POM中策划的依赖关系。在这种情况下,我们删除Jackson依赖项的版本,以便它将从Spring Boot Parent POM继承。
1 2 3 4 5 6 | . . . <dependency> <groupId>com.fasterxml.jackson.core</groupId> jackson-databind</artifactId> </dependency> . . . |
现在,该版本将从父POM继承,此问题将得到解决。
但是,如果我们想使用更新版本的Jackson,该怎么办?正确的方法是排除固有依赖性,并显式添加其新版本,如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | . . . <dependency> <groupId>com.fasterxml.jackson.core</groupId> jackson-databind</artifactId> <version>2.7.4</version> <exclusions> <exclusion> <groupId>com.fasterxml.jackson.core</groupId> jackson-core</artifactId> </exclusion> <exclusion> <groupId>com.fasterxml.jackson.core</groupId> jackson-annotations</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> jackson-core</artifactId> <version>2.7.4</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> jackson-annotations</artifactId> <version>2.7.4</version> </dependency> . . . |
此POM配置将覆盖Spring Boot父POM中设置的Jackson依赖关系。
这篇文章专门针对Spring Boot版本1.3.3。自然,Spring Boot团队将不断发展未来发行版中使用的Jackson版本。
对于习惯使用Maven依赖关系的开发人员,将版本包含在依赖关系声明中是一个非常容易的错误。由于版本冲突,这可能会导致一些意外的问题。对于经验丰富的开发人员来说,依赖于Spring Boot父POM有点范式的转变。有些人不想放弃控制权,但是从长远来看,我希望您最好利用Spring Boot策划的依赖项。