読者です 読者をやめる 読者になる 読者になる

かんがるーさんの日記

最近自分が興味をもったものを調べた時の手順等を書いています。今は Spring Boot をいじっています。

Spring Boot 1.3.x の Web アプリを 1.4.x へバージョンアップする ( その10 )( インジェクションの方法を @Autowired によるフィールドインジェクション → コンストラクタインジェクションへ変更する )

概要

記事一覧はこちらです。

Spring Boot 1.3.x の Web アプリを 1.4.x へバージョンアップする ( その9 )( 1.3系 → 1.4系で実装方法が変更された点を修正する ) の続きです。

  • 今回の手順で確認できるのは以下の内容です。
    • これまでは DI 対象のフィールドに @Autowired アノテーションを付加してインジェクションしていましたが、1.4 系からコンストラクタインジェクションが主流になったと聞いたので、コンストラクタインジェクションに変更します。

参照したサイト・書籍

  1. Spring 4.3 DIコンテナ関連の主な変更点
    http://qiita.com/kazuki43zoo/items/172d132ff8f4ba098888

  2. SpringでField InjectionよりConstructor Injectionが推奨される理由
    http://pppurple.hatenablog.com/entry/2016/12/29/233141

    • なぜコンストラクタインジェクションがよいのか、についてはこちらの記事が分かりやすかったです。
  3. Does JUnit4 testclasses require a public no arg constructor?
    http://stackoverflow.com/questions/1451496/does-junit4-testclasses-require-a-public-no-arg-constructor

目次

  1. コンストラクタインジェクションって面倒なのでは? と思ったが IntelliJ IDEA がきちんと補完してくれる!
  2. コンストラクタインジェクションに変更する
  3. clean タスク → Rebuild Project → build タスクを実行してみる
  4. Mail001Helper, Mail002Helper, Mail003Helper の3クラス全てに同じような修正をしたのに Mail003Helper だけ checkstyle のチェックで警告が出る理由は?
  5. 次回は。。。

手順

コンストラクタインジェクションって面倒なのでは? と思ったが IntelliJ IDEA がきちんと補完してくれる!

コンストラクタインジェクションが主流になったと聞いた時は、フィールドに @Autowired アノテーションを付けるだけと比較して、わざわざコンストラクタに引数を追加して、かつコンストラクタ内でフィールドにセットするのは書くのが面倒そうだなと思ったのですが、さすがは IntelliJ IDEA、サポートは万全でした。

例えばクラス内に final が付いたフィールドを記述すると、以下の画像のようにフィールドの下に赤波線が表示されます。

f:id:ksby:20170301014351p:plain

赤波線が表示されたフィールドにカーソルを移動して Alt+Enter を押すとコンテキストメニューが表示されます。

f:id:ksby:20170301014604p:plain

「Add constructor parameter」を選択するとコンストラクタが自動生成されて、コンストラクタインジェクションの処理が記述されます。

f:id:ksby:20170301020240p:plain

この補完はフィールドの出現順通りにコンストラクタの引数にセットしてくれます。例えば先程記述したフィールドの上に別のフィールドを記述して、

f:id:ksby:20170301021218p:plain

Alt+Enter を押して「Add constructor parameter」を選択すると、

f:id:ksby:20170301021325p:plain

コンストラクタの引数も先程記述された引数の前に追加されます。コンストラクタ内の記述もフィールドと同じ順に記述されます。

f:id:ksby:20170301021445p:plain

Javadoc も自動的に補完してくれます。コンストラクタに Javadoc を記述した後、記述済のフィールドの間に別のフィールドを記述します。

f:id:ksby:20170301021832p:plain

Alt+Enter を押して「Add constructor parameter」を選択すると、

f:id:ksby:20170301022006p:plain

引数やコンストラクタ内の記述の位置を中間にしてくれるだけでなく、Javadoc の記述の位置も中間になります。

f:id:ksby:20170301022109p:plain

また最初にフィールドだけ全て書いて、後からコンストラクタを生成することもできます。まずフィールドだけ書きます。

f:id:ksby:20170301023734p:plain

赤波線にカーソルを移動してから Alt+Enter を押します。コンテキストメニューが表示されますので、「Add constructor parameters」を選択します。複数だと parameter ではなく parameters になるとは。。。

f:id:ksby:20170304002702p:plain

「Choose Fields to Generate Constructor Parameters for」ダイアログが表示されます。デフォルトではフィールドは1つしか選択されていませんので、全て選択してから「OK」ボタンをクリックします。

