インターセプタ
今回はインターセプタです。
【前提条件】
[環境]
- JDK 1.7.0_07
- Glassfish 3.1.2.2
- PostgreSQL 9.1(JDBC:postgresql-9.1-901.jdbc4.jar)
【サンプルコード】
[ステレオタイプアノテーション]
インターセプトする対象を
明示するためのアノテーションを作成します。
サンプルでは
クラスに対してインターセプトするアノテーション、
メソッドに対してインターセプトするアノテーションの
二つを作成します。
package jp.glory.common.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import javax.interceptor.InterceptorBinding; @InterceptorBinding @Target({ ElementType.TYPE }) @Retention(RetentionPolicy.RUNTIME) public @interface SampleClassAround { }
package jp.glory.common.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import javax.interceptor.InterceptorBinding; @InterceptorBinding @Target({ ElementType.TYPE, ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) public @interface SampleMethodAround { }
インターセプトするためのアノテーションには
下記のアノテーションをつける必要があります。
■javax.interceptor.InterceptorBindingアノテーション
■java.lang.annotation.Retentionアノテーション
■java.lang.annotation.ElementTypeアノテーション
RetentionアノテーションにRUNTIMEのみが指定できます。
Targetアノテーションには
TYPEのみ、TYPEとMETHODの指定、
どちらかのみ定義できます。
[インターセプタ]
サンプルでは
クラスに対するインターセプタ、
メソッドに対するインターセプタ
を作成します。
インターセプタは処理の前後で
標準出力を行います。
[個別のインターセプタ]
package jp.glory.common.interceptor; import javax.interceptor.AroundInvoke; import javax.interceptor.Interceptor; import javax.interceptor.InvocationContext; import jp.glory.common.annotation.SampleClassAround; @Interceptor @SampleClassAround public class SampleClassInterceptor { @AroundInvoke public Object invokeAround(final InvocationContext context) throws Exception { InterceptorCommon common = new InterceptorCommon(context, "class"); return common.invokeAround(); } }
package jp.glory.common.interceptor; import javax.interceptor.AroundInvoke; import javax.interceptor.Interceptor; import javax.interceptor.InvocationContext; import jp.glory.common.annotation.SampleMethodAround; @Interceptor @SampleMethodAround public class SampleMethodInterceptor { @AroundInvoke public Object invokeAround(final InvocationContext context) throws Exception { InterceptorCommon common = new InterceptorCommon(context, "method"); return common.invokeAround(); } }
インターセプタには
javax.interceptor.Interceptorアノテーションをつけます。
インターセプトするステレオタイプアノテーションもつけます。
インターセプトしたときに実行するメソッドには
javax.interceptor.AroundInvokeアノテーションをつけます。
パラメータはInvocationContext型で、戻り値はObject型です。
InterceptorCommonクラスは
サンプル用に独自に作成した共通処理用クラスです。
[共通処理クラス]
サンプルでは共通クラス内で
標準出力する処理を実装しています。
package jp.glory.common.interceptor; import javax.interceptor.InvocationContext; public class InterceptorCommon { private final InvocationContext context; private final String invokeType; public InterceptorCommon(final InvocationContext context, final String invokeType) { this.context = context; this.invokeType = invokeType; } public Object invokeAround() throws Exception { final String prefix = createPrefix(); System.out.println(prefix + invokeType + " " + "before"); final Object returnObj = context.proceed(); System.out.println(prefix + invokeType + " " + "after"); return returnObj; } private String createPrefix() { final StringBuilder builder = new StringBuilder(); builder.append(context.getTarget().getClass().getSimpleName()); builder.append("#"); builder.append(context.getMethod().getName()); builder.append(":"); return builder.toString(); } }
InvocationContext#proceedメソッドで
インターセプト先のメソッドが実行されます。
InvocationContext#getTargetメソッドで
インターセプト先のクラスが取得できます。
InvocationContext#getMethodメソッドで
実行したメソッドにMethodオブジェクトが取得できます。
[ターゲット]
サンプルではインターセプトする
対象のクラスはManagedBeanです。
package jp.glory.ui.impl; import javax.enterprise.context.RequestScoped; import javax.inject.Inject; import javax.inject.Named; import jp.glory.application.SampleInterceptorApplication; import jp.glory.common.annotation.SampleClassAround; import jp.glory.common.annotation.SampleMethodAround; @Named(value = "sampleIC") @RequestScoped @SampleClassAround public class SampleInterceptorPage { @Inject private SampleInterceptorApplication application = null; @SampleMethodAround public void executeCommand() { application.execute(); } }
[beans.xml]
作成したインターセプタを有効にするためには
beans.xmlに設定を追加します。
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://docs.jboss.org/cdi/beans_1_0.xsd"> <interceptors> <class>jp.glory.common.interceptor.SampleClassInterceptor</class> <class>jp.glory.common.interceptor.SampleMethodInterceptor</class> </interceptors> </beans>
Interceptorsタグ内に実行するインターセプタを指定します。
[その他クラス]
ManaedBeanから実行されるクラスです。
呼び出されるだけで何もしません。
package jp.glory.application; public interface SampleInterceptorApplication { void execute(); }
package jp.glory.application.impl; import jp.glory.application.SampleInterceptorApplication; public class SampleInterceptorApplicationImpl implements SampleInterceptorApplication { @Override public void execute() { } }
[ページ]
<?xml version="1.0" encoding="UTF-8" ?> <!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:f="http://java.sun.com/jsf/core" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:h="http://java.sun.com/jsf/html"> <f:view> <h:form id="interceptorForm"> <h:commandButton action="#{sampleIC.executeCommand()}" value="テスト" /> </h:form> </f:view> </html>
【実行】
ページからManagedBeanが実行された時のログです。
情報: SampleInterceptorPage$Proxy$_$$_WeldSubclass#executeCommand:class before 情報: SampleInterceptorPage$Proxy$_$$_WeldSubclass#executeCommand:method before 情報: SampleInterceptorPage$Proxy$_$$_WeldSubclass#executeCommand:method after 情報: SampleInterceptorPage$Proxy$_$$_WeldSubclass#executeCommand:class after