【CDI】インターセプタ（@Interceptor）

#概要

CDI管理Beanのロジック処理における
前後の共通処理部分を切り分けて共通パーツとして扱う仕組みである
＠Interceptorの使い方

#環境

 - IDE : Eclipse 4.7 Oxygen
 - APサーバ : GlassFish 4.1.1
 - JDK 1.8
 - OS : Windows7
 - [プロジェクトの設定](http://qiita.com/nifram/items/123ec5689da654b365aa#eclipse%E3%81%AE%E8%A8%AD%E5%AE%9A)

#対象読者

 - CDI初心者

#インターセプトとは

アプリケーションのロジックには直接関係しない、前処理や後処理部分を切り離し
共通部品として扱えるようにする仕組みのことを言います。

####【　図１．前後処理の共通化　】
![インターセプタ１.png](https://qiita-image-store.s3.amazonaws.com/0/190666/79668394-2590-f5da-5e40-da201f047b74.png)

この切り分けた部分を、必要とするロジック達で使いまわします。

使われる場面としては

 - トランザクション管理
 - メソッドの開始・終了ログ

等が多いそうです。

今回は基本的な記述方法や動きだけを確認していきます。

まずはインターセプタの動きを確認する為のCDI管理Bean

```InterceptorTestBean.java

package bean;

import javax.enterprise.context.ApplicationScoped;
import javax.inject.Named;

import annotation.PracticeInterceptor;

@Named
@ApplicationScoped
public class InterceptorTestBean {

	@PracticeInterceptor
	public void execute() {
		System.out.println("InterceptorTestBean execute() now");
	}
}
```
index.xhtmlのボタンを押下された際
このInterceptorTestBeanの`execute()`メソッドを呼び出します。

そしてこのメソッドが呼び出された際に
共通パーツとして切り分けておいたインターセプタを特定、実行する為に

＠PracticeInterceptor

アノテーションを作成、付与します。

作成の記述は以下

```PracticeInterceptor.java
package annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import javax.interceptor.InterceptorBinding;

@Inherited
@InterceptorBinding
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD,ElementType.TYPE})
public @interface PracticeInterceptor{}
```

次にインターセプタの内容です。

```TraceInterceptor.java
package interceptor;

import javax.annotation.Priority;
import javax.interceptor.AroundInvoke;
import javax.interceptor.Interceptor;
import javax.interceptor.InvocationContext;

import annotation.PracticeInterceptor;

@Interceptor
@Priority(Interceptor.Priority.APPLICATION)
@PracticeInterceptor
public class TraceInterceptor {

	@AroundInvoke
	public Object obj(InvocationContext ic)throws Exception{

		//メソッド実行前
		System.out.println("start in Interceptor");

		//次のインターセプターチェーンの実行
		Object result = ic.proceed();

		//メソッドの実行後
		System.out.println("end in Interceptor");

		return result;
	}
}

```
ここのインターセプタにも先ほど作成した`@PracticeInterceptor`を付与することで、このインターセプタが特定されて実行されます。

最後に、処理を起動する為のViewです。
「インタセプタ確認」ボタンを押下することで処理が走ります。

```index.xhtml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
	xmlns:h="http://xmlns.jcp.org/jsf/html"
	xmlns:jsf="http://xmlns.jcp.org/jsf">
<head>
<title>型解決テスト</title>
</head>
<h:body>
	<h:form id="frm">
		<input jsf:action="#{interceptorTestBean.execute}" type="submit" value="インタセプタ確認" />
	</h:form>
</h:body>
</html>
```
```ruby:実行結果（コンソール画面）
2017-07-18T15:25:40.303+0900|情報: start in Interceptor
2017-07-18T15:25:40.303+0900|情報: InterceptorTestBean execute() now
2017-07-18T15:25:40.303+0900|情報: end in Interceptor
```

##インターセプタに関連してくるアノテーション
初めて見るアノテーションが多かったので
上から順に１つ１つ確認していきます。 

`InterceptorTestBean.java`
 
 - ＠PracticeInterceptor
 
　自分で作成したインターセプタバインディング型のアノテーションです。インターセプタバインディング型については後程記述します。 

`PracticeInterceptor.java`

 - ＠Inherited
 - ＠InterceptorBinding
 - ＠Retention(RetentionPolicy.RUNTIME)
 - ＠Target({ElementType.METHOD,ElementType.TYPE})

　`@Retention`と`@Target`に関しては、[過去記事](http://qiita.com/nifram/items/77eda1b01d7d4a6cf1db#%E9%9D%99%E7%9A%84%E5%9E%8B%E8%A7%A3%E6%B1%BAqualifire)にあるので省略します。

####＠Inherited
　「＠Inheritedが付与されたアノテーション」を付与したクラスが継承された際、子クラスにアノテーションも継承させます。
（メタアノテーション）
※要検証

####＠InterceptorBinding
　インターセプタの胆アノテーション。
このメタアノテーションが付与されたアノテーションをインターセプタ実装クラスとインターセプト対象に付与することで、紐付を行います。
出来上がったユーザ定義アノテーションは読んで字のごとく
**インターセプタバインディング型**
(インターセプタを紐付るよー型)
と呼ばれます。

`TraceInterceptor.java`

 - ＠Interceptor
 - ＠Priority(Interceptor.Priority.APPLICATION)
 - ＠AroundInvoke

####＠Interceptor
　このアノテーションを付与することで、付与されたクラスはインターセプタと認識され、CDI管理Beanとしてコンテナに管理されます。

####＠Priority(Interceptor.Priority.APPLICATION)
　インターセプタの優先順位を示します。
（）内の数値が小さい順に優先されます。以下は用意されている定数です。

 - PLATFORM_BEFORE = 0
 - LIBRARY_BEFORE = 1000
 - APPLICATION = 2000
 - LIBRARY_AFTER = 3000
 - PLATFORM_AFTER = 4000

####＠AroundInvoke
　インターセプト実装メソッドに付与するアノテーションです。
付与されたメソッドは実行時に引数として **InvocationContext**インスタンスを設定します。

InvocationContextが持っているメソッドとして
`proceed()`
があり、このメソッドを呼び出すことでインターセプト対象とした本来のメソッドを実行します。

共通部分を分けた際の
前処理は　`.proceed()` の前に
後処理は　`.proceed()` の後に
記述することになります。

※戻り値はQbject型になります。

###メソッドではなくクラスにインターセプト
先ほどはメソッドをインターセプトの対象に選びました。
今度はクラスをインターセプトの対象に選んでみます。



```InterceptorTestBean.java
package bean;

import javax.enterprise.context.ApplicationScoped;
import javax.inject.Named;

import annotation.PracticeInterceptor;

@Named
@ApplicationScoped
@PracticeInterceptor
public class InterceptorTestBean {


	public void execute1() {
		System.out.println("InterceptorTestBean execute1() now");
	}
	public void execute2() {
		System.out.println("InterceptorTestBean execute2() now");
	}
	public void execute3() {
		System.out.println("InterceptorTestBean execute3() now");
	}
}
```
メソッドから`@PracticeInterceptor`アノテーションを取り除きクラスに付与。
そのうえでメソッドを３つに増やしました。

処理を起動する為のViewもボタンを３つに増やします。

```index.xhtml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
	xmlns:h="http://xmlns.jcp.org/jsf/html"
	xmlns:jsf="http://xmlns.jcp.org/jsf">
<head>
<title>型解決テスト</title>
</head>
<h:body>
	<h:form id="frm">
		<input jsf:action="#{interceptorTestBean.execute1}" type="submit" value="インタセプタ1確認" />
		<input jsf:action="#{interceptorTestBean.execute2}" type="submit" value="インタセプタ2確認" />
		<input jsf:action="#{interceptorTestBean.execute3}" type="submit" value="インタセプタ3確認" />
	</h:form>
</h:body>
</html>
```
![インターセプタView.png](https://qiita-image-store.s3.amazonaws.com/0/190666/a1c9330a-f86c-e6ae-2c1d-19cf07649c8f.png)

左から順にボタンを３つ押した実行結果

```ruby:実行結果（コンソール画面）
2017-07-18T15:30:17.115+0900|情報: start in Interceptor
2017-07-18T15:30:17.115+0900|情報: InterceptorTestBean execute1() now
2017-07-18T15:30:17.115+0900|情報: end in Interceptor
2017-07-18T15:30:19.218+0900|情報: start in Interceptor
2017-07-18T15:30:19.218+0900|情報: InterceptorTestBean execute2() now
2017-07-18T15:30:19.218+0900|情報: end in Interceptor
2017-07-18T15:30:21.507+0900|情報: start in Interceptor
2017-07-18T15:30:21.508+0900|情報: InterceptorTestBean execute3() now
2017-07-18T15:30:21.508+0900|情報: end in Interceptor
```
`InterceptorTestBean`クラスのすべてのメソッドに
インターセプトが適用されている事が確認できました。

###1つのメソッドに複数のインターセプト

先述した`＠Priority`を用いて優先順位付けを行った上で
１つのメソッドに複数インターセプトを適用してみます。

１つ目のインターセプタ（優先度：高）
＠Priorityの数値　`APPLICATION`

```TraceInterceptor1.java
package interceptor;

import javax.annotation.Priority;
import javax.interceptor.AroundInvoke;
import javax.interceptor.Interceptor;
import javax.interceptor.InvocationContext;

import annotation.PracticeInterceptor;

@Interceptor
@Priority(Interceptor.Priority.APPLICATION)
@PracticeInterceptor
public class TraceInterceptor1 {

	@AroundInvoke
	public Object obj(InvocationContext ic)throws Exception{

		//メソッド実行前
		System.out.println("start in Interceptor1");

		//次のインターセプターチェーンの実行
		Object result = ic.proceed();

		//メソッドの実行後
		System.out.println("end in Interceptor1");

		return result;
	}
}
```
２つ目のインターセプタ（優先度：低）
＠Priorityの数値　`APPLICATION + 10`

```TraceInterceptor2.java
package interceptor;

import javax.annotation.Priority;
import javax.interceptor.AroundInvoke;
import javax.interceptor.Interceptor;
import javax.interceptor.InvocationContext;

import annotation.PracticeInterceptor;

@Interceptor
@Priority(Interceptor.Priority.APPLICATION + 10)
@PracticeInterceptor
public class TraceInterceptor2 {

	@AroundInvoke
	public Object obj(InvocationContext ic)throws Exception{

		//メソッド実行前
		System.out.println("start in Interceptor2");

		//次のインターセプターチェーンの実行
		Object result = ic.proceed();

		//メソッドの実行後
		System.out.println("end in Interceptor2");

		return result;
	}
}
```
以上の構成に書き換えて `execute1()` を呼び出し。
（View　左ボタン）

```ruby:実行結果（コンソール画面）
2017-07-18T15:59:49.910+0900|情報: start in Interceptor1
2017-07-18T15:59:49.910+0900|情報: start in Interceptor2
2017-07-18T15:59:49.910+0900|情報: InterceptorTestBean execute1() now
2017-07-18T15:59:49.910+0900|情報: end in Interceptor2
2017-07-18T15:59:49.910+0900|情報: end in Interceptor1
```

このような結果になります。
優先度順で挟み込む形です。

![インターセプタ２.png](https://qiita-image-store.s3.amazonaws.com/0/190666/4c0882c1-fd58-ec01-6268-6a91ebd19e4c.png)


まだログやトランザクションは触ったことがないので実際に実用するとどうなるのかはピンときませんが

どういった動きをしているのかは確認できました。