ひこぽんのーと

覚書と雑記です。

JavaEE 7をやってみよう。 CDI その2

その1のつづき。

その1に書いたサンプルは、
インターフェイスに対して実装クラスが1つしかなかったが、
複数の実装を持つインターフェイスはどうインジェクションされるのだろう。
Springだとコンフィグに書いた記憶もあるが、詳しくは忘れた。
ここではJavaEEの作法を覚えるので、まぁ、いい。

JavaEEでは限定子というものを作り、
それを対象に貼り付けることでインジェクション対象を明確にする。

書いていてよくわからないが、読んでいてもわかりづらい。
ソースを書こう。

まず、限定子を作る。
新規ウィザードからCDI(Context and Dependency Injection)グループの中の
Qualifier Annotationを選び、

f:id:nagamitsu1976:20150801150810p:plain

パッケージ名と限定子名を決め、完了を押すと自動生成される。

f:id:nagamitsu1976:20150801150911p:plain

・QRichText限定子
限定子についてるアノテーションについては、
@Target…型、メソッド、パラメタ、フィールドの優先順序でクラスを決める、
@Retention…VM起動中の間
ってな意味ですかね。
詳しくはドキュメントを読む必要があるけれど、今はこのままで。
package text.qualifier;

import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

import javax.inject.Qualifier;

import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

@Qualifier
@Target({ TYPE, METHOD, PARAMETER, FIELD })
@Retention(RUNTIME)
@Documented
public @interface QRichText {

}

続いて、その1のサンプルに出したITextインターフェイスの実装をもう1つ作る。

・RichTextクラス
getTextメソッドの処理は、SimpleTextクラスとは実装を変えます。
テスト結果がわかりやすいように。
そして重要なのが、クラスに限定子を付加すること。
package text.impl;

import javax.enterprise.context.RequestScoped;

import text.qualifier.QText;

@RequestScoped
@QRichText
public class RichText extends SimpleText {
    private static final String DECORATOR = "-";
    
    @Override
    public String getText() {
        String deco = "";
        String text = super.getText();
        StringBuffer sb = new StringBuffer();
        if (text != null) {
            for (int i = 0; i < text.getBytes().length + 2; i++) {
                deco += DECORATOR;
            }
            sb.append(deco);
            sb.append("<BR />");
            sb.append(DECORATOR);
            sb.append(text);
            sb.append(DECORATOR);
            sb.append("<BR />");
            sb.append(deco);
            sb.append("<BR />");
        }
        return sb.toString();
    }
}

でもって、Managed Beanも拡張します。

・DisplayTextクラス
SimpleTextクラスとRichTextクラスをDIできるようにITextのメンバを増やします。
そして、一方には、先ほど作った@QRichText限定子を付加します。
これによって、メンバとクラスの関係付けが行われます。目印を付けたってことですか。
『ここには@QRichTextの印をつけたクラスをいれよ』てなことですね。
package manage;

import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;
import javax.inject.Named;

import text.IText;
import text.qualifier.QRichText;

@Named(value="display")
@RequestScoped
public class DisplayText {
    
    @Inject
    private IText iText;
    
    @Inject
    @QRichText
    private IText iText2;
    
    private String inputText1;
    
    /**
     * inputText1を取得します。
     * @return inputText1
     */
    public String getInputText1() {
        return inputText1;
    }

    /**
     * inputText1を設定します。
     * @param inputText1 inputText1
     */
    public void setInputText1(String inputText1) {
        this.inputText1 = inputText1;
    }
    
    public String getText1() {
        iText.setText(inputText1);
        return iText.getText();
    }

    public String getText2() {
        iText2.setText(inputText1);
        return iText2.getText();
    }
}

さて、テスト用のJSFを書き換えましょう。

・di.xhtml
docoText1には、SimpleTextクラスの結果が、
decoText2には、RichTextクラスの結果が出るはずです。
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:ui="http://java.sun.com/jsf/facelets">
<h:head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <h:outputStylesheet library="css" name="default.css" />
    <title>DIサンプル</title>
</h:head>
<h:body>
    <h:form id="form1">
        <h:outputLabel value="文字を入力するのだ。" /><br />
        <h:inputSecret id="inputText1" value="#{display.inputText1}">
            <f:ajax execute="form1" />
        </h:inputSecret>
        <hr/>
        <h:outputLabel value="ここに入力した文字がでるぞ。" />
        <br />
        <h:outputText id="decoText1" value="#{display.text1}" escape="false" />
        <hr/>
        <h:outputLabel value="ここにも入力した文字がでるぞ。" />
        <br />
        <h:outputText id="decoText2" value="#{display.text2}" escape="false" />
    </h:form>
</h:body>
</html>

実行すると、こんな感じです。
f:id:nagamitsu1976:20150801152357p:plain

・結局のところ…
インターフェイスに対して実装クラスが増えると、
関連付けを明確にするため、限定子を作成してこれを用いなければならない。
限定子はアノテーションなので、ソースが増えると。
手間はあまりかわらないが、コンフィグへの記載が不要で、ソース上で関連がわかるのが便利なのか。