The Java EE 7 Tutorialの25 Contexts and Dependency Injection for Java EE: Advanced Topicsのセクションを読んでテキトーに訳した。
25 Contexts and Dependency Injection for Java EE: Advanced Topics
このチャプターではContexts and Dependency Injection for Java EE (CDI)のより高度な機能について解説します。とくに、CDIの高度な機能が提供する強い型付けによる疎結合コンポーネントに触れます。
以下のトピックをここで扱います。
- Packaging CDI Applications
- Using Alternatives in CDI Applications
- Using Producer Methods, Producer Fields, and Disposer Methods in CDI Applications
- Using Predefined Beans in CDI Applications
- Using Events in CDI Applications
- Using Interceptors in CDI Applications
- Using Decorators in CDI Applications
- Using Stereotypes in CDI Applications
25.1 Packaging CDI Applications
Java EEアプリケーションをデプロイするとき、CDIはbean archvies内のbeanを探索します。bean archiveは任意のモジュールで、このモジュールにはCDIランタイムがmanageおよび注入が可能なbeanが含まれています。bean archivesには二種類あり、explicit bean archivesとimplicit bean archivesです。
explicit bean archiveはbeans.xml
デプロイメント記述子を含むarchiveで、このファイルは空ファイルが可能で、version number無し、もしくはversion number 1.1でbean-discovery-mode
属性にall
と設定します。たとえば、
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd" version="1.1" bean-discovery-mode="all"> ... </beans>
CDIは、explicit archiveでは@Vetoed
アノテーション付与されたものを除いて、任意のbeanをmanageおよび注入可能です。
implicit bean archiveは、scope typeアノテーションを付与されたbeanを含み、beans.xml
デプロイメント記述子を含まないか、bean-discovery-mode
属性にannotated
を設定したbeans.xml
デプロイメント記述子を含みます。
implicit archiveでは、CDIはscope typeアノテーションを付与されたbeanのみmanageと注入が可能です。
webアプリケーションでは、beans.xml
デプロイメント記述子はもし存在するなら、WEB-INF
ディレクトリに配置します。EJBモジュールやJARファイルでは、beans.xml
デプロイメント記述子はもし存在するなら、META-INF
ディレクトリに配置します。
25.2 Using Alternatives in CDI Applications
あるbeanが異なる目的に使用するために複数バージョンを持つ場合、The simplegreeting CDI Exampleで示すように、あるqualifierか別のqualifierかをデプロイメント時に注入することでbeanを選択することが出来ます。
アプリケーションのソースコードを変更する代わりに、デプロイ時にalternativesを使うことが出来ます。
Alternativesは以下のような目的に良く使用されます。
- 実行時に決定されるクライアント依存のビジネスロジックを扱うため。
- 特定のデプロイメントシナリオを検証するbeanを指定するため(たとえば、国ごとの消費税法が要求する国ごとの消費税ビジネスロジック)。
- テストに使用するbeanのダミー(モック)バージョンを作成するため。
beanをルックアップ、注入、EL解決で使用可能にするには、javax.enterprise.inject.Alternative
アノテーションを付与して、beans.xml
にalternatives
要素を指定します。
例えば、beanの完全なバージョンと、テストのためだけの簡易バージョンを作成したいとします。The encoder Example: Using Alternativesで解説されているサンプルは二つのbeanCoderImpl
とTestCoderImpl
を持っています。テスト用のbeanは以下のアノテーションが付与されています。
@Alternative public class TestCoderImpl implements Coder { ... }
完全なバージョンにはアノテーションはありません。
public class CoderImpl implements Coder { ... }
managed beanはCoder
インターフェースのインスタンスを注入します。
@Inject
Coder coder;
アプリケーションがbeanのalternativeバージョンを使うのは、beans.xml
ファイルで下記のようにバージョンが宣言されている場合だけです。
<beans ...> <alternatives> <class>javaeetutorial.encoder.TestCoderImpl</class> </alternatives> </beans>
beans.xml
のalternatives
要素をコメントアウトするとCoderImpl
クラスが使われます。
同一インタフェースを実装する複数のbeanすべてに@Alternative
アノテーションを付与可能です。この場合、使用するalternative beanを記述したbeans.xml
ファイルを指定しなければなりません。もしCoderImpl
にも@Alternative
アノテーションが付与されている場合、二つのbeanの一つを常にbeans.xml
に指定する必要があります。
beans.xml
ファイルで指定されるalternativesは同一archiveのクラスにのみ適用されます。複数モジュールで構成されるアプリケーションでグローバルなalternativesを指定するには@Priority
アノテーションを使用します。以下がその例です。
@Alternative @Priority(Interceptor.Priority.APPLICATION+10) public class TestCoderImpl implements Coder { ... }
もし@Priority
アノテーションを付与された同一インタフェースを実装する複数のalternative beansがある場合、高プライオリティ値のalternativeが選択されます。@Priority
アノテーションを使用する場合、beans.xml
でalternativeを指定する必要はありません。
25.2.1 Using Specialization
Specializationは、あるbeanを別のbeanで代替できるという点でalternativesと似た機能を持ちます。しかし、すべての場合で他のbeanをオーバーライドしたい場合もあるでしょう。例として下記二つのbeanを定義したとします。
@Default @Asynchronous public class AsynchronousService implements Service { ... }
@Alternative public class MockAsynchronousService extends AsynchronousService { ... }
このとき、beans.xml
でalternativeとしてMockAsynchronousService
を宣言したとすると、以下の注入ポイントはMockAsynchronousService
を解決します。
@Inject Service service;
しかし、以下はMockAsynchronousService
ではなくAsynchronousService
を解決しますが、その理由はMockAsynchronousService
は@Asynchronous
qualifierを持たないからです。
@Inject @Asynchronous Service service;
MockAsynchronousService
が常に注入されることを保証するには、すべてのbean typeとAsynchronousService
のbean qualifiersを実装する必要があります。しかし、もしAsynchronousService
がproducer methodやobserver methodを宣言した場合、この扱いづらいメカニズムは他のbeanが決して呼ばれないことを保証しません*1。Specializationはシンプルなメカニズムを提供します。
Specializationは、実行時および開発時に動作します。もしあるbeanが別のbeanをspecializesすると宣言する場合、そのbeanはその他のbeanクラスを拡張し、実行時にspecialized beanが別のbeanを完全に置き換えます。もし前者のbeanがproducer methodで生成される場合、producer methodもオーバーライドする必要があります。
beanをspecializeするにはjavax.enterprise.inject.Specializes
アノテーションを付与します。たとえば、以下のようにbeanを宣言します。
@Specializes public class MockAsynchronousService extends AsynchronousService { ... }
この場合、MockAsynchronousService
クラスがAsynchronousService
の代わりに常に呼び出されます。
通常、@Specializes
アノテーションが付与されたbeanはalternativeでありbeans.xml
でalternativeとして宣言されます。そうしたbeanはデフォルト実装を置き換えることを意味し、alternative実装は自動的にデフォルト実装の全qualifiersを継承します。もしEL名があればそれも継承します。
25.3 Using Producer Methods, Producer Fields, and Disposer Methods in CDI Applications
producer methodは注入可能なオブジェクトを生成します。一般的に、以下の場合にproducer methodsを使用します。
- そのbean自身ではないオブジェクトを注入するとき。
- 注入されるオブジェクトの実装型が実行時に変化するとき。
- beanのコンストラクタが実行しないカスタム初期化をオブジェクトが要求するとき。
producer methodsの詳細な情報についてはInjecting Objects by Using Producer Methodsを参照してください。
producer fieldはproducer methodのシンプルな別の方法で、オブジェクトを生成するbeanのフィールドです。単純なgetterメソッドの代わりに使用可能です。Producer fieldsは、データソース・JMSリソース・webサービス参照のようなJava EEリソースを宣言するのに役立ちます。
producer methodやfieldにはjavax.enterprise.inject.Produces
アノテーションを付与します。
25.3.1 Using Producer Methods
producer methodによって、開発時とデプロイ時ではなく実行時にbean実装を選択可能になります。たとえば、The producermethods Example: Using a Producer Method to Choose a Bean Implementationでは、managed beanは以下のproducer methodを定義しています。
@Produces @Chosen @RequestScoped public Coder getCoder() { switch (coderType) { case TEST: return new TestCoderImpl(); case SHIFT: return new CoderImpl(); default: return null; } }
getCoder
は実質的にはgetterメソッドで、メソッドと同じアノテーションとqualifierを付与することでcoder
プロパティが注入され、インタフェースの選択したバージョンが使用されます。
@Inject @Chosen @RequestScoped Coder coder;
qualifierの指定は必須で、注入するCoder
をCDIに伝えます。qualifier無しでは、CDI実装はCoderImpl
, TestCoderImpl
, getCoder
が返すいずれかの型から一つを選ぶことが出来ず、曖昧な依存関係をユーザに知らせてデプロイをキャンセルします。
25.3.2 Using Producer Fields to Generate Resources
producer fieldのよくある使い方はJPAのEntityManager
(Chapter 37, "Introduction to the Java Persistence API,"を参照)やJDBCDataSource
などのオブジェクトを生成することです。こうしたオブジェクトはコンテナによってmanagedされます。たとえば、@UserDatabase
qualifierを作成して以下のようにentity manager用のproducer fieldを宣言可能です。
@Produces @UserDatabase @PersistenceContext private EntityManager em;
@UserDatabase
qualifierが使用可能な場所は、別のbeanにオブジェクトを注入するとき、RequestBean
、アプリケーションのどこででもOKです。
@Inject @UserDatabase EntityManager em; ...
The producerfields Example: Using Producer Fields to Generate Resourcesのサンプルではproducer fieldsでentity managerを生成する方法を示しています。似たような@Resource
, @EJB
, @WebServiceRef
オブジェクトを注入するメカニズムも使用可能です。
resource injectionへの依存を最小化するために、アプリケーションの一か所でリソース用のproducer fieldを指定し、それからアプリケーションの必要な場所にオブジェクトを注入してください。
25.3.3 Using a Disposer Method
producer methodやproducer fieldをオブジェクト生成に使用し、必要であれば実行後に削除することが出来ます。そのためには、@Disposes
アノテーションを付与したdisposer methodが必要です。たとえば、以下のようにentity managerをクローズ可能です。
public void close(@Disposes @UserDatabase EntityManager em) { em.close(); }
disposer methodはcontextが終了するときに自動的に呼ばれ(この例の場合、RequestBean
がconversation scopeなのでconversationの終了時)、close
メソッドの引数はproducer fieldが生成したオブジェクトになります。
25.4 Using Predefined Beans in CDI Applications
Java EEは以下のインタフェースを実装する定義済みbeanを提供します。
javax.transaction.UserTransaction
: A Java Transaction API (JTA)ユーザトランザクション。java.security.Principal
: 個人・企業・ログインIDなどの任意のエンティティを表現するプリンシパルの抽象的な概念です。注入されたプリンシパルにアクセスが行われるときは常に、プリンシパルは現在の呼び出し元のアイデンティティを表現しています。たとえば、プリンシパルは初期化時にフィールドに注入されます。その後、注入されたプリンシパルを使用するメソッドは、プリンシパルが注入されたオブジェクト上で呼び出されます。この例では、注入されたプリンシパルはメソッドが実行されるときの現在の呼び出し元のアイデンティティを表現しています*2。javax.validation.Validator
: beanインスタンス用のvalidator。このインタフェースを実装するbeanはdefault bean validation objectValidatorFactory
用のValidator
オブジェクトを注入可能にします。javax.validation.ValidatorFactory
: 初期化されたValidator
インスタンスを返すためのファクトリークラス。このインタフェースを実装するbeanはdefault bean validationValidatorFactory
objectを注入可能にします。javax.servlet.http.HttpServletRequest
: クライアントからのHTTPリクエスト。このインタフェースを実装するbeanによってservletでリクエストの詳細を取得可能になります。javax.servlet.http.HttpSession
: クライアントとサーバ間のHTTPセッション。このインタフェースを実装するbeanによってservletでセッションに関する情報にアクセスし、セッションにオブジェクトをバインド可能になります。javax.servlet.ServletContext
: servletsがサーバブレットコンテナで通信するためのコンテキストオブジェクト。
定義済みbeanを注入するには、リソースにはjavax.annotation.Resource
アノテーション、CDI beanにはjavax.inject.Inject
アノテーションを使用して、beanのインスタンスを取得するための注入ポイントを作成します。bean typeごとに、beanが実装するインタフェースのクラス名を指定します。
Table 25-1 Injection of Predefined Beans
Predefined Bean | Resource or CDI Bean | Injection Example |
---|---|---|
UserTransaction | Resource | @Resource UserTransaction transaction; |
Principal | Resource | @Resource Principal principal; |
Validator | Resource | @Resource Validator validator; |
ValidatorFactory | Resource | @Resource ValidatorFactory factory; |
HttpServletRequest | CDI bean | @Inject HttpServletRequest req; |
HttpSession | CDI bean | @Inject HttpSession session; |
ServletContext | CDI bean | @Inject ServletContext context; |
定義済みbeanは、dependentスコープおよび定義済みデフォルトqualifier@Default
で注入されます。
注入されるリソースの詳細については、Resource Injectionを参照してください。
以下のコード片は定義済みbeanを注入するために@Resource
と@Inject
アノテーションを使用する方法を示しています。このコードはユーザトランザクションとcontextオブジェクトをservletクラスTransactionServlet
に注入します。ユーザトランザクションはjavax.transaction.UserTransaction
インタフェースを実装する定義済みbeanのインスタンスです。contextオブジェクトはjavax.servlet.ServletContext
インタフェースを実装する定義済みbeanのインスタンスです。
import javax.annotation.Resource; import javax.inject.Inject; import javax.servlet.http.HttpServlet; import javax.transaction.UserTransaction; ... public class TransactionServlet extends HttpServlet { @Resource UserTransaction transaction; @Inject ServletContext context; ... }
25.5 Using Events in CDI Applications
イベントにより、コンパイル時の依存関係を必要としないbean間通信が可能になります。あるbeanがイベントを定義し、別のbeanがイベントを発火し、また別のbeanがイベントをハンドリングします。それぞれのbeanは別々のパッケージやアプリケーションの別階層で定義可能です。
25.5.1 Defining Events
イベントは以下の要素で構成されます。
- Javaオブジェクトのイベントオブジェクト。
- ゼロ個以上のevent qualifiers
例として、The billpayment Example: Using Events and InterceptorsサンプルのPaymentEvent
beanは三つのプロパティとそのsetter/getterを持っています。
public String paymentType; public BigDecimal value; public Date datetime; public PaymentEvent() { }
また、このサンプルは二種類のPaymentEvent
を区別するqualifiersを定義しています。すべてのイベントはデフォルトのqualifier @Any
も持ちます。
25.5.2 Using Observer Methods to Handle Events
イベントハンドラはイベントを受け取るためにobserver methodを使用します。
各observer methodは、引数として@Observes
アノテーションと任意のqualifiersを付与された特定のイベントタイプを取ります。observer methodは、もしイベントオブジェクトがイベントタイプにマッチするか、イベントの全qualifiersがobserver methodのevent qualifiersとマッチする場合、イベント通知を受けます。
observer methodはイベント引数に加えて他の引数を取ることが出来ます。追加引数は注入ポイントと宣言可能なqualifiersです。
billpayment
サンプルのイベントハンドラPaymentHandler
は二つのobserver methodsを定義し、PaymentEvent
の各タイプごとに一つのメソッドがあります。
public void creditPayment(@Observes @Credit PaymentEvent event) { ... } public void debitPayment(@Observes @Debit PaymentEvent event) { ... }
また、Observer methodsはconditionalかtransactionalにできます。
- conditional observer methodは、observer methodを定義するbeanインスタンスがすでに現在のコンテキストに存在する場合のみ、イベント通知を受けます。conditional observer methodを宣言するには、
@Observes
の引数にnotifyObserver=IF_EXISTS
を指定します。
@Observes(notifyObserver=IF_EXISTS)
デフォルトのunconditionalにするには、@Observes(notifyObserver=ALWAYS)
を指定します。
- transactional observer methodは、イベントが発火したトランザクションの完了前(before-completion)や完了後(after-completion)フェーズに、イベント通知を受けます。トランザクションが正常か非正常に完了した後にのみ通知が発生するようにも指定できます。transactional observer methodを作成するには、
@Observes
の引数に以下のいずれかを指定します。
@Observes(during=BEFORE_COMPLETION) @Observes(during=AFTER_COMPLETION) @Observes(during=AFTER_SUCCESS) @Observes(during=AFTER_FAILURE)
デフォルトのnontransactionalを使用するには、@Observes(during=IN_PROGRESS)
を指定します。
トランザクション完了前に呼び出されるobserver methodは、トランザクションインスタンスにトランザクションロールバックを強制するために、setRollbackOnly
メソッドを呼び出すことができます。
Observer methodsは例外をスロー可能です。もしtransactional observer methodが例外をスローすると、例外はコンテナによって捕捉されます。もしobserver methodがnontransactionalなら、例外はイベント処理を強制終了させ、他のobserver methodsは一切呼び出されません。
25.5.3 Firing Events
イベントを発火するには、javax.enterprise.event.Event.fire
メソッドを呼びます。このメソッドはイベントを発火して任意のobserver methodsに通知を行います。
billpayment
サンプルでは、managed beanのPaymentBean
はユーザインタフェースから受け取る情報を使用して適切なイベントを発火します。4つのbeanがあり、二つはイベントオブジェクトで、二つはデータ保持用です。managed beanは二つのイベントbeanを注入します。pay
メソッドは、データ保持用オブジェクトを生成するためにnew
を使用し、発火するイベントを選択するためにswitch
を使います。
@Inject @Credit Event<PaymentEvent> creditEvent; @Inject @Debit Event<PaymentEvent> debitEvent; private static final int DEBIT = 1; private static final int CREDIT = 2; private int paymentOption = DEBIT; ... @Logged public String pay() { ... switch (paymentOption) { case DEBIT: PaymentEvent debitPayload = new PaymentEvent(); // populate payload ... debitEvent.fire(debitPayload); break; case CREDIT: PaymentEvent creditPayload = new PaymentEvent(); // populate payload ... creditEvent.fire(creditPayload); break; default: logger.severe("Invalid payment option!"); } ... }
fire
メソッドの引数はデータ保持用クラスのPaymentEvent
です。発火されたイベントは、observer methodsが受け取ります。
25.6 Using Interceptors in CDI Applications
interceptorは、関連付けられたターゲットクラスで発生するライフサイクルイベントもしくはメソッド実行を割り込むために使われるクラスです。インターセプターが実行するタスクは、ログや監査など、アプリケーションのビジネスロジックから切り離され、また、アプリケーション内で繰り返し出現するものです。そうしたタスクはしばしばcross-cuttingタスクと呼ばれます。インターセプターはメンテナンスが容易になるように一箇所でそうしたタスクのコードを指定します。Java EEプラットフォームに初めてインターセプターが導入されたときはEJBを指定していました。Java EE 7プラットフォームにおいては、managed beanを含む全種類のJava EE managed objectをインターセプターに指定可能です。
Java EEのインターセプターについての詳細な情報は、Chapter 54, "Using Java EE Interceptors"を参照してください。
インターセプターのクラスはたいてい@AroundInvoke
アノテーションを付与されたメソッドを含み、インターセプトされるメソッドが実行されるときにインターセプターが実行するタスクを指定します。また、ライフサイクルコールバックインターセプターを指定するために@PostConstruct
, @PreDestroy
, @PrePassivate
, @PostActivate
アノテーションを付与したメソッドを含むことも出来、@AroundTimeout
でEJBタイムアウトインターセプターを指定することも出来ます。インターセプタークラスは一つ以上のインターセプターメソッドを含むことが出来ますが、各タイプに一つだけメソッドを作成しなければなりません。
インターセプターとともに、アプリケーションは一つ以上のinterceptor binding typesを定義し、これはターゲットbeanやメソッドにインターセプターを関連付けるアノテーションです。たとえば、billpayment
サンプルはinterceptor binding type named @Logged
とinterceptor named LoggedInterceptor
があります。interceptor binding typeはqualifier宣言に似た宣言ですが、javax.interceptor.InterceptorBinding
アノテーションを使用します。
@Inherited @InterceptorBinding @Retention(RUNTIME) @Target({METHOD, TYPE}) public @interface Logged { }
また、interceptor bindingにはjava.lang.annotation.Inherited
があり、これはアノテーションがスーパークラスから継承されることを意味します。さらに、@Inherited
アノテーションはカスタムスコープを適用します(このチュートリアルでは扱いません)が、qualifiersは適用しません。
interceptor binding typeは他のinterceptor bindingsを宣言可能です。
インターセプタークラスはinterceptor bindingと同様に@Interceptor
アノテーションを使用します。例は、The billpayment Example: Using Events and Interceptorsを参照してください。
すべての@AroundInvoke
メソッドはjavax.interceptor.InvocationContext
引数を取り、java.lang.Object
を返し、Exception
をスローします。InvocationContext
メソッドを呼び出し可能です。@AroundInvoke
メソッドはproceed
メソッドを呼び出さなければならず、これはターゲットクラスのメソッド呼び出しを行います。
インターセプターとbinding typeを定義すると、beanの全メソッドか特定のメソッドでインターセプターを呼び出すことを指定するために、binding typeをbeanや個々のメソッドにアノテーション付与が可能になります。たとえばbillpayment
サンプルでは、PaymentHandler
beanには@Logged
アノテーションが付与されており、そのクラスの任意のビジネスメソッド呼び出しが、インターセプターの@AroundInvoke
メソッド呼び出しを発生させることを意味しています。
@Logged @SessionScoped public class PaymentHandler implements Serializable {...}
しかし、PaymentBean
beanでは、pay
とreset
メソッドのみが@Logged
アノテーションを付与されており、よってインターセプターはこれらのメソッドが呼び出されたときのみ呼び出されます。
@Logged public String pay() {...} @Logged public void reset() {...}
CDIアプリケーションで呼び出されるインターセプターはbeans.xml
に指定します。たとえば、LoggedInterceptor
クラスは以下のように指定されています。
<interceptors> <class>javaeetutorial.billpayment.interceptors.LoggedInterceptor</class> </interceptors>
もしアプリケーションが一つ以上のインターセプターを使用する場合、インターセプターはbeans.xml
で指定された順に呼び出されます。
beans.xml
で指定するインターセプターは同一アーカイブのクラスにのみ適用されます。複数モジュールで構成されるアプリケーションでインターセプターをグローバルに指定するためには@Priority
を、以下のように使用します。
@Logged @Interceptor @Priority(Interceptor.Priority.APPLICATION) public class LoggedInterceptor implements Serializable { ... }
低プライオリティのインターセプターが最初に呼び出されます。@Priority
アノテーションを使用する場合、beans.xml
にインターセプターを指定する必要はありません。
25.7 Using Decorators in CDI Applications
decoratorは、javax.decorator.Decorator
アノテーションを付与するクラスあるいはbeans.xml
のdecorators
要素です。
デコレータbeanクラスはjavax.decorator.Delegate
アノテーションを付与したデリゲート注入ポイント(delegate injection point)を持つ必要があります。この注入ポイントは、フィールド・コンストラクタ引数・デコレータクラスの初期化メソッド引数に設定可能です。
デコレータは外見的にはインターセプターに似ています。しかし、デコレータはインターセプターによって実行されるタスクと相互補完的なタスク実行を行います。インターセプターはメソッド実行とbeanのライフサイクルに関連付けられたcross-cuttingタスクを実行しますが、任意のビジネスロジックは実行不可能です。一方デコレータはbeanのビジネスメソッドをインターセプトすることでビジネスロジックを実行します。インターセプターがそうであるように、異なる種類のアプリケーションで再利用可能にする代わりに、そのロジックは特定のアプリケーションに固有であることを意味します。
たとえば、encoder
サンプルのalternative TestCoderImpl
クラスを使う代わりに、デコレータを以下のように作成可能です。
@Decorator public abstract class CoderDecorator implements Coder { @Inject @Delegate @Any Coder coder; public String codeString(String s, int tval) { int len = s.length(); return "\"" + s + "\" becomes " + "\"" + coder.codeString(s, tval) + "\", " + len + " characters in length"; } }
このデコレータを使用するサンプルはThe decorators Example: Decorating a Beanを参照してください。
このシンプルなデコレータは、CoderImpl.codeString
が返すエンコード済み文字列ではなく、詳細な出力を返します。より複雑なデコレータは他のビジネスロジックを実行したりデータベースの情報を格納したり出来ます。
デコレータは抽象クラスとして宣言可能で、その理由はインタフェースのすべてのビジネスロジックを実装する必要が無いからです。
CDIアプリケーションでデコレータをインターセプターやalternativeのように呼び出せるようにするには、beans.xml
に定義が必要です。たとえばCoderDecorator
クラスを以下のように定義します。
<decorators> <class>javaeetutorial.decorators.CoderDecorator</class> </decorators>
アプリケーションが一つ以上のデコレータを使用する場合、デコレータはbeans.xml
で指定される順に呼び出されます。
アプリケーションがインターセプターとデコレータを両方とも持つ場合、最初にインターセプターが呼び出されます。つまり、デコレータはインターセプト出来ません。
beans.xml
で指定するデコレータは同一アーカイブのクラスにのみ適用されます。複数モジュールで構成されるアプリケーションでグローバルなデコレータを指定するには@Priority
アノテーションを、以下のサンプルのように使用します。
@Decorator @Priority(Interceptor.Priority.APPLICATION) public abstract class CoderDecorator implements Coder { ... }
低プライオリティのデコレータが最初に呼び出されます。@Priority
アノテーションを使用する場合、beans.xml
でデコレータを指定する必要はありません。
25.8 Using Stereotypes in CDI Applications
stereotypeとは、他のアノテーションを含む、beanに適用されるアノテーションの一種です。stereotypeは、似たような機能を実行する多数のbeanを有する、大規模アプリケーションで特に有用です。stereotypeは以下を指定するアノテーションの一種です。
- デフォルトスコープ。
- ゼロ個以上のinterceptor bindings。
- オプションで、デフォルEL名を保証するための
@Named
アノテーション。 - オプションで、このstereotypeがalternativeだとすべてのbeanに指定するための
@Alternative
アノテーション。
特定のstereotypeアノテーションが付与されたbeanは常に固有のアノテーションを使用するので、多数のbeanに同じアノテーションを適用する必要はありません。
たとえば、stereotypeのAction
をjavax.enterprise.inject.Stereotype
アノテーションを使用して作成したとします。
@RequestScoped @Secure @Transactional @Named @Stereotype @Target(TYPE) @Retention(RUNTIME) public @interface Action {}
@Action
アノテーションを付与されたすべてのbeanはリクエストスコープ、デフォルトEL名、interceptor bindings@Transactional
と@Secure
を持ちます。
また、stereotypeのMock
を作成できます。
@Alternative @Stereotype @Target(TYPE) @Retention(RUNTIME) public @interface Mock {}
このアノテーションが付与されたすべてのbeanはalternativeになります。
同一beanに複数のstereotypeを付与可能で、以下のようなアノテーション付与されたbeanを作成可能です。
@Action @Mock public class MockLoginAction extends LoginAction { ... }
beanごとに異なるスコープを指定するために、stereotypeが指定するスコープをオーバーライドすることも可能です。以下の宣言はMockLoginAction
beanをリクエストスコープではなくセッションスコープに変えています。
@SessionScoped @Action @Mock public class MockLoginAction extends LoginAction { ... }
CDIにはModel
と呼ばれる組み込みstereotypeが利用可能で、これはMVCアプリケーションアーキテクチャのモデル層を定義するbeanで使うことを目的としています。このstereotypeは@Named
と@RequestScoped
の両方を指定しています。
@Named @RequestScoped @Stereotype @Target({TYPE, METHOD, FIELD}) @Retention(RUNTIME) public @interface Model {}
関連リンク
- The Java EE 7 Tutorialのテキトー翻訳まとめ - Qiita - Java EE 7 Tutorialのうち、自分がテキトー翻訳したものの一覧