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

日々常々

ふつうのプログラマがあたりまえにしたいこと。

DIコンテナのインジェクション方法の使い分けについて

DIコンテナを使う時にどのインジェクションを使うかって話です。 たぶん誰かがどこかで同じようなことを書いているだろうけれど、気にせず書くよ。 「他の誰かが書いている」なんてのを書かない理由にしてると何も書けなくなるし。

コンテナ
DIコンテナのこと。
コンテナ管理
インスタンスのライフサイクルをコンテナが管理していること。雑に言えば、使う側で new しないってこと。
インジェクション
Dependency Injectionのこと。

Short Answer

コンストラクタインジェクションを使いましょう。使い分けなくていいです。

3種類のインジェクション

インジェクションには3種類ありますね。他あっても知らない。

  • フィールドインジェクション
  • セッターインジェクション
  • コンストラクタインジェクション

フィールドインジェクション

一番よく見るかな。

class Hoge {

    @Inject
    Piyo piyo;
}

この状態でHogeをコンテナにインスタンス生成してもらうと、piyoフィールドにコンテナ管理されているPiyoインスタンスがインジェクションされる。 記述量が少なくて済むのは嬉しいですね。

このインスタンスをコンテナ外で生成して使おうとすると、piyoフィールドに何かしらの方法でインスタンスを設定してあげなきゃいけない。 何かしらの方法ってのは十中八九リフレクションですね。フィールドをpublicにする手もあるけれど、あまりしないかな。 コンテナでライフサイクルを管理したいオブジェクトは、プロダクトコードではコンテナ外で使いません(やっちゃいけない)が、テストコードではありえます。

問題点は、(リフレクションを使わない場合)テストでコンテナを使わないといけなくなります。 コンテナを使うと重くなります。 コンテナの重さはキャッシュやコンテナの高速化などで対応できなくはないです。 とはいえ、テストの重さがコンテナの起動時間に支配されている開発現場において、適切な対応がされることは稀です。 対応せずに放置すると、単純に時間がかかるのでテストの実行機会が減ります。とても残念ですね。

もう一つ問題があって、このオブジェクトがコンテナに依存することです。 せっかくDIコンテナを使うことで依存関係を逆転させたのに、DIコンテナを使わないと適切にインスタンス生成できない物体になっています。 なんというか、本末転倒感があります。 個人的にはこちらの理由で使いたくない。

セッターインジェクション

黎明期によく見たけど、最近はあまり見ないような。

class Hoge {

    Piyo piyo;

    @Inject
    void setPiyo(Piyo piyo) {
        this.piyo = piyo;
    }
}

この状態でHogeをコンテナにインスタンス生成してもらうと、setPiyo(Piyo)メソッドがコンテナ管理されているPiyoインスタンスを引数として呼び出される。 前述のリフレクション云々よりは挙動がわかりやすいですね。

コンテナ外で使う時も、自分でインスタンス生成してからセッターを呼び出せばいいだけです。

問題点は、セッターなこと。 って言うと趣味が入ってるように捉えられるかもですが、セッターを持つってことは、状態遷移があるってことなんです。 状態遷移があるオブジェクトは不安定です。組み合わせるともっと不安定になります。 疑心暗鬼になりながら使いたくないです。

なんだかんだで、コンテナ管理するインスタンスってコンテナ内ではシングルトンなことが多いです。 スコープを省略した場合のデフォルトがシングルトンなことからもわかるかと思います。 これはこれでどうなって思うのですが、まあ現状はそうです。 そんなインスタンスの状態が不安定とか言われると、私は落ち着いて開発できない。

私の心理的な安全のためにセッターインジェクションは使わないでほしいです。 フィールドインジェクションは「リフレクションを使う時点で危険なのをわかってやってる」と自分に言い聞かせることができるので、この点は許容できます。

コンストラクタインジェクション

なぜかあまり見ない。

class Hoge {

  final Piyo piyo;

  @Inject
  public Hoge(Piyo piyo) {
    this.piyo = piyo;
  }
}

この状態でHogeをコンテナにインスタンス生成してもらうと、コンテナ管理されているPiyoインスタンスがコンストラクタの引数として呼び出される。 インジェクションタイミングがわかりやすいのがいいですね。

フィールドインジェクションと比較して「インジェクションする数が増えると積み重なってすごく冗長になる」と言うのを聞きます。 でも、それだけ多くのものに依存している状態がそもそもおかしいと断言できます。

