Spring AOP ポイントカット指定子の書き方について

# 概要

AOPのポイントカット指定子の書き方のまとめ記事になります。
なお、この記事ではAOPの詳細、Aspectの実装やアドバイスについては扱いません。別途リンクしている参考記事をご覧ください。

**環境**

* Windows10 Professional
* Java 1.8.0_131
* Spring Boot 1.5.3
    * Spring AOP 4.3.8

**参考**

* [Aspect Oriented Programming with Spring] (http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#aop)
* [[spring]7.2 @AspectJ support] (http://d.hatena.ne.jp/minokuba/20110302/1299075764)
* [SpringでAOP] (http://qiita.com/NagaokaKenichi/items/386af61b6866d60964e8#target)
* [Java Method Logging with AOP and Annotations] (http://www.yegor256.com/2014/06/01/aop-aspectj-java-method-logging.html)


## PointCut式

### 指定子

Spring AOPがサポートするAspectJのポイントカット指定子(pointcut designators)の種類。

* execution
* within
* this
* target
* args
* @&#x200b;target
* @&#x200b;args
* @&#x200b;within
* @&#x200b;annotation
* bean (Spring AOPの追加ポイントカット指定子)


### execution

> for matching method execution join points, this is the primary pointcut designator you will use when working with Spring AOP

#### 書式

```java:format
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern) throws-pattern?)
```

|書式部                  | パターン      |備考    |
|:----------------------|:-------------|:-------|
|modifiers-pattern      |修飾子         |省略化  |
|ret-type-pattern       |戻り値の型     |        |
|declaring-type-pattern |宣言の型       |省略化   |
|name-pattern           |メソッド名     |        |
|param-pattern          |パラメータの型 |        |
|throws-pattern         |例外の型       |省略化  |


```java:例
@Around("execution(public String com.example.*..*ServiceImpl.find*(String,Long,Long) throws Exception)")
                   ^^^^^^ ^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^
                   |      |      |                           |    |                  |
                   |      |      |                           |    |                  +--- throws-pattern
                   |      |      |                           |    +--- param-pattern
                   |      |      |                           +--- name-pattern
                   |      |      +--- declaring-type-pattern
                   |      +--- ret-type-pattern
                   +--- modifiers-pattern
```

この条件は下記のクラスのfindOneメソッドにマッチします。

```java:例
package com.example.service.impl;

@Service
public class EvianServiceImpl implements EvianService {

  //...省略

  public String findOne(String title, Long id, Long price) throws Exception {
    //...省略
  }

}
```

##### modifiers-pattern

修飾子を指定します。publicは省略可能です。
ただしSpring AOPではpublicメソッドのみサポートするので実質、明示的に指定する必要はありません。

```java:例_(省略した記述)
@Around("execution(String *..*ServiceImpl.find*(String,Long,Long) throws Exception)")
```

> Due to the proxy-based nature of Spring’s AOP framework, calls within the target object are by definition not intercepted.
> For JDK proxies, only public interface method calls on the proxy can be intercepted. With CGLIB, public and protected method calls on the proxy will be intercepted, and even package-visible methods if necessary. However, common interactions through proxies should always be designed through public signatures.


##### ret-type-pattern

戻り値のパターンを指定します。型を明示しなくてもいい場合は"*"で記述することができます。

```java:例_(戻り値を*で記述)
@Around("execution(* *..*ServiceImpl.find*(String,Long,Long) throws Exception)")
```

##### declaring-type-pattern

パッケージ名、クラス名のパターンを指定します。記述の通り部分一致で指定することができます。
省略可能です。

```java:例_(省略した記述)
@Around("execution(public String find*(..) throws Exception)")
```

##### name-pattern

メソッド名のパターンを指定します。既述の通り部分一致で指定することができます。

```java:例_(メソッド名を部分一致で記述)
find*(..)
```

もちろん、省略しない記述もできます。

```java:例_(省略しない記述)
findOne(..)
```


##### param-pattern

パラメータのパターンを指定します。何通りかの記述方法がありますが特に明示する必要がなければ".."と記述することができます。

```java:例
@Around("execution(* *..*ServiceImpl.find*(..) throws Exception)")
```

部分的に指定する場合。1番目の引数はStringで2番目以降の型は任意（引数がなくてもよい）の場合。

```java:例
find*(String,..)
```

引数の数が決まっているが、型は決めない場合はワイルドカードで記述します。

```java:例
find*(*,*,*)
```

引数を取らない場合。

```java:例
find*()
```


##### throws-pattern

例外のパターンを指定します。名前の部分一致で指定することもできます。

```java:例
@Around("execution(* *..*ServiceImpl.find*(..) throws *Exception)")
```

省略可能です。

```java:例
@Around("execution(* *..*ServiceImpl.find*(..))")
```


### within

> limits matching to join points within certain types (simply the execution of a method declared within a matching type when using Spring AOP)

FQCNで指定するクラスのメソッドに適用します。

```java:OK
@Around("within(com.example.service.impl.WeatherServiceImpl)")
```

指定するパッケージ下のクラスのメソッドに適用します。

```java:OK
@Around("within(com.example.service.impl.*)")
```

指定するクラスのメソッドに適用します。

```java:OK
@Around("within(*..WeatherServiceImpl)")
```

インターフェースは指定できません。

```java:NG
@Around("within(com.example.service.WeatherService)")
```

```java
public interface WeatherService {
  //...省略
}
```

#### バインディング

withinはバインディングはできません。


### @&#x200b;within

> limits matching to join points within types that have the given annotation (the execution of methods declared in types with the given annotation when using Spring AOP)

> '@within' can also be used in a binding form :- see the following section on advice for how to make the annotation object available in the advice body.

指定するアノテーションを付与したクラスのメソッドに適用します。

```java:OK
@Around("@within(com.example.annotation.AopWithin)")
```

```java:例
@AopWithin
@Service
public class WeatherServiceImpl implements WeatherService {
  //...省略
}
```

#### バインディング

アノテーションをアドバイザにバインドすることができます。

```java:OK
@Around("@within(aopWithin)")
public Object w4(ProceedingJoinPoint pjp, AopWithin aopWithin) throws Throwable {
  //...省略
}
```


### target

> limits matching to join points (the execution of methods when using Spring AOP) where the target object (application object being proxied) is an instance of the given type

> 'target' is more commonly used in a binding form :- see the following section on advice for how to make the target object available in the advice body.


指定するインターフェースまたは抽象クラスの実装クラスのメソッドに適用します。

```java:OK
@Around("target(com.example.service.AbstractService)")
```

#### バインディング

インターフェースをアドバイザにバインドすることができます。

```java:OK
@Around("target(service)")
public Object t2(ProceedingJoinPoint pjp, AbstractService service) throws Throwable {
  //...省略
}
```

データソースもインターフェースなので下記のようにデータソースのメソッドにも適用できます。

```java:例
@Around("target(datasource)")
public Object b1(ProceedingJoinPoint pjp, DataSource datasource) throws Throwable {
  //...省略
}
```


### @&#x200b;target

> limits matching to join points (the execution of methods when using Spring AOP) where the class of the executing object has an annotation of the given type

> '@target' can also be used in a binding form :- see the following section on advice for how to make the annotation object available in the advice body.

不明です。
調べたのですがよくわかりませんでした。


### args

> 'args' is more commonly used in a binding form :- see the following section on advice for how to make the method arguments available in the advice body.

指定する引数を取るメソッドに適用する。
args単体で設定すると実行時エラーになったのでexecutionと併せて対象を絞っています。

この例ではWeatherFormというクラスを引数に取るメソッドに適用しています。

```java:OK
@Around("execution(* com.example.*..*.*(..)) && args(com.example.form.WeatherForm)")
```

argsは指定する引数の並びが重要です。
WeatherFormの他にも引数を取る場合は、その引数を明示するか、または".."を付ける必要があります。

```java:例
@Around("execution(* com.example.*..*.*(..)) && args(com.example.form.WeatherForm,..)")
```

#### バインディング

引数をアドバイザにバインドすることができます。

```java:OK
@Around("execution(* com.example.*..*.*(..)) && args(form,..)")
public Object a2(ProceedingJoinPoint pjp, WeatherForm form) throws Throwable {
  //...省略
}
```

### @&#x200b;args

> '@args' can also be used in a binding form :- see the following section on advice for how to make the annotation object(s) available in the advice body.

指定するアノテーションを付与したクラスのオブジェクトを引数に取るメソッドに適用します。

```java:OK
@Around("execution(* com.example.*..*.*(..)) && @args(com.example.annotation.AopTest)")
```

```java:例
@AopTest
public class WeatherForm implements Serializable {
  //...省略
}
```

下記のようにメソッドの引数にアノテーションを指定する方法では適用できません（有効になりません）。

```java:NG
public void forecast(@AopTest WeatherForm form) {
  //...省略
}
```

#### バインディング

アノテーションをアドバイザにバインドすることができます。

```java:OK
@Around("execution(* com.example.*..*.*(..)) && @args(aopTest)")
public Object a4(ProceedingJoinPoint pjp, AopTest aopTest) throws Throwable {
  //...省略
}
```


### bean

Springコンテナが管理するbeanの名前で指定します。名前にワイルドカードを使用することができます。
この例では"ServiceImpl"という名前で終わるbeanのメソッドに適用しています。

```java:例
@Around("bean(*ServiceImpl)")
```


### @&#x200b;annotation

> '@annotation' can also be used in a binding form :- see the following section on advice for how to make the annotation object available in the advice body.

指定するアノテーションを付与したメソッドに適用します。

```java:OK
@Around("@annotation(com.example.annotation.AopTest)")
```

```java:例
@AopTest
public void beforeTransaction() {
  //...省略
}
```

#### バインディング

アノテーションをアドバイザにバインドすることができます。

```java:OK
@Around("@annotation(aopTest)")
public Object a2(ProceedingJoinPoint pjp, AopTest aopTest) throws Throwable {
  //...省略
}
```

### サポートしていないポイントカット指定子

2017年9月時点

* call
* get
* set
* preinitialization
* staticinitialization
* initialization
* handler
* adviceexecution
* withincode
* cflow
* cflowbelow
* @this
* @withincode

> The full AspectJ pointcut language supports additional pointcut designators that are not supported in Spring. These are: call, get, set, preinitialization, staticinitialization, initialization, handler, adviceexecution, withincode, cflow, cflowbelow, if, @this, and @withincode. Use of these pointcut designators in pointcut expressions interpreted by Spring AOP will result in an IllegalArgumentException being thrown.