令和時代に「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 が省略可能になりました。

コンストラクタインジェクションの例(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>
...

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

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 に処理を委譲するものです。

スクリーンショット 2019-12-31 11.03.14.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の追加
    • プロパティの変更
  • プロパティのリネーム
    • 各種プロパティの名前が大幅に変更

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

まとめ

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

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account