1. 概要

この記事では、Springで作成できるさまざまなタイプのAOPアドバイスについて説明します。

Advice は、特定のジョインポイントでアスペクトによって実行されるアクションです。 さまざまな種類のアドバイスには、「アラウンド」、「ビフォア」、「アフター」のアドバイスが含まれます。アスペクトの主な目的は、ロギング、プロファイリング、キャッシング、トランザクション管理などの横断的関心事をサポートすることです。

また、ポイントカット式について詳しく知りたい場合は、これらの前のイントロを確認してください。

2. アドバイスを有効にする

Springでは、AspectJアノテーションを使用してアドバイスを宣言できますが、最初に構成クラスに@EnableAspectJAutoProxyアノテーションを適用する必要があります。これにより、AspectJの@Aspect[X248Xでマークされたコンポーネントの処理がサポートされます。 ]注釈。

@Configuration
@EnableAspectJAutoProxy
public class AopConfiguration {
    ...
}

2.1. スプリングブーツ

Spring Bootプロジェクトでは、@EnableAspectJAutoProxyを明示的に使用する必要はありません。 AspectまたはAdviceがクラスパス上にある場合に、SpringのAOPサポートを有効にする専用のAopAutoConfigurationがあります。

3. アドバイスの前に

このアドバイスは、その名前が示すように、ジョインポイントの前に実行されます。例外がスローされない限り、アドバイスするメソッドの継続的な実行を妨げることはありません。

呼び出される前にメソッド名をログに記録するだけの次の側面について考えてみます。

@Component
@Aspect
public class LoggingAspect {

    private Logger logger = Logger.getLogger(LoggingAspect.class.getName());

    @Pointcut("@target(org.springframework.stereotype.Repository)")
    public void repositoryMethods() {};

    @Before("repositoryMethods()")
    public void logMethodCall(JoinPoint jp) {
        String methodName = jp.getSignature().getName();
        logger.info("Before " + methodName);
    }
}

logMethodCall アドバイスは、repositoryMethodsポイントカットによって定義されたリポジトリメソッドの前に実行されます。

4. アドバイス後

@After アノテーションを使用して宣言されたアドバイスの後は、例外がスローされたかどうかに関係なく、一致したメソッドの実行後に実行されます。

ある意味では、finallyブロックに似ています。 通常の実行後にのみアドバイスをトリガーする必要がある場合は、@AfterReturningアノテーションによって宣言されたreturningAdviceを使用する必要があります。 ターゲットメソッドが例外をスローしたときにのみアドバイスをトリガーする場合は、 throwing Advice、@AfterThrowingアノテーションを使用して宣言されたを使用する必要があります。

Foo の新しいインスタンスが作成されたときに、いくつかのアプリケーションコンポーネントに通知したいとします。 FooDao からイベントを公開することはできますが、これは単一責任の原則に違反します。

代わりに、次の側面を定義することでこれを実現できます。

@Component
@Aspect
public class PublishingAspect {

    private ApplicationEventPublisher eventPublisher;

    @Autowired
    public void setEventPublisher(ApplicationEventPublisher eventPublisher) {
        this.eventPublisher = eventPublisher;
    }

    @Pointcut("@target(org.springframework.stereotype.Repository)")
    public void repositoryMethods() {}

    @Pointcut("execution(* *..create*(Long,..))")
    public void firstLongParamMethods() {}

    @Pointcut("repositoryMethods() && firstLongParamMethods()")
    public void entityCreationMethods() {}

    @AfterReturning(value = "entityCreationMethods()", returning = "entity")
    public void logMethodCall(JoinPoint jp, Object entity) throws Throwable {
        eventPublisher.publishEvent(new FooCreationEvent(entity));
    }
}

まず、 @AfterR eturning アノテーションを使用することで、ターゲットメソッドの戻り値にアクセスできることに注意してください。 次に、タイプ JoinPoint、のパラメーターを宣言することにより、ターゲットメソッドの呼び出しの引数にアクセスできます。

次に、イベントをログに記録するだけのリスナーを作成します。

@Component
public class FooCreationEventListener implements ApplicationListener<FooCreationEvent> {

    private Logger logger = Logger.getLogger(getClass().getName());

    @Override
    public void onApplicationEvent(FooCreationEvent event) {
        logger.info("Created foo instance: " + event.getSource().toString());
    }
}

5. アドバイスの周り

アラウンドアドバイスは、メソッド呼び出しなどのジョインポイントを囲みます。

これは最も強力な種類のアドバイスです。 アラウンドアドバイスは、メソッド呼び出しの前後の両方でカスタム動作を実行できます。また、ジョインポイントに進むか、独自の戻り値を提供するか例外をスローすることによってアドバイスされたメソッドの実行を短縮するかを選択する責任があります。

その使用法を示すために、メソッドの実行時間を測定するとします。 このためのアスペクトを作成しましょう:

@Aspect
@Component
public class PerformanceAspect {

    private Logger logger = Logger.getLogger(getClass().getName());

    @Pointcut("within(@org.springframework.stereotype.Repository *)")
    public void repositoryClassMethods() {};

    @Around("repositoryClassMethods()")
    public Object measureMethodExecutionTime(ProceedingJoinPoint pjp) throws Throwable {
        long start = System.nanoTime();
        Object retval = pjp.proceed();
        long end = System.nanoTime();
        String methodName = pjp.getSignature().getName();
        logger.info("Execution of " + methodName + " took " + 
          TimeUnit.NANOSECONDS.toMillis(end - start) + " ms");
        return retval;
    }
}

このアドバイスは、repositoryClassMethodsポイントカットに一致する結合ポイントのいずれかが実行されたときにトリガーされます。

このアドバイスは、タイプの1つのパラメーターを取ります ProceedingJointPoint。 このパラメーターは、ターゲットメソッド呼び出しの前にアクションを実行する機会を提供します。 私この場合、メソッドの開始時間を節約するだけです。

次に、ターゲットメソッドは任意のタイプの結果を返すことができるため、アドバイスの戻りタイプはObjectです。 ターゲットメソッドがvoidの場合、 nullが返されます。 ターゲットメソッドの呼び出し後、タイミングを測定してログに記録し、メソッドの結果値を呼び出し元に返すことができます。

6. 概要

この記事では、Springのさまざまな種類のアドバイスと、それらの宣言および実装について学習しました。 スキーマベースのアプローチとAspectJアノテーションを使用してアスペクトを定義しました。 また、いくつかの可能なアドバイスアプリケーションを提供しました。

これらすべての例とコードスニペットの実装は、myGitHubプロジェクトにあります。