かんがるーさんの日記

最近自分が興味をもったものを調べた時の手順等を書いています。今は Spring Boot をいじっています。

Spring Boot 2.1.x の Web アプリを 2.2.x へバージョンアップする ( その4 )( Spring Boot を 2.1.11 → 2.2.2 へバージョンアップする )

概要

記事一覧はこちらです。

Spring Boot 2.1.x の Web アプリを 2.2.x へバージョンアップする ( その3 )( Gradle を 5.6.4 → 6.0.1 へバージョンアップする ) の続きです。

  • 今回の手順で確認できるのは以下の内容です。
    • Spring Boot を 2.1.11 → 2.2.2 へバージョンアップします。

参照したサイト・書籍

  1. Issue 438464: json displayed with wrong encoding (i.e. not unicode)
    https://bugs.chromium.org/p/chromium/issues/detail?id=438464

  2. Bug 197369 - JSON displayed with wrong encoding when loaded in a frame (browser default instead of UTF-8)
    https://bugs.webkit.org/show_bug.cgi?id=197369

  3. Deprecate MediaType.APPLICATION_JSON_UTF8 in favor of APPLICATION_JSON
    https://github.com/spring-projects/spring-framework/issues/22788

  4. Upgrading to Spring Framework 5.x - Upgrading to Version 5.2
    https://github.com/spring-projects/spring-framework/wiki/Upgrading-to-Spring-Framework-5.x#upgrading-to-version-52

  5. What's New in Spring Framework 5.x - What's New in Version 5.2
    https://github.com/spring-projects/spring-framework/wiki/What's-New-in-Spring-Framework-5.x#whats-new-in-version-52

目次

  1. Spring Initializr で 2.2.2 のプロジェクトを作成する
  2. build.gradle を変更する
  3. build タスク実行時に出るエラーを修正する
  4. メモ書き

手順

Spring Initializr で 2.2.2 のプロジェクトを作成する

Spring Initializr で 2.2.2 のプロジェクトを作成して、生成された build.gradle を見て反映した方が良い点があるか確認します。

f:id:ksby:20191231105357p:plain f:id:ksby:20191231105449p:plain f:id:ksby:20191231114548p:plain f:id:ksby:20191231114639p:plain f:id:ksby:20191231114803p:plain f:id:ksby:20191231114839p:plain f:id:ksby:20191231114922p:plain f:id:ksby:20191231115024p:plain f:id:ksby:20191231115218p:plain f:id:ksby:20191231115303p:plain f:id:ksby:20191231115406p:plain

以下の build.gradle が作成されました。今回は Spring Boot 2.0.x の Web アプリを 2.1.x へバージョンアップする ( その3 )( build.gradle を変更する ) で実施した時のような「Import Module from Gradle」ダイアログは表示されませんでした。

plugins {
    id 'org.springframework.boot' version '2.2.2.RELEASE'
    id 'io.spring.dependency-management' version '1.0.8.RELEASE'
    id 'java'
}

group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '11'

configurations {
    developmentOnly
    runtimeClasspath {
        extendsFrom developmentOnly
    }
    compileOnly {
        extendsFrom annotationProcessor
    }
}

repositories {
    mavenCentral()
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-actuator'
    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'
    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') {
        exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
    }
    testImplementation 'org.springframework.security:spring-security-test'
}

test {
    useJUnitPlatform()
}

以下の点はこれまで見たことがなかったので build.gradle に反映します。

  • configurations { ... } の記述が追加されている。
  • dependencies block で org.springframework.boot:spring-boot-devtools を指定する時の記述が developmentOnly になっている。
  • testImplementation('org.springframework.boot:spring-boot-starter-test')exclude group: 'org.junit.vintage', module: 'junit-vintage-engine' が記述されている。

build.gradle を変更する

