はじめに
Spring Boot 1系が2019年8月1日にEOLを迎えるということで、今更ながらSpring Bootのバージョンを2系に更新しました(1.5.2
→2.1.5
)。
そのときにやったことをメモしておきます。
主な構成
- Java 8
- Gradle 4.10.3
- Web、バッチ両方ありの巨大なマルチプロジェクト
- Springアプリケーション5個
- プレーンなJavaアプリケーション1個
- ライブラリ系のモジュール6個
- MySQL
- Flyway
- JOOQ
- Thymeleaf
やったこと
情報収集
↓の公式の移行ガイドに基本的な移行方法や注意点が書かれています。
Spring Boot 2.0 Migration Guide
公式の移行ガイドを読みながら作業し、不明な点や躓いた点はググって解決していきました。
また、メジャーバージョンが変わる主要ライブラリについては、リリースノートを見て破壊的変更がないかチェックしました。
1. まずはspring-boot-properties-migrator
をdependenciesに追加(※移行が終わったら外す)
Spring Boot 2でapplication.properties
/application.yml
で定義するプロパティのキーや設定内容が変わったものがあります。
spring-boot-properties-migrator
をdependenciesに足しておくと、アプリケーション起動時に、設定内容が古いままになっているプロパティがある場合に警告ログを出してくれます。また、一時的に古いプロパティの書き方のままでも動作するようにしてくれます。
dependenciesに↓の行を追加します。(※移行が終わったら外します)
runtime "org.springframework.boot:spring-boot-properties-migrator"
2. GradleのDependency Managementプラグインを追加
1系ではSpring Bootプラグインの中にDependency Managementプラグインが含まれていましたが、2系からはDependency Managementプラグインは別になったようです。
build.gradleにDependency Managementプラグインを追加します。
apply plugin: 'org.springframework.boot'
+ apply plugin: 'io.spring.dependency-management' // <-- コレを追加
3. Spring Bootのバージョンを上げる
etx {
- springBootVersion = '1.5.2.RELEASE'
+ springBootVersion = '2.1.5.RELEASE'
}
Sprin Bootのバージョンを上げると、いろいろのライブラリのバージョンも自動的に上がります。
今回の主な変更
- Spring (4→5)
- Thymeleaf (2→3)
- MySQLのJDBCドライバー (5.1→8.0)
- Flyway (4→5)
- JOOQ (3.9→3.11)
また、RDBのコネクションプールのライブラリがTomcat JDBC
からHikariCP
に変わりました。
※ 各ライブラリの具体的なバージョン番号はspring-boot-dependenciesのPOMファイルに載っています
4. Spring Bootの設定
4.1. Gradleの設定
bootRepackage
というタスクがなくなり、bootJar
という名前のタスクになりました
bootJar {
mainClassName = 'foo.bar.App'
launchScript() // <-- bootRapackageで`executable = true`を設定していた場合、これがその代わりになる
}
4.2. プロパティをバインドするときのキー名を修正
@ConfigurationProperties
や@Value
でSpring BootのプロパティをJavaにバインドしている場合は、プロパティ名の書き方を"Canonical"なものに統一する必要があります。
詳しくはSpring Boot 2.0 Canonical Propertiesを参照してください。
基本的には全部小文字にし、「-」と「_」は削除すればOKでした
- @ConfigurationProperties(prefix = "foo-bar.apiKey")
+ @ConfigurationProperties(prefix = "foobar.apikey")
5. Spring Web MVC
5.1. WebMvcConfigurerAdapter→WebMvcConfigurer
WebMvcConfigurerAdapter
クラスが廃止になり、WebMvcConfigurer
インターフェースに変更されました
- public class WebMvcConfig extends WebMvcConfigurerAdapter {
+ public class WebMvcConfig implements WebMvcConfigurer {
5.2. Controllerで拡張子ありのURLの自動マッピングができなくなった
@GetMapping("/users")
public List<Users> listUsers() {
のようなエンドポイントを用意し、これに対してフロントエンドからGET /users.json
のようなパスでアクセスしていましたが、これができなくなりました。
普通にGET /users
でアクセスすればOKなので、.json
を全部消しました。
6. MySQL
5.1
→8.0
になります
6.1. MySQLのドライバーのクラス名を変更
com.mysql.jdbc.Driver
となっている箇所を探してcom.mysql.cj.jdbc.Driver
に置換しました。
7. JOOQ
3.9
→3.11
になります。
JOOQのバージョンが新しくなったことに伴い、何箇所か修正しました。
The jOOQ Release Note HistoryのBreaking changes
は一通り読んでおくといいと思います。
7.1. JOOQのGradleプラグインのバージョンを上げる
JOOQ3.11.x
以降は、gradle-jooq-plugin3.x
を使う必要があります
plugins {
- id 'nu.studer.jooq' version '2.0.11'
+ id 'nu.studer.jooq' version '3.0.3'
}
7.2. JOOQのコード生成のGradle設定を修正
APIのパッケージ名が変わっていたので修正
mainDb(sourceSets.main) {
jdbc {
...
}
generator {
- name = 'org.jooq.util.DefaultGenerator'
+ name = 'org.jooq.codegen.DefaultGenerator'
...
database {
- name = 'org.jooq.util.mysql.MySQLDatabase'
+ name = 'org.jooq.meta.mysql.MySQLDatabase'
...
7.3. JOOQの生成をし直す
コードを再生成します
7.4. Cursor#fetchOne()が非推奨になったので修正
while (cursor.hasNext())
- Record record = cursor.fetchOne();
+ Record record = cursor.fetchNext();
8. Flyway
※3系から5系に移行する場合は、一旦4系にする必要があるそうです
https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.0-Migration-Guide#flyway
8.1. プロパティ名変更
application.properties / application.ymlのプロパティ名が、flyway.*
→spring.flyway.*
になりました
- flyway:
- enabled: true
+ spring:
+ flyway:
+ enabled: true
8.2. マイグレーション実行履歴管理テーブルの名前を設定
Flyway 3系, 4系ではschema_version
という名前のテーブルでFlywayの実行履歴が管理されていましたが、5系ではflyway_schema_history
という名前に変わっています。
schema_version
を引き続き使用するためにはapplication.properties
/ application.yml
のspring.flyway.table
というプロパティを設定します。
spring:
flyway:
table: schema_version
また、GradleでFlywayプラグインを使用している場合はそちらにも設定しておきます
flyway {
...
table = 'schema_version'
}
9. Thymeleaf
9.1. 依存ライブラリの修正
- implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity4'
+ implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity5' // <--- Spring SecurityのDialectのバージョンアップ
9.2. テンプレートレイアウトの記述方法を修正
Thymeleaf3からレイアウトの記法が変わりました。
参考:
- https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.0-Migration-Guide#template-engines
- https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html#template-layout
例
レイアウト
<!DOCTYPE html>
<html lang="ja"
xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout">
<head>
<meta charset="utf-8"/>
<!-- 「{各ページのタイトル} | アプリ名」のようなtitleになる -->
- <title layout:title-pattern="$CONTENT_TITLE | $DECORATOR_TITLE">アプリ名</title>
+ <title layout:title-pattern="$CONTENT_TITLE | $LAYOUT_TITLE">アプリ名</title>
...
</head>
<body>
<!-- 共通のHTMLを埋め込む -->
- <div layout:replace="common/header::partial"></div>
+ <div layout:replace="~{common/header::partial}"></div>
<!-- 各ページのコンテンツをここに展開 -->
<div layout:fragment="content"></div>
</bod>
</html>
個別ページ
<!DOCTYPE html>
<html
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
xmlns:th="http://www.springframework.org/schema/mvc"
- layout:decorator="layout/default">
+ layout:decorate="~{layout/default}">
<head>
<title>各ページのタイトル</title>
</head>
<body>
<!-- 各ページのコンテンツ -->
<div layout:fragment="content">
Hello World
</div>
</body>
</html>
10. コネクションプール
Tomcat JDBCからHikariCPに変更なったので、設定内容を修正します。
プロパティ名は
spring.datasource.tomcat.*
→ spring.datasource.hikari.*
になります。
HikariCPで設定可能な項目はcom.zaxxer.hikari.HikariConfigに定義されています。
トラブルシューティング
Jar生成時にフロントエンドのビルド内容が含まれなくなってしまった
もともとGradleでフロントエンドのビルドを行うタスクを定義し、jar作成時に実行されるように設定していました。
jar.dependsOn compileFrontend
が、Spring Boot 2系からはbootJarタスクでjarを生成するようになったので↓のように変更しました
bootJar.dependsOn compileFrontend
DBUnitがコケる(MySQL)
org.dbunit.database.AmbiguousTableNameException: ACCOUNTS
複数のデータベース間で同じ名前のテーブルが存在するときにエラーが発生するようです。
JDBCのURLにnullCatalogMeansCurrent=true
というクエリパラメータを追加したら直りました。
(MySQLのJDBCドライバーのデフォルトの挙動が変わったようです)
JOOQでTINYINT(1)
のフィールドに対してSELECTしたときにコケる
java.lang.ClassCastException: java.lang.Boolean cannot be cast to java.lang.Byte
JOOQを利用するときの方針としてBIT(1)
をBoolean、TINYINT(1)
をByteにマッピングしていました。
が、Spring Boot 2にアップデート後、TINYINT(1)
がBooleanにマッピングされるようになってしまいClassCastExceptionが発生していました。
JDBCのURLにtinyInt1isBit=false
というクエリパラメータを追加したら直りました。
MySQLのJDBCドライバーは内部的にTINYINT(1)
をBIT(1)
として扱うらしく、Booleanになってしまったようです。
WEBのドメイン名に「_」が入っている場合に、IllegalArgumentException
java.lang.IllegalArgumentException: The character [_] is never valid in a domain name.
Tomcatのバージョンが上がり、_
を含むドメイン名を受け付けなくなったようです。
(ドメイン名に_
が含まれるのはRFCの仕様としておかしいらしい。)
→サブドメインに_
が入っていたので、サブドメイン名を変更して対応しました。
参考: https://stackoverflow.com/questions/53504857/the-character-is-never-valid-in-a-domain-name