インスタンス生成時点で状態が安定するのが実に良い。 フィールドにfinalつけれるのが嬉しいです。 フィールドもローカル変数もデフォルトfinalにならないかなぁと常々思うけど、これは別の話。

<脱線> フィールドインジェクションはデフォルトコンストラクタなどでインスタンスを生成してから、フィールドにリフレクションで設定します。セッターインジェクションも同じです。 そのため、インスタンス生成直後に値が入ってなかったりします。 その間隙をついてNullPointerExceptionが起こったりすることも無いとは言えません。 実際起こることはあまりないのですが、疑いたくなる気持ちをわかってください。 </脱線>

また、インスタンス生成で必要なものがコンストラクタで明示されています。 テストなどのコンテナの外でインスタンス生成する際も特に制約はありません。

と言うことで、問題点は特に挙げられません。

なお、Springでもコンストラクタインジェクションは@Autowiredとか書かなくてもインジェクションしてくれるようになってたりします。 この辺もコンストラクタインジェクションの追い風。

なぜ複数のインジェクション方法があるのか

コンテナを作る側の都合かなーと。知らんけど。 使う側の事情でこの3種類のインジェクションを使い分けたことは今までなくて、多分今後もない。

インスタンスを生成するためにはコンストラクタを呼ぶ必要があります。 「コンテナ管理するにはデフォルトコンストラクタが必要」なんてのを見聞きしたことはあるでしょう。 これはインスタンス生成時にClass#newInstance()を使ってるから…なんて理由ではないと思いますが、ともかく単にインスタンス生成するだけならデフォルトコンストラクタが楽です。 コンテナに限らず、インスタンスを生成するライブラリがデフォルトコンストラクタを要求するのは珍しい話ではありません。 そう言えばClass#newInstance()がJDK9でDeprecatedになりますね。関係ないですけど。

話を戻して。コンテナを作る側の都合ではフィールドインジェクションが楽です。 コンテナ管理対象のインスタンスを一通り生成してから必要なインスタンスをインジェクションしていけばいいだけだからです。

コンストラクタインジェクションでは、コンストラクタが要求するインスタンスを先に生成する必要があります。 インジェクション対象のインスタンスがなければコンストラクタを呼び出せないですから。 こうなってくると、インスタンスの生成順を考慮しないといけなくなるわけで、コンテナが多段構成になっていたり、定義の上書きがあったりするととても面倒です。 依存関係の循環なども問題になってきます。

セッターインジェクションでも生成順は問題になってきます。 なぜならインジェクションで呼び出すのはメソッドなので、メソッドで引数のオブジェクトのメソッドが呼び出されるかもしれません。 「セッターではメソッド呼び出しをしないでください」とか言うわけにもいかないので、インジェクションするオブジェクトは完成している必要があります。

そんなこんなで、フィールドインジェクションが作る側の都合は楽っぽい。 その次にインスタンス生成がデフォルトコンストラクタでいいセッターインジェクションが楽で、一番面倒なのがコンストラクタインジェクションだったり。

まぁ、使う側にとっては知ったこっちゃないです。 使う側は安定した完成したインスタンスが手に入ればいいんで。

で、結論

コンストラクタインジェクションを使いましょう。

ことしのふりかえり

2016年12月31日です。 最近の日付表記のマイブームだと 2016-12-31T22:11 です。 大晦日ですね。

さて、今年を振り返ってみよう。 来年になったら忘れる、と言うかもう忘年会してるから忘れちゃってるんですが、なんとか引っ張り出して。

今年もなんだかんだで色々ありました。

対外的なこと

対外的な活動だとこんな感じですかね。

  • WEB+DB PRESSで「Javaの新定石」の連載
  • 2月関ジャバで「よくある業務開発の自動化事情」の再演
  • JJUG CCC 2016 Spring で「テスト自動化のまわりみち」の登壇
  • JJUG CCC 2016 Fall で「どうしようJUnit 5」の登壇
  • 大阪Jenkins勉強会 でのLT
  • 合同勉強会 in 大都会岡山 -2016 Winter- でのLT

お仕事のこと

お仕事では相変わらずJavaなプロジェクトでよくわからない立ち位置でいました。 プログラマなんですけどね。リーダー補助とか、なんだかんだと。

あと、今年の最初には全く予想もしてなかったのですが、なんだかんだで仕事がなくなりました。 なんの準備もしてないので1月から無職。自分でもびっくり。

