コードの必要なセクションを簡単に書き直すか、EJB 3.0で登場したインターセプターメカニズムを使用できます。
詳細が興味深く、実装の小さな例であれば、猫をお願いします。
インターセプターは、binメソッドの呼び出しをインターセプトして、メソッドの前に特定のアクションを実行します。 同様の概念は、サーブレットのフィルターチェーンに似ています。
簡単な例から説明を始めます。
インターセプターを使用するには、呼び出しをインターセプトするメソッドを使用して、クラス自体を最初に定義します。
public class SimpleLogger{ @AroundInvoke public Object addLog(InvocationContext context){ //- return context.proceed(); }
インターセプトメソッドは@AroundInvokeアノテーションでマークする必要があります。後でそのプロパティを詳細に調べます。
ここで、必要なメソッドの呼び出しをインターセプトするには、目的のインターセプターを示すアノテーションを追加するだけです。 たとえば、次のように:
public class Summ { @Interceptors (SimpleLogger.class) public double getResult(){ // } }
この例では、誰かがSummクラスのgetResultメソッドを呼び出すと、実行される前にSimpleLoggerクラスのaddLogメソッドが最初に実行され、その後getResultメソッドに実行を返します。
まず、インターセプトメソッドを検討します。
@AroundInvokeアノテーションが必要です。インターセプトクラスにこのアノテーションを持つメソッドは1つだけです。
このメソッドには、public、private protected、またはpacket access modifierがありますが、finalまたはstaticとして宣言することはできません
注釈付きメソッドのセマンティクスは、次のパターンを満たしている必要があります。
オブジェクト<メソッド名>(InvocationContext <変数名>)は例外をスローします。
インターセプトメソッド内でアクションを実行した後、context.proceed()を呼び出して、ビジネスメソッドまたはチェーン内の別のインターセプターに実行を返します。 ただし、この呼び出しを完了しない場合、誰にも実行が与えられません。たとえば、データ検証を使用してインターセプターを編成する場合、検証が失敗した場合、実行を継続する必要はありません。
InvocationContextインターフェイスを使用すると、呼び出されたメソッドに関する情報にアクセスできます。
そのコードを検討してください。
public interface InvocationContext { public Object getTarget(); public Method getMethod(); public Object[ ] getParametrs(); public void setParametrs(Object[ ] ); public java.util.Map<String,Object> getContextData(); public Object proceed(); }
- getTargetは、インターセプトされたメソッドが呼び出されたオブジェクトを返します
- getMethodは、インターセプターが呼び出されたBeanのメソッドを返しますが、Beanのライフサイクルがインターセプトされた場合(@PreDestroyメソッドなど)、nullを返します
- getParametrsは、メソッドの入力パラメーターをオブジェクトの配列として返します
- setParametrsを使用すると、実行中にメソッドパラメーターを変更できます。
- getContextDataはインターセプターが相互に対話するために使用できるマップを返します。つまり、1つのメソッドが複数のインターセプターによってインターセプトされた場合、最初のメソッドはこのマップに何らかの値を書き込むことができ、後続のインターセプターは独自の目的でこれらの値を読み取ることができます。
また、インターセプトされたBeanのライフサイクルのコールバックメソッドをインターセプトすることもできます。そのためには、インターセプタークラスに対応するメソッドを追加する必要があります。 たとえば、次のように:
public class SimpleLogger{ @PostConstruct public void init(InvocationContext context){ // , context.getTarget() context.proceed(): } @PreDestroy public void remove(InvocationContext context){ //- context.proceed(): } }
インターセプターのもう1つの特別な使用法は、アクションが1つのアプリケーション内のすべてのクラスに適用される、いわゆる「デフォルトインターセプター」を宣言できることです。 このようなインターセプターは、デプロイメント記述子を介してのみ宣言でき、次のようになります
<interceptor-binding> <ejb-name>*<ejb-name> <interceptor-class> ru.interceptortest.Validate</interceptor-class> </interceptor-binding>
さらに、このインターセプターのアクションをどのクラスにも拡張しないことが必要な場合は、アノテーション@ExcludeDefaultInterceptorsでマークする必要があります。
インターセプターを使用して簡単な例を作成してみましょう。 まず、インターセプタークラスを作成します。
インターセプトされたメソッドが呼び出されたときにサーバーコンソールにメッセージを表示する最も単純なクラスを作成しましょう。
public class SimpleLogger { @AroundInvoke public Object logAction(InvocationContext context) throws Exception { System.out.println("object - " + context.getTarget().getClass()); System.out.println( "method - " + context.getMethod()); return context.proceed(); } }
そして、算術演算を実行する単純なクラスを作成し、アノテーションの@Interceptors(SimpleLogger.class)でメソッドの1つをマークします。
@Stateless public class Count implements Serializable{ double firstArgument=0; double secondArgument=0; // @Interceptors(SimpleLogger.class) public double getSummResult() { return getFirstArgument()+ getSecondArgument(); } }
このクラスのメソッドgetSummResult()を呼び出すと、impleLogger.logActionがサーバーコンソールに出力されます。
object - class ru.interceptorexample.Count method - public double ru.interceptorexample.Count.getSummResult()
別のインターセプタークラスを追加します。
public class LifeLogger { @AroundInvoke public Object logAll(InvocationContext context) throws Exception { System.out.println("LogAll object - " + context.getTarget()); System.out.println("LogAll method - " + context.getMethod()); return context.proceed(); } @PostConstruct public Object logCreate(InvocationContext context) throws Exception { System.out.println("create - " + context.getTarget().getClass().getClass); return context.proceed(); } }
そして、Countクラスに別のメソッドを追加します。
public double getMultiplyResult(){ return getFirstArgument() * getSecondArgument(); }
さらに、アノテーション@Interceptors(LifeLogger.class)でクラス全体をマークします。
現在、getMultiplyResultメソッドが呼び出されると、最初にCountクラスのインスタンスを作成するときに、LifeLoggerインターセプターがサーバーコンソールに出力します。
create - class ru.interceptorexample.Count
そして:
LogAll object - class ru.interceptorexample.Count LogAll method - public double ru.interceptorexample.Count.getMultiplyResult()
また、getSummResultメソッドを呼び出すと、LifeLoggerインターセプターが最初に機能し、次にSimpleLoggerがサーバーコンソールで機能します。
LogAll object - class ru.interceptorexample.Count LogAll method - public double ru.interceptorexample.Count.getSummResult() object - class ru.interceptorexample.Count method - public double ru.interceptorexample.Count.getSummResult()
明らかに、実際のタスクでは、コンソールからの出力を、たとえばlog4jを使用して通常のロギングに置き換えることをお勧めしますが、これらの例がインターセプターメカニズムの仕組みを理解するのに十分であることを願っています。
このメカニズムの操作について詳しく知りたい人のための情報源:
1. oracle.comのマニュアル
2.動作中のEJB 3-デブパンダ、レザラーマン、デレクレーンISBN:1-933988-34-7
希望する人は、githubから小さなシンプルなWebアプリケーションの形式でソースコードを取得できます(プロジェクトはnetbeansとglassfishの下で行われましたが、別の環境に転送する必要がある場合は難しくないと思います)。 github.com/tsegorah/JavaInterceptorsExample.git