Spring Boot 2.1系から2.2系にバージョンアップした話

HOT PEPPER Beauty でバックエンドを担当している奥冨です。 先月 HOT PEPPER Beauty に美容クリニックのカウンセリング予約ができるサービス(以降 美容クリニック)が追加されました。 美容クリニックでは Web フレームワークとして Spring Boot を採用しており、 本記事では開発中に行った Spring Boot のバージョンアップについて Spring Boot の変更点、そしてプロジェクトでどう対応したのか解説します。

今回は Spring Boot 2.1.1.RELEASE から 2.2.1.RELEASE へバージョンアップした際の一例を紹介できればと思っております。

また、先に公開した 美容クリニック紹介記事にて、アーキテクチャ及び開発体制について紹介させて頂いております。よろしければ、そちらもご参照ください。
「HOT PEPPER Beauty」美容クリニックをリリースしました

バージョンアップ対応

ざっくり以下の手順で対応を進めていきました。
今回のバージョンアップはリリース直前ということもあり、その際の問題も合わせて記載していきます。

  1. Spring Boot 2.2 Release Notesを読んで影響調査
  2. Spring Boot バージョンを 2.2 系に変更
  3. エラーを解消する
    • ビルドエラーを解消する
    • 挙動が変わった箇所を対応する

ver2.1.1 から 2.2.1 への変更点

Spring Boot 2.2 Release Notesに記載されている変更点を確認します。 以下に大きな変更点をまとめます。

項目 変更点
Upgrading to Version 5.2 Jackson の必須バージョンが 2.9.7+になりました
@Configration に proxyBeanMethod オプションが追加されました (default = true)
Spring が返す ContentsType が変更になりました(“application/json;charset=UTF-8” → “application/json”)
Deprecations from Spring Boot 2.1 2.2 系の時点で削除されたクラス、アノテーションなど
@WebMvcTest の secure オプションが廃止されました
直接のリストはないので Spring Boot 2.1 の非推奨リスト から確認
JMX now disabled by default 2.2 系から JMX は default でオフになりました
※有効化する場合は spring.jmx.enabled = true
JUnit 5 spring-boot-starter-test が default で JUnit5 を提供するようになりました
Gradle requirements Gradle の必須バージョンが 4.10+になりました
HttpHiddenMethodFilter disabled by default HttpHiddenMethodFilterが default で無効化になりました
Java 13 support Java13 サポートされるようになりました
Lazy initialization 遅延初期化が使えるようになりました default はオフ
起動時間が早くなる代わりに、最初のリクエスト処理が遅くなる、起動時のエラーが後々発生するようになります

Spring Boot 自体のバージョンアップ

美容クリニックでは Spring Boot が提供する BOM を利用しており、個別のライブラリのバージョンは記載しておりません。
parent タグ内のバージョンを2.1.1.RELEASEから2.2.1.RELEASEに上げることによって対応完了です。
BOM にバージョンを依存することによって、FW やサードパーティライブラリのバージョン依存関係を管理する必要がなくなりますので大変便利です。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<project xmlns="http://maven.apache.org/POM/4.0.0"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.2.1.RELEASE</version>
    <relativePath/>
  </parent>
  <dependencies>
    <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-web -->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-thymeleaf -->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-thymeleaf</artifactId>
    </dependency>
  </dependencies>

ビルドエラー対応

実際に発生したビルドエラーとその対応について書きます。

@WebMvcTest の secure オプションが廃止された

@WebMvcTest は Controller のユニットテストを実装する時に使用するアノテーションです。 2.1 系までは、認証を行うか secure オプションによってオンオフ出来たんですが、2.2 系からオプションが廃止され認証必須となりました。

secureオプションを使っていた場合は以下のようなエラー文言が出ます。

1
Groovyc: 'secure'is not part of the annotation org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest -> org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest in @org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest

認証回避できないので、@WithMockUserという認証 Mock 用のアノテーションを使用して、認証処理を行います
※美容クリニックでは要件上、ID/PASS によってユニットテスト内容が変わらないため独自アノテーションを使用しています

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
@WithSecurityContext(
        factory = MockCustomerUserSecurityContextFactory.class
)
public @interface WithMockCustomerUser {
    String userId() default "123456789";
    String nickname() default "ニックネーム";
}
1
2
3
4
5
6
7
8
@WebMvcTest(controllers = MemberController.class)
@WithMockCustomerUser // 使用例
class MemberControllerTest extends AbstractControllerTest {
    @InjectMocks
    MemberController memberController
}

PageRequest のコンストラクタが変更された

PageRequest はページングを行う為の、ページ数やソート方法を格納している Spring Data が提供するクラスです。 2.2 系からコンストラクタが変更され、PageRequest(int,int)から(int page, int size, Sort sort)になりました。 以前と同じ処理にするには、PageRequest.of(int,int)を使用する必要があります。

1
2
3
4
// ver 2.1.1
public PageRequest(int page, int size) { this(page, size, Sort.unsorted()); }
// ver 2.2.1
public static PageRequest of(int page, int size) { return of(page, size, Sort.unsorted());  }

挙動が変わった箇所を対応する

Content-Type の対応

2.2 系から JSON をレスポンスとして返す際の Content-Type が変更されました。
application/json;charset=UTF-8application/json
Content-Type が変わることにより、古いブラウザから見た時に文字化けする可能性がありました。 リリースも近くリテストの範囲が広かったため、今回は差分が出ないように以前の Content-Type を使用するように対応しました。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
 * Spring Boot 2.2からContentTypeにCharsetが付与されなくなった為、意図的に付与する
 */
@Configuration
@AutoConfigureBefore(HttpMessageConvertersAutoConfiguration.class)
public class JacksonMessageConverterConfiguration {
    @Bean
    public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter(ObjectMapper objectMapper) {
        MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter(objectMapper);
        // MappingJackson2HttpMessageConverterのdefaultのMediaTypeを一部改変する
        // APPLICATION_JSON -> APPLICATION_JSON_UTF8
        converter.setSupportedMediaTypes(Lists.newArrayList(MediaType.APPLICATION_JSON_UTF8,
                                                            new MediaType("application", "*+json")));
        return converter;
    }
}

HiddenHttpMethodFilter を有効化

ブラウザからの POSTリクエストを PUTDELETE リクエストとして処理出来るように、Method を書き換えるための Filter です。
2.1 系 では default で有効化されていましたが、2.2 系 からは無効化されていたので以下の設定で再度有効化しました。

1
2
3
4
5
6
spring:
  mvc:
    # HiddenMethodFilterを有効化する
    hiddenmethod:
      filter:
        enabled: true

さいごに

今回は開発中に行った Spring Boot のバージョンアップを紹介いたしました。 フレームワークのバージョンアップって、どこに変更があって、どんな影響があるのか難しいですよね。 自分が話した内容が一つでも他プロダクトの参考になればと思っております。

これはほんの一例ですが、美容クリニックでは、さまざまな技術チャレンジやドメイン設計に関われる環境があります。興味がある方がいらっしゃいましたら、ぜひ採用ページを御覧ください。