令和時代に「Spring入門」「Spring徹底入門」を読むとき気をつけるべきN個のこと

# この記事について
事ある度に書いたり言ったりしている通り、2020年を迎えようとしている現在でも、信頼できるSpring関連書籍は下記の2冊しかありません。

- [Spring徹底入門](https://www.shoeisha.co.jp/book/detail/9784798149158)
- [改訂新版Spring入門](https://gihyo.jp/book/2016/978-4-7741-8217-9)

2冊（以下「書籍」）とも超良書なのですが、どちらもリリースされたのが2016年で、対応しているSpringのバージョンが4.2と古くなっています。

2019年末時点での最新版はSpring 5.2です。この記事では、上記書籍を令和の今読む際、特に気をつけるべき点をいくつか紹介していきます。

> 4.x->5.xの差分すべてについては、[GitHubのWiki](https://github.com/spring-projects/spring-framework/wiki/Spring-Framework-Versions)を確認してください。

# JDKは8以上を使うべし
Spring 5.0以降から、JDKのベースラインが8になりました（Spring 4はJDK 6ベース）。今からSpringを使おうと言う人が、JDK 6とか7を使おうとはしないと思いますが・・・。

Spring 5.2では、JDK 14までサポートされます。JDKとSpringのバージョン対応の詳細は[GitHubのWiki](https://github.com/spring-projects/spring-framework/wiki/Spring-Framework-Versions#jdk-version-range)を参照してください。

# フィールドインジェクションではなくコンストラクタインジェクション＋ `@Autowired` 省略で書くべし
Spring 4.2以前から、DIの方法は3つありました。

```java:フィールドインジェクションの例
@Component
public class Hoge {
    @Autowired
    Fuga fuga;
}
```

```java:セッターインジェクションの例
@Component
public class Hoge {
    private Fuga fuga;

    @Autowired
    public void setFuga(Fuga fuga) {
        this.fuga = fuga;
    }
}
```

```java:コンストラクタインジェクションの例
@Component
public class Hoge {
    private final Fuga fuga;

    @Autowired
    public Hoge(Fuga fuga) {
        this.fuga = fuga;
    }
}
```

書籍でよく使われているのは、フィールドインジェクションです。おそらく、記述量が最も少ないからでしょう（紙面の都合もあるかも）。

Spring 4.3から、クラス内にコンストラクタがただ1つしかない場合は、 `@Autowired` が省略可能になりました。

```java:コンストラクタインジェクションの例（4.3以降）
@Component
public class Hoge {
    private final Fuga fuga;

    // コンストラクタが1つしか無いので@Autowiredは省略可能！
    public Hoge(Fuga fuga) {
        this.fuga = fuga;
    }
}
```

クラスをイミュータブルにできるので、可能な限りコンストラクタインジェクションを使いましょう。

# `@RequestMapping` ではなく `@GetMapping` などを使うべし
Spring MVCでコントローラークラス・メソッドを書くには `@RequestMapping` を使います。

```java:4.2以前
@Controller
@RequestMapping("/hoge")
public class HogeController {

    @RequestMapping(value = "/index", method = RequestMethod.GET)
    public String index() {
        return "index";
    }
}
```

Spring 4.3から、 `@GetMapping` ・ `@PostMapping` など各HTTPリクエストメソッドごとのアノテーションが導入されました。とても短く書けていいですね！

```java:4.3以降
@Controller
@RequestMapping("/hoge") // ここは@RequestMappingのままでOK
public class HogeController {

    @GetMapping("/index") // @XxxMappingを使う！
    public String index() {
        return "index";
    }
}
```

# Thymeleaf 3を使いHTML形式で書くべし（not XHTML）
書籍ではThymeleaf 2が使われています。Thymeleaf 2で画面を書くには、XHTML形式で書く必要があります。

> ライブラリを追加すれば、2でもHTML形式で書くことは可能です。

```html:2以前
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8" />
    <title>画面</title>
</head>
<body>
<form action="index.html" th:action="@{findByFirstName}">
    名キーワード：<input type="text" name="firstName" />
    <input type="submit" value="検索" />
</form>
...
```

Thymeleaf 3では、デフォルトでHTML形式で書くことができます。

```html:3以降
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>画面</title>
</head>
<body>
<form action="index.html" th:action="@{findByFirstName}">
    名キーワード：<input type="text" name="firstName">
    <input type="submit" value="検索">
</form>
...
```

`/` を忘れて実行時例外、なんてことが無くなって嬉しいですね！

# `WebMvcConfigurerAdapter` クラスではなく `WebMvcConfigurer` インタフェースを使うべし
Spring 4.3以前では、Spring MVC関連のJava Configを作成する際、 `WebMvcConfigurerAdapter` クラスを継承することが多いです。

```java:4.3以前
@Configuration
@EnableWebMvc
public class MvcConfig extends WebMvcConfigurerAdapter {

    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        ...
    }
    ...
}
```

Spring 5.0からこのクラスは非推奨になり、このクラスが実装している `WebMvcConfigurer` インタフェースを使うことが推奨されています。

```java:5.0以降
@Configuration
@EnableWebMvc
public class MvcConfig implements WebMvcConfigurer {

    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        ...
    }
    ...
}
```

`WebMvcConfigurerAdapter` クラスは、 `WebMvcConfigurer` インタフェースを実装して、全メソッドを実装が空の状態でオーバーライドしたクラスです。Java 8ベースになったことで、 `WebMvcConfigurer` インタフェースの全メソッドが、実装が空のデフォルトメソッドになりました。これにより役目を終えたため、 `WebMvcConfigurerAdapter` クラスは非推奨になったのです。

[`WebMvcConfigurer` のソースコード](https://github.com/spring-projects/spring-framework/blob/master/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurer.java)

# Hibernate Validatorの `@NotBlank` ではなく、Bean Validationの `@NotBlank` を使うべし
Spring 5からJava EE 8対応により、Bean Validationのバージョンが1.xから2.0（Hibernate Validator 6.0以降）に上がりました。

> Spring 5でBean Validation 1.xを使うことも可能ですが、基本的には最新バージョンを使ったほうが良いと思います。

Bean Validation 2.0では、新しい制約アノテーションが追加されました。その一部は、Hibernate Validator独自のアノテーションだったものが、Bean Validation標準に取り入れられたものです。具体的には下記のアノテーションになります（すべて `javax.validation.constraints` パッケージ）。

- `@NotEmpty`
- `@NotBlank`
- `@Email`

これに伴い、Hibernate Validator独自の `@NotBlank` ・ `@NotEmpty` ・ `@Email` （すべて`org.hibernate.validator.constraints` パッケージ）は非推奨になりました。

# Spring Dataの `CrudRepository` に互換性が無いので注意すべし
Spring Data 2.xから、 `CrudRepository` に定義されたメソッドの名前・戻り値などが変更されました。

変更点は `java.util.Optional` 対応、メソッド名変更などです。

```java:1以前のCrudRepository
public interface CrudRepository<T, ID extends Serializable> extends Repository<T, ID> {

	<S extends T> S save(S entity);

	<S extends T> Iterable<S> save(Iterable<S> entities);

	T findOne(ID id);

	boolean exists(ID id);

	Iterable<T> findAll();

	Iterable<T> findAll(Iterable<ID> ids);

	long count();

	void delete(ID id);

	void delete(T entity);

	void delete(Iterable<? extends T> entities);

	void deleteAll();
}
```

```java:2以降のCrudRepository
public interface CrudRepository<T, ID> extends Repository<T, ID> {

	<S extends T> S save(S entity);

	<S extends T> Iterable<S> saveAll(Iterable<S> entities);

	Optional<T> findById(ID id);

	boolean existsById(ID id);

	Iterable<T> findAll();

	Iterable<T> findAllById(Iterable<ID> ids);

	long count();

	void deleteById(ID id);

	void delete(T entity);

	void deleteAll(Iterable<? extends T> entities);

	void deleteAll();
}
```

# Spring Securityの `PasswordEncoder` は必ず明示的に設定すべし
Spring Security 4以前では、 `PasswordEncoder` を明示的に指定しなかった場合、パスワードのハッシュ化が行われませんでした。

Spring Security 5以降では、 `PasswordEncoder` を明示的に指定しなかった場合、 `DelegatingPasswordEncoder` が使われるようになりました。これは、DBなどに保存されているパスワードのプレフィックスを読んで、適切な `PasswordEncoder` に処理を委譲するものです。

<img width="866" alt="スクリーンショット 2019-12-31 11.03.14.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/58650/d1e1471a-46c5-11ca-22e3-93370139848e.png">

本番環境で `PasswordEncoder` を指定していない人はいないと思います（そう信じたい）。しかし、勉強用のコードでは簡略化のために指定しないことはあったかと思います。Spring Security 5.x以降では、必ず指定しましょう。 `PasswordEncoder` をBean定義すればOKです。もちろん、DBなどに保存されているパスワードも同じアルゴリズムの `PasswordEncoder` でハッシュ化してください。

```java:PasswordEncoderの指定
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    ...
    
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}
```

# Spring Bootの内容はだいぶ変わっているので注意すべし
Spring Boot 1.x->2.xについては、変更点が多すぎて書ききれませんｗ

パッと思いつくだけでも、

- 各種ライブラリのアップデート
    - Spring 4.x -> 5.x
    - Spring Data 1.x -> 2.x
    - Spring Security 4.x -> 5.x
    - Thymeleaf 2 -> 3
    - Hibernate -> 5.4
    - Jackson -> 2.10
    - Hibernate Validator 5.x -> 6.1
    - Flyway 4 -> 6
- JDK 8対応
    - thymeleaf-extras-java8time追加済み
    - jackson-datatype-jdk8など追加済み
- セキュリティの簡素化
    - セキュリティ関連のプロパティがほとんど削除された
- Actuatorの改良
    - 内部アーキテクチャの抜本的変更
    - 認証・認可の変更
    - Micrometerの追加
    - プロパティの変更
- プロパティのリネーム
    - 各種プロパティの名前が大幅に変更

・・・などなど。他にもいっぱいあるかも。

## 2020-02-12追記
JSUG勉強会で発表された資料を追加しておきます。どちらも貴重な資料ですので、ぜひ読んでみてください！

### 勉強会スライド
- [決済サービスのSpring Bootのバージョンを2系に上げた話（@b1a9idps）](https://www.slideshare.net/RyosukeUchitate/spring-boot2)
- [Spring Boot 1.5→2.1バージョンアップを経験して分かったハマりどころ（@kawakawaryuryu）](https://speakerdeck.com/kawakawaryuryu/spring-boot-1-dot-5-nil-2-dot-1baziyonatupuwojing-yan-sitefen-katutahamaridokoro)

### @b1a9idps さんのブログ一覧
- [決済サービスのSpring Bootのバージョンを2系に上げた](https://note.com/b1a9idps/n/n0b9ca2ee57a2)
- [Flyway 3.x -> 5.xに上げた](https://www.b1a9idps.com/posts/versionup-flyway-from-3-to-5)
- [Spring Boot 1.5.xから2.0.xに上げた ~Spring Web編~](https://www.b1a9idps.com/posts/versionup-spring-boot-web-1-to-2)
- [Spring Boot 1.5.xから2.0.xに上げた ~Spring Data編~](https://www.b1a9idps.com/posts/versionup-spring-boot-data-1-to-2)
- [Spring Boot 1.5.xから2.0.xに上げた ~Spring Test編~](https://www.b1a9idps.com/posts/versionup-spring-boot-test-1-to-2)

## 2020-10-16追記
Spring Boot 2.3以降では、spring-boot-starter-webにHibernate Validatorが含まれなくなりました（[Release Note](https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.3-Release-Notes#validation-starter-no-longer-included-in-web-starters)）。

Hibernate Validatorを使いたい場合は、spring-boot-starter-validationを追加する必要があります。

# (2022-06-16追記) Spring Securityの `WebSecurityConfigurerAdapter` が非推奨になった

セキュリティ設定の書き方が大幅に変わりました。詳細は👇の記事をご参照ください。

https://qiita.com/suke_masa/items/908805dd45df08ba28d8

#  まとめ
思い出したら順次追記していきます。