概要
記事一覧はこちらです。
Spring Boot 2.5.x の Web アプリを 2.6.x へバージョンアップする ( その2 )( Spring Boot を 2.5.5 → 2.5.9 へ、Gradle を 7.2 → 7.4 へバージョンアップする ) の続きです。
- 今回の手順で確認できるのは以下の内容です。
- Spring Boot を 2.5.9 → 2.6.3 へバージョンアップします。
参照したサイト・書籍
FasterXML / jackson-dataformat-xml
https://github.com/FasterXML/jackson-dataformat-xml
Configure Jackson ObjectMapper in Spring MVC
https://vividcode.io/configure-jackson-object-mapper-in-spring-mvc/
目次
- Spring Initializr で 2.6.3 のプロジェクトを作成する
- build.gradle を変更する
- 出力されたメッセージ・失敗したテストの原因を取り除く(かもしれない)
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
testGetLibraryListByJackson2Xml() FAILED
手順
Spring Initializr で 2.6.3 のプロジェクトを作成する
Spring Initializr で 2.6.3 のプロジェクトを作成して、生成された build.gradle を見て反映した方が良い点があるか確認します。

以下の build.gradle が作成されました。
plugins {
id 'org.springframework.boot' version '2.6.3'
id 'io.spring.dependency-management' version '1.0.11.RELEASE'
id 'java'
}
group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '17'
configurations {
compileOnly {
extendsFrom annotationProcessor
}
}
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-actuator'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-data-redis'
implementation 'org.springframework.boot:spring-boot-starter-freemarker'
implementation 'org.springframework.boot:spring-boot-starter-mail'
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.session:spring-session-data-redis'
implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity5'
compileOnly 'org.projectlombok:lombok'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
runtimeOnly 'org.postgresql:postgresql'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.springframework.security:spring-security-test'
}
tasks.named('test') {
useJUnitPlatform()
}
今回は反映した方が良さそうな点はありませんでした。
build.gradle を変更する
buildscript {
ext {
group "ksbysample"
version "2.6.3"
}
repositories {
mavenCentral()
maven { url "https://repo.spring.io/release/" }
gradlePluginPortal()
}
dependencies {
classpath "org.postgresql:postgresql:42.3.2"
}
}
plugins {
id "java"
id "groovy"
id "eclipse"
id "idea"
id "org.springframework.boot" version "2.6.3"
id "io.spring.dependency-management" version "1.0.11.RELEASE"
id "checkstyle"
id "com.github.spotbugs" version "4.7.3"
id "pmd"
id "net.ltgt.errorprone" version "2.0.2"
id "com.gorylenko.gradle-git-properties" version "2.4.0"
id "org.seasar.doma.codegen" version "1.4.1"
}
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
wrapper {
gradleVersion = "7.4"
distributionType = Wrapper.DistributionType.ALL
}
[compileJava, compileTestGroovy, compileTestJava]*.options*.encoding = "UTF-8"
[compileJava, compileTestGroovy, compileTestJava]*.options*.compilerArgs = ["-Xlint:all,-options,-processing,-path"]
tasks.withType(JavaCompile).configureEach {
options.errorprone {
disableWarningsInGeneratedCode = true
disable("SameNameButDifferent")
}
}
tasks.named("compileTestJava").configure {
options.errorprone.enabled = false
}
bootJar {
duplicatesStrategy = DuplicatesStrategy.INCLUDE
}
jar {
enabled = false
}
task copyDomaResources(type: Sync) {
from sourceSets.main.resources.srcDirs
into compileJava.destinationDirectory
include "doma.compile.config"
include "META-INF/**/*.sql"
include "META-INF/**/*.script"
}
compileJava.dependsOn copyDomaResources
springBoot {
buildInfo()
}
idea {
module {
inheritOutputDirs = false
outputDir = file("$buildDir/classes/main/")
}
}
configurations {
compileOnly.extendsFrom annotationProcessor
testAnnotationProcessor.extendsFrom annotationProcessor
testImplementation.extendsFrom compileOnly
all {
exclude group: "junit", module: "junit"
}
spotbugsStylesheets { transitive = false }
}
checkstyle {
configFile = file("${rootProject.projectDir}/config/checkstyle/google_checks.xml")
toolVersion = "9.0"
sourceSets = [project.sourceSets.main]
}
spotbugs {
toolVersion = "${spotbugs.toolVersion.get()}"
ignoreFailures = true
spotbugsTest.enabled = false
excludeFilter = file("${rootProject.projectDir}/config/spotbugs/exclude.xml")
}
spotbugsMain {
reports {
html {
enabled = true
}
}
}
pmd {
toolVersion = "6.38.0"
sourceSets = [project.sourceSets.main]
ignoreFailures = true
consoleOutput = true
ruleSetFiles = rootProject.files("/config/pmd/pmd-project-rulesets.xml")
ruleSets = []
}
repositories {
mavenCentral()
}
dependencyManagement {
imports {
mavenBom(org.springframework.boot.gradle.plugin.SpringBootPlugin.BOM_COORDINATES) {
}
mavenBom("org.junit:junit-bom:5.8.2")
}
}
dependencies {
def spockVersion = "2.0-groovy-3.0"
def jdbcDriver = "org.postgresql:postgresql:42.3.2"
def domaVersion = "2.51.0"
def lombokVersion = "1.18.22"
def errorproneVersion = "2.9.0"
implementation("org.springframework.boot:spring-boot-starter-web")
implementation("org.springframework.boot:spring-boot-starter-validation")
implementation("org.springframework.boot:spring-boot-starter-thymeleaf")
implementation("org.thymeleaf.extras:thymeleaf-extras-springsecurity5")
implementation("org.springframework.boot:spring-boot-starter-data-jpa")
implementation("org.springframework.boot:spring-boot-starter-freemarker")
implementation("org.springframework.boot:spring-boot-starter-mail")
implementation("org.springframework.boot:spring-boot-starter-security")
implementation("org.springframework.boot:spring-boot-starter-data-redis")
implementation("org.springframework.boot:spring-boot-starter-amqp")
implementation("org.springframework.boot:spring-boot-starter-actuator")
developmentOnly("org.springframework.boot:spring-boot-devtools")
annotationProcessor("org.springframework.boot:spring-boot-configuration-processor")
implementation("org.springframework.session:spring-session-data-redis")
implementation("org.springframework.retry:spring-retry")
implementation("com.fasterxml.jackson.dataformat:jackson-dataformat-xml")
implementation("org.apache.commons:commons-lang3")
implementation("org.codehaus.janino:janino")
implementation("io.micrometer:micrometer-registry-prometheus")
testImplementation("org.springframework.boot:spring-boot-starter-test")
testImplementation("org.springframework.security:spring-security-test")
testImplementation("org.yaml:snakeyaml")
testImplementation("org.codehaus.groovy:groovy-sql")
testImplementation("org.mockito:mockito-inline")
runtimeOnly("${jdbcDriver}")
implementation("com.integralblue:log4jdbc-spring-boot-starter:2.0.0")
implementation("org.simpleframework:simple-xml:2.7.1")
implementation("com.univocity:univocity-parsers:2.9.1")
implementation("com.google.guava:guava:31.0.1-jre")
implementation("org.flywaydb:flyway-core:8.4.4")
testImplementation("org.dbunit:dbunit:2.7.2") {
exclude group: "postgresql", module: "postgresql"
}
testImplementation("com.icegreen:greenmail:1.6.5")
testImplementation("org.assertj:assertj-core:3.22.0")
testImplementation("com.jayway.jsonpath:json-path:2.7.0")
testImplementation("org.jsoup:jsoup:1.14.3")
testImplementation("cglib:cglib-nodep:3.3.0")
testImplementation("org.spockframework:spock-core:${spockVersion}")
testImplementation("org.spockframework:spock-spring:${spockVersion}")
annotationProcessor("org.projectlombok:lombok:${lombokVersion}")
compileOnly("org.projectlombok:lombok:${lombokVersion}")
implementation("org.seasar.doma:doma-core:${domaVersion}")
implementation("org.seasar.doma:doma-slf4j:${domaVersion}")
annotationProcessor("org.seasar.doma:doma-processor:${domaVersion}")
testImplementation("org.junit.jupiter:junit-jupiter")
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
errorprone("com.google.errorprone:error_prone_core:${errorproneVersion}")
compileOnly("com.google.errorprone:error_prone_annotations:${errorproneVersion}")
spotbugs(configurations.spotbugsPlugins.dependencies)
annotationProcessor("com.github.spotbugs:spotbugs-annotations:${spotbugs.toolVersion.get()}")
spotbugsPlugins("com.h3xstream.findsecbugs:findsecbugs-plugin:1.11.0")
}
..........
Spring Boot 2.5.4 へのバージョンアップとして以下の点を変更します。
- buildscript block の以下の点を変更します。
version "2.5.9"
→ version "2.6.3"
- plugins block の以下の点を変更します。
id "org.springframework.boot" version "2.5.9"
→ id "org.springframework.boot" version "2.6.3"
各種ライブラリのバージョンアップとして以下の点を変更します。
- buildscript block の以下の点を変更します。
classpath "org.postgresql:postgresql:42.2.24"
→ classpath "org.postgresql:postgresql:42.3.2"
- plugins block の以下の点を変更します。
id "com.gorylenko.gradle-git-properties" version "2.3.1"
→ id "com.gorylenko.gradle-git-properties" version "2.4.0"
- dependencyManagement block の以下の点を変更します。
mavenBom("org.junit:junit-bom:5.8.1")
→ mavenBom("org.junit:junit-bom:5.8.2")
- dependencies block の以下の点を変更します。
def jdbcDriver = "org.postgresql:postgresql:42.2.24"
→ def jdbcDriver = "org.postgresql:postgresql:42.3.2"
def domaVersion = "2.49.0"
→ def domaVersion = "2.51.0"
implementation("com.google.guava:guava:30.1.1-jre")
→ implementation("com.google.guava:guava:31.0.1-jre")
implementation("org.flywaydb:flyway-core:7.14.0")
→ implementation("org.flywaydb:flyway-core:8.4.4")
testImplementation("org.assertj:assertj-core:3.20.2")
→ testImplementation("org.assertj:assertj-core:3.22.0")
testImplementation("com.jayway.jsonpath:json-path:2.6.0")
→ testImplementation("com.jayway.jsonpath:json-path:2.7.0")
testImplementation("org.jsoup:jsoup:1.14.2")
→ testImplementation("org.jsoup:jsoup:1.14.3")
Gradle Tool Window の左上にある「Refresh All Gradle Projects」ボタンをクリックして更新します。
clean タスク実行 → Rebuild Project 実行 → build タスクを実行すると SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
のメッセージが出力されました。

SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
また、テストが1件失敗しました。

出力されたメッセージの原因を取り除く(かもしれない)
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
2.6.3 と 2.5.9 の gradlew dependencies
コマンドの出力結果を比較すると、
- 削除されたモジュールは以下の6つ。
- http://www.slf4j.org/codes.html#StaticLoggerBinder には
Note that slf4j-api versions 2.0.x and later use the ServiceLoader mechanism. Backends such as logback 1.3 and later which target slf4j-api 2.x, do not ship with org.slf4j.impl.StaticLoggerBinder.
と記述されていますが、2.6.3 と 2.5.9 のどちらも slf4j-api のバージョンは 1.7.33、logback のバージョンは 1.2.10 でした。
If you place a logging backend which targets slf4j-api 2.0.x, you need slf4j-api-2.x.jar on the classpath.
の記述もありますが、org.slf4j:slf4j-simple:1.8.0-beta4 が spotbugsSlf4j - configuration for the SLF4J provider to run SpotBugs に含まれていました。
という結果で SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
のメッセージが出力された原因は分からず。。。
build.gradle で変更した点を一旦元に戻して1つずつ変更して確認しようとしてみたところ、
- 2回目以降の build タスクでは
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
のメッセージが出力されない。
gradlew --stop
で Gradle Daemon を停止してから build タスクを実行すると、最初の1回目だけ SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
のメッセージが出力される。
ということが判明しました。
メッセージが出力される原因は Gradle のようで気にする必要もなさそうなので、何も対応はしないことにします。
testGetLibraryListByJackson2Xml() FAILED
テストが失敗した時にコンソールに出力されたメッセージは以下の内容でした。
CalilApiServiceTest > testGetLibraryListByJackson2Xml() FAILED
org.springframework.web.client.RestClientException at CalilApiServiceTest.java:37
Caused by: org.springframework.http.converter.HttpMessageNotReadableException at CalilApiServiceTest.java:37
Caused by: com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException at CalilApiServiceTest.java:37
testGetLibraryListByJackson2Xml のテストを単独で実行すると以下のメッセージが出力されました。