buildscript {
    ext {
        group "ksbysample"
        version "2.2.2-RELEASE"
    }
    repositories {
        mavenCentral()
        maven { url "https://repo.spring.io/release/" }
        gradlePluginPortal()
    }
}

plugins {
    id "java"
    id "eclipse"
    id "idea"
    id "org.springframework.boot" version "2.2.2.RELEASE"
    id "io.spring.dependency-management" version "1.0.8.RELEASE"
    id "groovy"
    id "checkstyle"
//    id "com.github.spotbugs" version "1.6.9"
    id "pmd"
    id "net.ltgt.errorprone" version "0.7.1"
    id "com.gorylenko.gradle-git-properties" version "2.2.0"
}

..........

configurations {
    developmentOnly
    runtimeClasspath.extendsFrom developmentOnly
    compileOnly.extendsFrom annotationProcessor

    // annotationProcessor と testAnnotationProcessor、compileOnly と testCompileOnly を併記不要にする
    testAnnotationProcessor.extendsFrom annotationProcessor
    testImplementation.extendsFrom compileOnly

    // for Doma 2
    domaGenRuntime
}

..........

dependencyManagement {
    imports {
        // mavenBom は以下の URL のものを使用する
        // https://repo.spring.io/release/org/springframework/boot/spring-boot-starter-parent/2.2.2.RELEASE/
        // bomProperty に指定可能な property は以下の URL の BOM に記述がある
        // https://repo.spring.io/release/org/springframework/boot/spring-boot-dependencies/2.2.2.RELEASE/spring-boot-dependencies-2.2.2.RELEASE.pom
        mavenBom(org.springframework.boot.gradle.plugin.SpringBootPlugin.BOM_COORDINATES) {
            // Spring Boot の BOM に定義されているバージョンから変更する場合には、ここに以下のように記述する
            // bomProperty "thymeleaf.version", "3.0.9.RELEASE"
        }
        mavenBom("org.junit:junit-bom:5.5.2")
    }
}

dependencies {
    def jdbcDriver = "org.postgresql:postgresql:42.2.9"
    def spockVersion = "1.3-groovy-2.5"
    def domaVersion = "2.26.0"
    def lombokVersion = "1.18.10"
    def errorproneVersion = "2.3.3"
    def powermockVersion = "2.0.4"
//    def spotbugsVersion = "3.1.11"

    // dependency-management-plugin によりバージョン番号が自動で設定されるもの
    // Appendix F. Dependency versions ( https://docs.spring.io/spring-boot/docs/current/reference/html/appendix-dependency-versions.html ) 参照
    implementation("org.springframework.boot:spring-boot-starter-web")
    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")
    compileOnly("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") {
        exclude group: "org.junit.vintage", module: "junit-vintage-engine"
    }
    testImplementation("org.springframework.security:spring-security-test")
    testImplementation("org.yaml:snakeyaml")

    // dependency-management-plugin によりバージョン番号が自動で設定されないもの、あるいは最新バージョンを指定したいもの
    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.8.4")
    implementation("com.google.guava:guava:28.1-jre")
    implementation("org.flywaydb:flyway-core:6.1.3")
    testImplementation("org.dbunit:dbunit:2.6.0")
    testImplementation("com.icegreen:greenmail:1.5.11")
    testImplementation("org.assertj:assertj-core:3.14.0")
    testImplementation("com.jayway.jsonpath:json-path:2.4.0")
    testImplementation("org.jsoup:jsoup:1.12.1")
    testImplementation("cglib:cglib-nodep:3.3.0")
    testImplementation("org.spockframework:spock-core:${spockVersion}")
    testImplementation("org.spockframework:spock-spring:${spockVersion}")

    // for lombok
    // testAnnotationProcessor、testCompileOnly を併記しなくてよいよう configurations で設定している
    annotationProcessor("org.projectlombok:lombok:${lombokVersion}")
    compileOnly("org.projectlombok:lombok:${lombokVersion}")

    // for Doma
    annotationProcessor("org.seasar.doma:doma:${domaVersion}")
    implementation("org.seasar.doma:doma:${domaVersion}")
    domaGenRuntime("org.seasar.doma:doma-gen:${domaVersion}")
    domaGenRuntime("${jdbcDriver}")

    // for JUnit 5
    // junit-jupiter で junit-jupiter-api, junit-jupiter-params, junit-jupiter-engine の3つが依存関係に追加される
    testImplementation("org.junit.jupiter:junit-jupiter")
    testRuntimeOnly("org.junit.platform:junit-platform-launcher")

    // for Error Prone ( http://errorprone.info/ )
    errorprone("com.google.errorprone:error_prone_core:${errorproneVersion}")
    compileOnly("com.google.errorprone:error_prone_annotations:${errorproneVersion}")

    // PowerMock
    testImplementation("org.powermock:powermock-module-junit4:${powermockVersion}")
    testImplementation("org.powermock:powermock-api-mockito2:${powermockVersion}")

    // for SpotBugs
//    compileOnly("com.github.spotbugs:spotbugs:${spotbugsVersion}")
//    compileOnly("net.jcip:jcip-annotations:1.0")
//    compileOnly("com.github.spotbugs:spotbugs-annotations:${spotbugsVersion}")
//    testImplementation("com.google.code.findbugs:jsr305:3.0.2")
}

