Spring Boot 2.0からSpring Boot 2.2にマイグレーションしたときの変更点

# はじめに

Spring Boot 2.0.2のアプリケーション（約7KL、SPAのサーバ部分）をSpring Boot 2.2.5にマイグレーションしてみたときの修正ポイントを書いておきます。

# Spring Bootの修正箇所

## Maven

JUnit5を使用していたのですが、Spring Boot 2.0ではspring-boot-starter-parentの管理下ではなかったjunit-platform-launcherとmockito-junit-jupiterが、Spring Boot 2.2では管理下になったため、バージョンの指定が不要となりました。

```xml:SpringBoot2.0
<dependency>
  <groupId>org.junit.platform</groupId>
  <artifactId>junit-platform-launcher</artifactId>
  <version>1.3.1</version>
  <scope>test</scope>
</dependency>
<dependency>
  <groupId>org.mockito</groupId>
  <artifactId>mockito-junit-jupiter</artifactId>
  <version>2.22.0</version>
  <scope>test</scope>
</dependency>
```

```xml:SpringBoot2.2
<dependency>
  <groupId>org.junit.platform</groupId>
  <artifactId>junit-platform-launcher</artifactId>
  <scope>test</scope>
</dependency>
<dependency>
  <groupId>org.mockito</groupId>
  <artifactId>mockito-junit-jupiter</artifactId>
  <scope>test</scope>
</dependency>
```

## Beanの上書き設定

Spring Boot 2.0では、同じBeanの定義があったときに、 `@Primary` を付与したBeanが優先されましたが、Spring Boot 2.2では初期化時にBeanDefinitionOverrideException例外が発生します。

```
Caused by: org.springframework.beans.factory.support.BeanDefinitionOverrideException: Invalid bean definition with name 'taskExecutor' defined in mypackage.TestConfiguration: Cannot register bean definition [Root bean: class [null]; scope=; abstract=false; lazyInit=null; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=true; factoryBeanName=testConfiguration; factoryMethodName=taskExecutor; initMethodName=null; destroyMethodName=(inferred); defined in mypackage.TestConfiguration] for bean 'taskExecutor': There is already [Root bean: class [null]; scope=; abstract=false; lazyInit=null; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=mainConfig; factoryMethodName=taskExecutor; initMethodName=null; destroyMethodName=(inferred); defined in class path resource [mypackage/MainConfig.class]] bound.
```

これを回避するには、application.ymlに `spring.main.allow-bean-definition-overriding` プロパティを設定してください。

```yaml:SpringBoot2.0
spring:
  main:
    allow-bean-definition-overriding: true
```

## RestTemplateBuilderの設定値の型変更

RestTemplateBuilderを用いてRestTemplateの設定を行う際、タイムアウトの設定がミリ秒指定から `java.time.Duration` 指定に変更となり、単位がわかりやすくなりました。

```java:SpringBoot1.5
new RestTemplateBuilder()
    .setConnectTimeout(5000)
    .setReadTimeout(60000);
```

```java:SpringBoot2.0
new RestTemplateBuilder()
    .setConnectTimeout(Duration.ofMillis(5000))
    .setReadTimeout(Duration.ofMillis(60000));
```

## Sortオブジェクトの生成方法変更

Spring Dataの `org.springframework.data.domain.Sort` オブジェクトの生成方法が、 `new` から `by` に変わりました。

```java:SpringBoot1.5
new Sort(Sort.Direction.fromString(sortDirection), sortColumn);
```

```java:SpringBoot2.0
Sort.by(Sort.Direction.fromString(sortDirection), sortColumn);
```

securityパッケージ配下のさらにservletパッケージの中に移動したようです。

# その他

これは元から実装が悪かったのですが、JPAのリポジトリインタフェースのメソッドに

```java
List<Auth> findByRoleContains(Collection<Role> roles);
```

という定義があり、使っていませんでした。
Spring Boot 2.2に変更してアプリを起動すると、

```
Caused by: java.lang.IllegalStateException: Operator CONTAINING on role requires a scalar argument, found interface java.util.Collection in method public abstract java.util.List mypackage.repository.AuthRepository.findByRoleContains(java.util.Collection).
```

の例外が発生しました。

Containsは文字列を含む検索であり、本来はContainsではなくInを使うべき箇所なのですが。なぜ今まで問題なかったのか。。（この使っていなかったから気づかなかった）

```java
List<Auth> findByRoleIn(Collection<Role> roles);
```

# まとめ

[Spring Boot 1.5から2.0にバージョンアップしたとき](https://qiita.com/phonypianist/items/8eb0dc9a51a9f21f941e)に比べればかなり楽でしたが、それでもちょくちょく細かな破壊的修正が加わっているため、注意が必要です。