今年の目標

今年の始まった頃は「xxxxやるぞー」とか思ってたりもしたのですが、 できたものもあれば、できなかったものもあり。

そういえば今月書いてたこの目標。

今年の目標を「1ヶ月に1エントリ以上のペースでブログを書く」にします。

Qiitaに1つ書いたので、それで12ってことで!

来年の抱負

らいねんかんがえる。

ご挨拶

みなさま、今年も一年お世話になりました。 来年もなんとかよろしくお願いいたします。

多くの人は忘れてるCoinの一つ

Java SE 7がリリースされたのは2011年7月。 さらに、パブリックアップデートが2015年4月で終了している子です。 5年も経てば一定の評価はできるでしょう。

さて、以下のコードをコンパイルしたらどうなるか。

void method() {
    try {
        throw new Exception();
    } catch(Exception e) {
        throw e;
    }
}

では次のコードは。

void method() {
    try {
        throw new RuntimeException();
    } catch(Exception e) {
        throw e;
    }
}

うん。

前者はコンパイルエラーで、後者は普通にコンパイルできます。

Project Coinの "Multi-catch and more precise rethrow" ですね。マルチキャッチの影に隠れてるから余計に目立たない。 Project Coinって言ったり、こうして書いてあることから思い当たったりする程度かと思います。 知らなかったり忘れてても問題ないものでしょう。

この機能、一度も使わなかったなぁ……。多分今後も使わない。

発端

このツイートで思い出した。で、こんなリプしたので

その「例外」の話、です。

#jjug_ccc 2016 FallでJUnit5の話をしてきた

今年の目標を「1ヶ月に1エントリ以上のペースでブログを書く」にします。 これで9エントリになるので後3ですね。2012年12月は1ヶ月で34エントリ書いてるので、今の私と彼が同一人物なら余裕なはず。

さて、2016-12-03のJJUG CCC 2016 Fallで話させてもらいました。 タイトルはどうしようJUnit 5です。

JJUG CCCについて

参加は6回目だと思ってたけど、7回目だった。どんどんパワフルになっている良いイベント。いい機会をいただけています。ありがとうございます。

資料

準備段階における言い訳をしてみる

1週間前からここ数年無い程度の体調不良に襲われ、滅多に仕事を休んだりしないのですが、2日間お休みしてしまったりしていました。当日は発表前に咳止めを飲んでたり。 あと、11/30にJUnit5.0.0-M3がリリースされて、いろいろと修正が必要になってたりもしてます。junit-jupiter-migration-supportとかM2にはなかったし。まあ準備できてないのはそれだけが理由じゃ無いんだけど。

そんなわけで、お聞き苦しいところもあったかと思いますが、恥じる内容では無いつもりです。

登壇のふりかえり

時間は過不足なく残り30秒程度で終えたはず。時間配分はなんとかなった。 エモ系セッションが多い私ですが、今回はテク系でやろうと思ってやりました。

感想。技術セッションは難しいなーと改めて思いました。

登壇前は「機能の単純な説明はしない。ユーザーガイドを読んでもらった方がいい。」と思ってたのですが、聞いて頂いている方々の反応を見て、機能説明の比重を増やして話しました。合ってたかどうかは知らない。

しかし長丁場のイベントで、単に知識を入れるようなセッションは負荷も大きいんだろうなと。 時間帯や私の話し方(飲み物忘れたのと風邪とで声が出づらかった・・・・・)もあったと思いますが、前回よりお疲れな方が多いように感じました。人数が多かったのもあって、室温も少し高かったのかな。

あと、デモは入れた方がよかったです。目先も変わるし。

今回は時間配分的に入れられなかったけど。

結局JUnit5はどうなんよ?

発表駆動で触っただけですが、私は気に入りました。聞いた方がどうとらえたかは知りません。 リリースされたら即使うと思います。楽しみです。

蛇足: JUnit5開発にあたっては寄付金が募集されたことがあって、キャンペーンページに名前載せてもらってたりする。

宣伝

スライドの中で触れてるAssertJの記事はWEB+DB PRESS Vol.95で書いてます!!

WEB+DB PRESS Vol.95

WEB+DB PRESS Vol.95

JaCoCoとMavenと何か

JaCoCoMavenカバレッジを取ろうってやつ。

  • Java: 1.8.0_102
  • Maven: 3.3.9
  • JaCoCo: 0.7.6.201602180812