Spring Boot 2.2.2 へのバージョンアップとして以下の点を変更します。

  • buildscript block の以下の点を変更します。
    • version "2.1.11-RELEASE"version "2.2.2-RELEASE"
  • plugins block の以下の点を変更します。
    • id "org.springframework.boot" version "2.1.11.RELEASE"id "org.springframework.boot" version "2.2.2.RELEASE"
  • configurations block に以下の3行を追加します。
    • developmentOnly
    • runtimeClasspath.extendsFrom developmentOnly
    • compileOnly.extendsFrom annotationProcessor
  • dependencies block の以下の点を変更します。
    • runtimeOnly("org.springframework.boot:spring-boot-devtools")developmentOnly("org.springframework.boot:spring-boot-devtools")
    • testImplementation("org.springframework.boot:spring-boot-starter-test")exclude group: "org.junit.vintage", module: "junit-vintage-engine" を追加します。

各種ライブラリのバージョンアップとして以下の点を変更します。

  • plugins block の以下の点を変更します。
    • id "com.gorylenko.gradle-git-properties" version "2.0.0"id "com.gorylenko.gradle-git-properties" version "2.2.0"
  • dependencyManagement の以下の点を変更します。
    • mavenBom("org.junit:junit-bom:5.4.1")mavenBom("org.junit:junit-bom:5.5.2")
  • dependencies block の以下の点を変更します。
    • def jdbcDriver = "org.postgresql:postgresql:42.2.5"def jdbcDriver = "org.postgresql:postgresql:42.2.9"
    • def domaVersion = "2.24.0"def domaVersion = "2.26.0"
    • def lombokVersion = "1.18.6"def lombokVersion = "1.18.10"
    • def powermockVersion = "2.0.0"def powermockVersion = "2.0.4"
    • implementation("com.integralblue:log4jdbc-spring-boot-starter:1.0.2")implementation("com.integralblue:log4jdbc-spring-boot-starter:2.0.0")
    • implementation("com.univocity:univocity-parsers:2.8.1")implementation("com.univocity:univocity-parsers:2.8.4")
    • implementation("com.google.guava:guava:27.1-jre")implementation("com.google.guava:guava:28.1-jre")
    • implementation("org.flywaydb:flyway-core:5.2.4")implementation("org.flywaydb:flyway-core:6.1.3")
    • testImplementation("com.icegreen:greenmail:1.5.10")testImplementation("com.icegreen:greenmail:1.5.11")
    • testImplementation("org.assertj:assertj-core:3.12.2")testImplementation("org.assertj:assertj-core:3.14.0")
    • testImplementation("org.jsoup:jsoup:1.11.3")testImplementation("org.jsoup:jsoup:1.12.1")
    • testImplementation("cglib:cglib-nodep:3.2.10")testImplementation("cglib:cglib-nodep:3.3.0")

