インターセプタ

今回はインターセプタです。

【前提条件】

[環境]

【概要】

JavaEEによって管理されているオブジェクトに対して、
インターセプトすることができます。

作成するクラスは
ステレオタイプアノテーションとインターセプタです。

【サンプルコード】

[ステレオタイプアノテーション]

インターセプトする対象を
明示するためのアノテーションを作成します。

サンプルでは
クラスに対してインターセプトするアノテーション
メソッドに対してインターセプトするアノテーション
二つを作成します。

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