いろいろ地味に古いけど気にしない。

普通の手順

設定

丁寧な解説ありますけど、もっと雑なの。

pom.xmlproject/build/pluginsに入れるだけ。

<plugin>
  <groupId>org.jacoco</groupId>
  <artifactId>jacoco-maven-plugin</artifactId>
  <version>0.7.6.201602180812</version>
</plugin>

別に入れなくてもいいけど、このくらいは書いた方が楽。

実行

mvn clean jacoco:prepare-agent test

面倒ならexecutionに書く。手順は公式の丁寧な解説を参照。

結果を見る

HTMLで手元で見たいなら jacoco:report を実行する。 そしたら target/site/jacoco/index.xml ができる。

Jenkinsにはjacoco.exeを直接見てもらえば、jacoco:reportは不要。

うまくいかない

testフェーズを実行してもjacoco.exeができなくて、jacoco:reportしてたらこんなのが出てたりするとき。

[INFO] --- jacoco-maven-plugin:0.7.6.201602180812:report (default-report) @ xxxxxxxxxxxxxx ---
[INFO] Skipping JaCoCo execution due to missing execution data file:/xxxxxxxxxxxxxx/target/jacoco.exec

たぶんmaven-surefire-pluginになんか設定入ってる。

<plugin>
  <artifactId>maven-surefire-plugin</artifactId>
  <version>2.19.1</version>
  <configuration>
    <argLine>-Xms128m</argLine>
  </configuration>
</plugin>

こっちで書いてると、jacoco:prepare-agentで設定したargLineが上書きされてしまう。 なので@{argLine}で参照しておく。

<plugin>
  <artifactId>maven-surefire-plugin</artifactId>
  <version>2.19.1</version>
  <configuration>
    <argLine>@{argLine} -Xms128m</argLine>
  </configuration>
</plugin>

だいたいはjacoco:prepare-agentのドキュメントとかに書いてたりするのだけれどね。

「Javaの新定石」でJava EEについて書きました(WEB+DB PRESS vol.94)

WEB+DB PRESS Vol.94

WEB+DB PRESS Vol.94

テーマは最近何かと話題の多いJava EEです。 かんたんなWebアプリをかんたんに作って、そこで使った技術要素を8ページで紹介する試み。 ギリギリまでSpring BootにするかJava EEにするか迷いました。

メインはJAX-RSCDIJPAです。当然ほかの仕様*1も使っていますが、UberJarで使っているPayara Micro以外はJava EE仕様の範囲でやっています。 アプリ自体はシンプルなものなので、ハンズオン的にできなくはないものだとも思っています。 コードのダウンロードもできますので、そちらもどうぞ。

Java EEと言えば、パーフェクト Java EEも出たところですね。 連載で「Java EEもいけるんじゃない?」と思ったら、手を出してみてもいいのではないでしょうか。(わたしもこれからよみます。)

パーフェクト Java EE

パーフェクト Java EE

*1:たとえばJSPServletJTAなど

WEB+DB PRESS vol.93のJavaの新定石は"マルチスレッド/GCとの付き合い方"です。

2016-06-24に発売してました。1ヶ月経ってるので、だいたい読んでくれてると信じてます。まだの方は是非。

WEB+DB PRESS Vol.93

WEB+DB PRESS Vol.93

前回も書いた通り、この連載は「昔からJava使ってるけど、最近の変化とかいちいち追うのめんどい」って方が対象です。今回のマルチスレッド関連はThreadから通して駆け抜けてみたり。書きながら書いたコードは私のGitHubの砂場に転がってたりします。

マルチスレッドとGCについては、Javaエンジニア養成読本の第5章ですこし触れてます。あちらは40ページ中の合わせて1ページでしたが、今回は8ページ中の8ページ。だからどうした。

Javaエンジニア養成読本からは1年半経ってますが、この程度の期間でそれほどJavaのこの辺は変わってませんし、多少増えた私の知識もたいして影響ない程度です。と言うことで、ある意味では書いてる内容やオチに差はないです。見比べたらわかるかも。

Javaエンジニア養成読本 [現場で役立つ最新知識、満載!] (Software Design plus)

Javaエンジニア養成読本 [現場で役立つ最新知識、満載!] (Software Design plus)

ちなみに「Javaの新定石」はうらがみさんと一緒に書かせて貰ってます。何気に一番勉強になってるのは私ではなかろうかね、と思ったり。