build タスク実行時に出るエラーを修正する

clean タスク実行 → Rebuild Project 実行 → build タスクを実行すると、build タスクでエラーが出るので修正します。

f:id:ksby:20191231135957p:plain

There were failing tests. See the report at: file:///D:/project-springboot/ksbysample-webapp-lending/build/reports/tests/test/index.html というメッセージが表示されたので、リンクをクリックして HTML をブラウザで表示させてみるとテストが2つ失敗していました。

f:id:ksby:20191231140102p:plain

失敗したログを表示させてみると、どちらも Content type expected:<application/json;charset=UTF-8> but was:<application/json> という理由でした。

f:id:ksby:20191231140320p:plain f:id:ksby:20191231140436p:plain

テストでは Content-Type に application/json;charset=UTF-8 がセットされてくる想定でいましたが、

f:id:ksby:20191231140644p:plain

@RestController が付加されたクラス内のメソッドの @RequestMappingvalue 属性しか指定していないと application/json;charset=UTF-8 ではなく application/json が返ってくるようになったようです。

f:id:ksby:20191231145744p:plain

org.springframework.http.MediaType クラスを見てみると、APPLICATION_JSON_UTF8、APPLICATION_JSON_UTF8_VALUE@Deprecated が付いていました。

f:id:ksby:20191231150817p:plain

Upgrading to Spring Framework 5.x - Upgrading to Version 5.2 にも Deprecation of MediaType.APPLICATION_JSON_UTF8 and MediaType.APPLICATION_PROBLEM_JSON_UTF8 の記述がありました。

よって、ksbysample.webapp.lending.webapi.library.LibraryControllerTest のテストクラス内で期待する Content-Type の文字列を application/json;charset=UTF-8application/json に変更することにします。

    @Test
    void 正しい都道府県を指定した場合には図書館一覧が返る() throws Exception {
        mvc.noauth.perform(get("/webapi/library/getLibraryList?pref=東京都"))
                .andExpect(status().isOk())
                .andExpect(content().contentType("application/json"))
                .andExpect(jsonPath("$.errcode", is(0)))
                .andExpect(jsonPath("$.errmsg", is("")))
                .andExpect(jsonPath("$.content[0].address", startsWith("東京都")))
                .andExpect(jsonPath("$.content[?(@.formal=='国立国会図書館東京本館')]").exists());
    }

    @Test
    void 間違った都道府県を指定した場合にはエラーが返る() throws Exception {
        mvc.noauth.perform(get("/webapi/library/getLibraryList?pref=東a京都"))
                .andExpect(status().isOk())
                .andExpect(content().contentType("application/json"))
                .andExpect(jsonPath("$.errcode", is(-2)))
                .andExpect(jsonPath("$.errmsg", is("都道府県名が正しくありません。")))
                .andExpect(jsonPath("$.content", hasSize(0)));
    }

application/json;charset=UTF-8 の件を変更した後、clean タスク実行 → Rebuild Project 実行 → build タスクを実行すると無事 "BUILD SUCCESSFUL" のメッセージが出力されました。

f:id:ksby:20191231155441p:plain

spring-boot-starter-test が JUnit 5 ベースに切り替わったのですが、Project Tool Window で src/test/groovy/ksbysample、src/test/java/ksbysample でコンテキストメニューを表示して「Run 'Tests in 'ksbysample'' with Coverage」を選択してテストを実行してもテストは全て成功しました。

f:id:ksby:20191231161711p:plain f:id:ksby:20191231162150p:plain

なんかあっさりバージョンアップできました。

メモ書き

履歴

2019/12/31
初版発行。