令和時代に「Spring入門」「Spring徹底入門」を読むとき気をつけるべきN個のこと
この記事について
事ある度に書いたり言ったりしている通り、2020年を迎えようとしている現在でも、信頼できるSpring関連書籍は下記の2冊しかありません。
2冊(以下「書籍」)とも超良書なのですが、どちらもリリースされたのが2016年で、対応しているSpringのバージョンが4.2と古くなっています。
2019年末時点での最新版はSpring 5.2です。この記事では、上記書籍を令和の今読む際、特に気をつけるべき点をいくつか紹介していきます。
4.x->5.xの差分すべてについては、GitHubのWikiを確認してください。
JDKは8以上を使うべし
Spring 5.0以降から、JDKのベースラインが8になりました(Spring 4はJDK 6ベース)。今からSpringを使おうと言う人が、JDK 6とか7を使おうとはしないと思いますが・・・。
Spring 5.2では、JDK 14までサポートされます。JDKとSpringのバージョン対応の詳細はGitHubのWikiを参照してください。
 フィールドインジェクションではなくコンストラクタインジェクション+ @Autowired 省略で書くべし
Spring 4.2以前から、DIの方法は3つありました。
@Component
public class Hoge {
    @Autowired
    Fuga fuga;
}
@Component
public class Hoge {
    private Fuga fuga;
    @Autowired
    public void setFuga(Fuga fuga) {
        this.fuga = fuga;
    }
}
@Component
public class Hoge {
    private final Fuga fuga;
    @Autowired
    public Hoge(Fuga fuga) {
        this.fuga = fuga;
    }
}
書籍でよく使われているのは、フィールドインジェクションです。おそらく、記述量が最も少ないからでしょう(紙面の都合もあるかも)。
Spring 4.3から、クラス内にコンストラクタがただ1つしかない場合は、 @Autowired が省略可能になりました。
@Component
public class Hoge {
    private final Fuga fuga;
    // コンストラクタが1つしか無いので@Autowiredは省略可能!
    public Hoge(Fuga fuga) {
        this.fuga = fuga;
    }
}
クラスをイミュータブルにできるので、可能な限りコンストラクタインジェクションを使いましょう。
 @RequestMapping ではなく @GetMapping などを使うべし
Spring MVCでコントローラークラス・メソッドを書くには @RequestMapping を使います。
@Controller
@RequestMapping("/hoge")
public class HogeController {
    @RequestMapping(value = "/index", method = RequestMethod.GET)
    public String index() {
        return "index";
    }
}
Spring 4.3から、 @GetMapping ・ @PostMapping など各HTTPリクエストメソッドごとのアノテーションが導入されました。とても短く書けていいですね!
@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形式で書くことは可能です。
<!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形式で書くことができます。
<!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 クラスを継承することが多いです。
@Configuration
@EnableWebMvc
public class MvcConfig extends WebMvcConfigurerAdapter {
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        ...
    }
    ...
}
Spring 5.0からこのクラスは非推奨になり、このクラスが実装している WebMvcConfigurer インタフェースを使うことが推奨されています。
@Configuration
@EnableWebMvc
public class MvcConfig implements WebMvcConfigurer {
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        ...
    }
    ...
}
WebMvcConfigurerAdapter クラスは、 WebMvcConfigurer インタフェースを実装して、全メソッドを実装が空の状態でオーバーライドしたクラスです。Java 8ベースになったことで、 WebMvcConfigurer インタフェースの全メソッドが、実装が空のデフォルトメソッドになりました。これにより役目を終えたため、 WebMvcConfigurerAdapter クラスは非推奨になったのです。
 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 対応、メソッド名変更などです。
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();
}
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 でハッシュ化してください。
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    ...
    
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}
Spring Bootの内容はだいぶ変わっているので注意すべし
Spring Boot 1.x->2.xについては、変更点が多すぎて書ききれませんw
パッと思いつくだけでも、
- 各種ライブラリのアップデート
- 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)
- Spring Boot 1.5→2.1バージョンアップを経験して分かったハマりどころ(@kawakawaryuryu)
@b1a9idps さんのブログ一覧
- 決済サービスのSpring Bootのバージョンを2系に上げた
- Flyway 3.x -> 5.xに上げた
- Spring Boot 1.5.xから2.0.xに上げた ~Spring Web編~
- Spring Boot 1.5.xから2.0.xに上げた ~Spring Data編~
- Spring Boot 1.5.xから2.0.xに上げた ~Spring Test編~
2020-10-16追記
Spring Boot 2.3以降では、spring-boot-starter-webにHibernate Validatorが含まれなくなりました(Release Note)。
Hibernate Validatorを使いたい場合は、spring-boot-starter-validationを追加する必要があります。
 (2022-06-16追記) Spring Securityの WebSecurityConfigurerAdapter が非推奨になった
セキュリティ設定の書き方が大幅に変わりました。詳細は👇の記事をご参照ください。
まとめ
思い出したら順次追記していきます。



Discussion