f:id:ksby:20170304003032p:plain

コンストラクタが生成されます。

f:id:ksby:20170304003146p:plain

Lombok の @RequiredArgsConstructor アノテーションを付ければ final のフィールドだけを引数に持つコンストラクタを自動生成してくれるのでコンストラクタ自体を省略することが可能ですが、自分はソースを見たときの分かりやすさや、インジェクション対象のフィールドが多い時にはそのことに気付いた方がよい(依存関係が多くクラスの作りを見直すべき)という点を考慮すると、コンストラクタは書いた方が良さそうな気がします。

尚、削除する時はフィールドを削除して何かキーを押せばコンストラクタの方も自動で削除してくれるということはありませんでした。1つずつ削除しましょう。

コンストラクタインジェクションに変更する

以下の方法・方針で変更します。

  • 変更は以下の手順で行います。
    • @Autowired アノテーションを削除する。
    • フィールドの宣言に final を追加する。
    • コンストラクタインジェクションを記述する。
  • @Autowired によるフィールドインジェクションの数が多いクラスがあってもコンストラクタインジェクションに変更するだけで、クラスの実装は見直しません。
  • テストクラスは @Autowired によるフィールドインジェクションのままとします。JUnit4 のテストクラスには引数なしのコンストラクタが必ず必要らしいからです ( 実際にコンストラクタインジェクションに修正したら引数なしのコンストラクタがないというエラーメッセージが出ました )。

以下のソースを修正しました。

  • src/main/java/ksbysample/webapp/lending/config/ApplicationConfig.java
  • src/main/java/ksbysample/webapp/lending/config/WebSecurityConfig.java
  • src/main/java/ksbysample/webapp/lending/helper/library/LibraryHelper.java
  • src/main/java/ksbysample/webapp/lending/helper/mail/EmailHelper.java
  • src/main/java/ksbysample/webapp/lending/helper/mail/Mail001Helper.java
  • src/main/java/ksbysample/webapp/lending/helper/mail/Mail002Helper.java
  • src/main/java/ksbysample/webapp/lending/helper/mail/Mail003Helper.java
  • src/main/java/ksbysample/webapp/lending/helper/message/MessagesPropertiesHelper.java
  • src/main/java/ksbysample/webapp/lending/helper/user/UserHelper.java
  • src/main/java/ksbysample/webapp/lending/listener/rabbitmq/InquiringStatusOfBookQueueListener.java
  • src/main/java/ksbysample/webapp/lending/security/AuthenticationFailureBadCredentialsEventListener.java
  • src/main/java/ksbysample/webapp/lending/security/AuthenticationSuccessEventListener.java
  • src/main/java/ksbysample/webapp/lending/security/LendingUserDetailsService.java
  • src/main/java/ksbysample/webapp/lending/service/file/BooklistCsvFileService.java
  • src/main/java/ksbysample/webapp/lending/service/queue/InquiringStatusOfBookQueueService.java
  • src/main/java/ksbysample/webapp/lending/service/UserInfoService.java
  • src/main/java/ksbysample/webapp/lending/web/admin/library/AdminLibraryController.java
  • src/main/java/ksbysample/webapp/lending/web/admin/library/AdminLibraryService.java
  • src/main/java/ksbysample/webapp/lending/web/booklist/BooklistController.java
  • src/main/java/ksbysample/webapp/lending/web/booklist/BooklistService.java
  • src/main/java/ksbysample/webapp/lending/web/booklist/UploadBooklistFormValidator.java
  • src/main/java/ksbysample/webapp/lending/web/confirmresult/ConfirmresultController.java
  • src/main/java/ksbysample/webapp/lending/web/confirmresult/ConfirmresultService.java
  • src/main/java/ksbysample/webapp/lending/web/lendingapp/LendingappController.java
  • src/main/java/ksbysample/webapp/lending/web/lendingapp/LendingappService.java
  • src/main/java/ksbysample/webapp/lending/web/lendingapproval/LendingapprovalController.java
  • src/main/java/ksbysample/webapp/lending/web/lendingapproval/LendingapprovalService.java
  • src/main/java/ksbysample/webapp/lending/web/springmvcmemo/BeanValidationGroupController.java
  • src/main/java/ksbysample/webapp/lending/web/LoginController.java
  • src/main/java/ksbysample/webapp/lending/web/WebappErrorController.java
  • src/main/java/ksbysample/webapp/lending/webapi/library/LibraryController.java
  • src/main/java/ksbysample/webapp/lending/webapi/weather/WeatherController.java

