😎

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

2024/08/10に公開

この記事について

事ある度に書いたり言ったりしている通り、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 が省略可能になりました。

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

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

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

@RequestMapping ではなく @GetMapping などを使うべし

Spring MVCでコントローラークラス・メソッドを書くには @RequestMapping を使います。

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リクエストメソッドごとのアノテーションが導入されました。とても短く書けていいですね!

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形式で書くことは可能です。

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形式で書くことができます。

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 クラスを継承することが多いです。

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

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

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

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

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

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

WebMvcConfigurer のソースコード

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 対応、メソッド名変更などです。

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();
}
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 でハッシュ化してください。

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勉強会で発表された資料を追加しておきます。どちらも貴重な資料ですので、ぜひ読んでみてください!

勉強会スライド

@b1a9idps さんのブログ一覧

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 が非推奨になった

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

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

まとめ

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

Discussion