Caused by: com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "Library" (class ksbysample.webapp.lending.service.calilapi.response.LibrariesForJackson2Xml), not marked as ignorable (one known property: "libraryList"])
at [Source: (PushbackInputStream); line: 4, column: 12] (through reference chain: ksbysample.webapp.lending.service.calilapi.response.LibrariesForJackson2Xml["Library"])
Unrecognized field "Library" (class ksbysample.webapp.lending.service.calilapi.response.LibrariesForJackson2Xml)
と出力されており、LibrariesForJackson2Xml クラスを見ると @XmlElement(name = "Library")
が動作していないようです。
@XmlRootElement(name = "Libraries")
@Data
public class LibrariesForJackson2Xml {
private List<LibraryForJackson2Xml> libraryList = new ArrayList<>();
@XmlElement(name = "Library")
public void addLibraryList(LibraryForJackson2Xml library) {
libraryList.add(library);
}
}
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
で調査した時に com.fasterxml.jackson.module:jackson-module-jaxb-annotations
が依存関係から削除されていたので、おそらくこれが原因でしょう。
build.gradle の dependencies block に implementation("com.fasterxml.jackson.module:jackson-module-jaxb-annotations:2.13.1")
を追加すると失敗したテストが成功するようになりました。

ただし削除された JAXB 関連のモジュールを追加するのも違和感があるので、別の方法を考えてみます。
https://github.com/FasterXML/jackson-dataformat-xml#additional-annotations を見ると @XmlRootElement、@XmlElement の代わりとして @JacksonXmlRootElement、@JacksonXmlProperty が提供されているようなので、それらに変更します。
package ksbysample.webapp.lending.service.calilapi.response;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
@JacksonXmlRootElement(localName = "Libraries")
@Data
public class LibrariesForJackson2Xml {
private List<LibraryForJackson2Xml> libraryList = new ArrayList<>();
@JacksonXmlProperty(localName = "Library")
public void addLibraryList(LibraryForJackson2Xml library) {
libraryList.add(library);
}
}
また、調査中に Configure Jackson ObjectMapper in Spring MVC の記事を見つけました。今は直接 XmlMapper を new するのではなく Jackson2ObjectMapperBuilder を使用する方が良さそうなので、src/main/java/ksbysample/webapp/lending/config/ApplicationConfig.java の mappingJackson2XmlHttpMessageConverter メソッドを以下のように変更します。
@SuppressWarnings({"PMD.UnnecessaryFullyQualifiedName"})
@Bean
@ConditionalOnClass(com.fasterxml.jackson.dataformat.xml.XmlMapper.class)
public MappingJackson2XmlHttpMessageConverter mappingJackson2XmlHttpMessageConverter() {
Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
return new MappingJackson2XmlHttpMessageConverter(builder.createXmlMapper(true).build().findAndRegisterModules());
}
testGetLibraryListByJackson2Xml のテストを実行すると成功することが確認できました。

clean タスク実行 → Rebuild Project 実行 → build タスクを実行すると、今度は "BUILD SUCCESSFUL" のメッセージが出力されました。

履歴
2022/02/18
初版発行。