使用Maven构建的Spring Boot中的Jackson依赖问题

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应用程序中,您可能会发现以下错误:

Jackson Dependency Conflict Error in Spring Boot

您可能会看到几个不同的错误。以下是一些其他示例:

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,并覆盖较新的版本。我们可以通过再次运行dependency:tree命令来看到这一点:

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策划的依赖项。