カスタムSpring AOPアノテーションの実装

1前書き

この記事では、SpringのAOPサポートを使用してカスタムAOPアノテーションを実装します。

まず、AOPの概要と利点を説明しながら、AOPの概要を説明します。これに続いて、私達は私達の注釈を段階的に実行していきます、そして私達が進むにつれて徐々にAOP概念のより深い理解を積み重ねます。

その結果、AOPの理解が深まり、将来的にカスタムのSpringアノテーションを作成できるようになります。

2 AOPアノテーションとは何ですか?

簡単に要約すると、AOPはアスペクト指向プログラミングの略です。

基本的には、** 既存のコードに変更を加えずに動作を追加する方法です。

AOPの詳細な紹介として、AOPのリンクに関する記事があります:/spring-aop-pointcut-tutorial[pointcuts]および advice 。この記事では、すでに基本的な知識があることを前提としています。

この記事で実装するAOPの種類はアノテーション駆動型です。 Spring @を使用したことがあれば、すでにこれに精通しているかもしれません。 Transactional アノテーション:

@Transactional
public void orderGoods(Order order) {
  //A series of database calls to be performed in a transaction
}

アノテーションメタデータを使用することによって、当社のコアビジネスロジックがトランザクションコードで汚染されることはありません。これにより、推論、リファクタリング、独立したテストがより簡単になります。

場合によっては、Springアプリケーションを開発している人は、これがどのように機能するのかについて詳細に考えなくても、これを「Spring Magic」と見なすことができます。実際には、起こっていることは特に複雑ではありません。

ただし、この記事の手順を完了すると、AOPを理解して活用するために独自のカスタム注釈を作成できるようになります。

3 Mavenの依存関係

まず、https://search.maven.org/classic/#search%7Cga%7C1%7C%20(g%3A%22org.springframework.boot%22%20AND%20a%3A%22spring-boot-を追加しましょう。スターターペアレント%22)%20OR%20%20(g%3A%22org.springframework.boot%22%20AND%20a%3A%22スプリングブートスターター%22)%20OR%20%20(g%3A% 22org.springframework.boot%22%20AND 20a%3A%22spring-boot-starter-aop%22)[Mavenの依存関係]。

この例では、Spring Bootを使用します。構成上の規則により、できるだけ早く立ち上げて実行できるようにするためです。

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>1.5.2.RELEASE</version>
</parent>

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-aop</artifactId>
    </dependency>
</dependencies>

AOPスターターが含まれていることに注意してください。これにより、アスペクトの実装を開始するために必要なライブラリが取り込まれます。

4カスタム注釈を作成する

作成しようとしているアノテーションは、メソッドの実行にかかる時間を記録するために使用されるものです。注釈を作成しましょう。

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface LogExecutionTime {

}

比較的単純な実装ですが、2つのメタアノテーションが何のために使われているのかに注目する価値があります。

@Target 注釈は、私たちの注釈が適用される場所を示しています。ここでは__ElementType.Methodを使用しています。これは、メソッドに対してのみ機能することを意味します。注釈を他の場所で使用しようとすると、コードはコンパイルに失敗します。私たちのアノテーションはロギングメソッドの実行時間に使われるので、この振る舞いは理にかなっています。

そしてhttps://docs.oracle.com/javase/8/docs/api/java/lang/annotation/Retention.html[ @Retention ]は、注釈が実行時にJVMで利用可能かどうかを示すだけです。デフォルトではそうではないので、Spring AOPはアノテーションを見ることができないでしょう。これが、再設定された理由です。

5私たちのアスペクトを創る

注釈ができたので、アスペクトを作成しましょう。これは私たちの分野横断的な懸念をカプセル化するモジュールにすぎません。それが私たちの場合であるのはメソッド実行時間ロギングです。それは httpsでアノテーションが付けられたクラスです://www.eclipse.org/aspectj/doc/released/aspectj5rt-api/org/aspectj/lang/annotation/Aspect.html?is-external = true[@Aspect]:

@Aspect
@Component
public class ExampleAspect {

}

また、 @Component アノテーションも含まれています。検出される春の豆。

基本的に、これはカスタムアノテーションにインジェクトしたいロジックを実装するクラスです。

6. ポイントカットとアドバイスを作成する

それでは、ポイントカットとアドバイスを作成しましょう。これは、私たちの側面にあるアノテーション付きメソッドになります。

@Around("@annotation(LogExecutionTime)")
public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
    return joinPoint.proceed();
}

技術的には、これによってまだ動作が変わることはありませんが、分析が必要なことはまだ多くあります。

まず、このメソッドに httpsという注釈を付けました。//www.eclipse.org/aspectj/doc/released/aspectj5rt-api/org/aspectj/lang/annotation/Around.html?is-external = true[@Around]。 これは私たちのアドバイスであり、アドバイスの前後には、メソッドの実行前後に余分なコードを追加することを意味します。 before after など、他の種類のアドバイスもありますが、これらはこの記事の範囲外です。

次に、 @ Around アノテーションにはポイントカット引数があります。私たちのポイントカットは、「 @ LogExecutionTime でアノテーションが付けられているメソッドにこのアドバイスを適用する」と言っているだけです。

メソッド logExecutionTime() 自体が私たちのアドバイスです。単一の引数 ProceedingJoinPoint があります。 ここでは、これが実行メソッドになります。これは[email protected]で注釈が付けられています。

最後に、アノテーション付きメソッドが呼び出されたときに起こることは、私たちのアドバイスが最初に呼び出されることです。次に何をするかを決めるのは私たちのアドバイスです。私たちの場合、私たちのアドバイスは__proceed()を呼び出すこと以外何もしないことです。

7. 実行時間を記録する

これでスケルトンが完成しました。アドバイスにロジックを追加するだけです。これは、元のメソッドを呼び出すことに加えて実行時間を記録するものになります。この追加の動作をアドバイスに追加しましょう。

@Around("@annotation(LogExecutionTime)")
public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
    long start = System.currentTimeMillis();

    Object proceed = joinPoint.proceed();

    long executionTime = System.currentTimeMillis() - start;

    System.out.println(joinPoint.getSignature() + " executed in " + executionTime + "ms");
    return proceed;
}

繰り返しますが、ここでは特に複雑なことは何もしていません。

現在の時刻を記録し、メソッドを実行してから、コンソールにかかった時間を表示しました。また、 joinpoint インスタンスを使用するために提供されているメソッドシグネチャも記録しています。メソッドの引数など、必要に応じて他の情報にアクセスすることもできます。

それでは、メソッドに @ LogExecutionTime、 というアノテーションを付けてから、実行してみましょう。これが正しく動作するためにはSpring Beanでなければならないことに注意してください。

@LogExecutionTime
public void serve() throws InterruptedException {
    Thread.sleep(2000);
}

実行後、次のメッセージがコンソールに記録されているはずです。

void org.baeldung.Service.serve() executed in 2030ms

8結論

この記事では、Spring Boot AOPを利用してカスタム注釈を作成しました。これをSpring Beanに適用して、実行時に追加の動作を追加できます。

私たちのアプリケーションのソースコードはhttps://github.com/eugenp/tutorials/tree/master/spring-aop[GitHubで利用可能]で入手可能です。これはMavenプロジェクトであり、そのまま実行できます。