clean タスク → Rebuild Project → build タスクを実行してみる

よく考えたら前回 clean タスク → Rebuild Project → build タスクを実行していませんでしたね。。。と思いつつ、実行してみます。

Error Prone の 2.0.18 がダウンロードされています。

f:id:ksby:20170304005232p:plain

その後で checkstyle の JavadocMethod の警告が出ています。追加したコンストラクタに Javadoc のコメントを付けていないからでしょう。でも警告が出力されたクラスを見ると Mail001Helper, Mail002Helper, Mail003Helper の3クラスに同じような修正をしたにも関わらず Mail003Helper しか出ていないのが気になりました。

f:id:ksby:20170304005629p:plain

あとテストが1つ失敗して “BUILD FAILED” の文字が出力されました。

f:id:ksby:20170304005743p:plain

Mail001Helper, Mail002Helper, Mail003Helper の3クラス全てに同じような修正をしたのに Mail003Helper だけ checkstyle のチェックで警告が出る理由は?

追加したコンストラクタを見ると、Mail001Helper クラスは、

    public Mail001Helper(FreeMarkerUtils freeMarkerUtils
            , JavaMailSender mailSender) {
        this.freeMarkerUtils = freeMarkerUtils;
        this.mailSender = mailSender;
    }

Mail003Helper クラスは、

    public Mail003Helper(FreeMarkerUtils freeMarkerUtils
            , JavaMailSender mailSender
            , ValuesHelper vh) {
        this.freeMarkerUtils = freeMarkerUtils;
        this.mailSender = mailSender;
        this.vh = vh;
    }

google_checks.xml の JavadocMethod の設定を見ると以下のように定義されていました。

        <module name="JavadocMethod">
            <property name="scope" value="public"/>
            <property name="allowMissingParamTags" value="true"/>
            <property name="allowMissingThrowsTags" value="true"/>
            <property name="allowMissingReturnTag" value="true"/>
            <property name="minLineCount" value="2"/>
            <property name="allowedAnnotations" value="Override, Test"/>
            <property name="allowThrowsTagsForSubclasses" value="true"/>
        </module>

おそらく原因は <property name="minLineCount" value="2"/> ですね。コンストラクタ内の行数により警告を出すか否かを判断しているようです。

<property name="minLineCount" value="3"/> に変更すると Mail003Helper クラスも警告されなくなりました。

f:id:ksby:20170304222035p:plain

<property name="minLineCount" value="1"/> に変更すると Mail001Helper, Mail002Helper, Mail003Helper の全てのクラスが警告されます。

f:id:ksby:20170304222346p:plain

さて、どうするかですが、

  • public, protected, private 問わず、メソッドには全てコメントは欲しい気がする。
  • Google Java Style Guide を見てみると、7.3 Where Javadoc is used に “At the minimum, Javadoc is present for every public class” と記載されていた。google_checks.xml<property name="scope" value="public"/> の定義が書かれているのは、このためだろう。これは Google Java Style Guide 通りでいいかな、と思った。
  • setter, getter のようなメソッド内に通常1行しか書かないものを対象外にしたいので <property name="minLineCount" value="2"/> のようなルールを設定しているような気がするが、自分は lombok を使うのでこの点は気にしなくていいはず。
  • そういえば Doma-Gen で自動生成した Entity クラスは lombok を使用せず独自に setter, getter を記述していたはずと思ったが、自動生成された Entity クラスのソースを見ると全てコメントが入っていた。
  • <property name="minLineCount" value="2"/> のルールのままにしておけば、依存関係が多くないコストラクタインジェクションが目的のコンストラクタは Javadoc がなくてもよくなるのか。。。 その方がいいかな、という気がする。

などと考えてみましたが、やっぱり public は全てコメントを付けるルールでいいかなと思ったので <property name="minLineCount" value="2"/> の設定は削除します。

google_checks.xml から <property name="minLineCount" value="2"/> の行を削除した後、警告が出ているメソッドに Javadoc を記述します。

次回は。。。

テストクラスのアノテーションの変更の前に以下2つの作業を行います。

  • Error Prone を 2.0.15 → 2.0.18 へバージョンアップします。
  • clean タスク → Rebuild Project → build タスク実行時に失敗したテストを見直します。

ソースコード

履歴

2017/03/05
初版発行。