CDIインターセプターとSpring AspectJ

1前書き

インターセプターパターンは通常、アプリケーションに新しい分野横断的な機能やロジックを追加するために使用され、多数のライブラリで強力なサポートを提供します。

この記事では、これら2つの主要なライブラリーについて説明します。

CDIインターセプターとSpring AspectJ。

2 CDIインターセプタープロジェクトの設定

CDIはJava EEで正式にサポートされていますが、Java SE環境でCDIを使用するためのサポートを提供する実装もあります。

Weld は、Java SEでサポートされているCDI実装の一例と見なすことができます。

CDIを使用するには、WeldライブラリをPOMにインポートする必要があります。

<dependency>
    <groupId>org.jboss.weld.se</groupId>
    <artifactId>weld-se-core</artifactId>
    <version>2.3.5.Final</version>
</dependency>

最新のWeldライブラリはhttps://mvnrepository.com/artifact/org.jboss.weld.se/weld-se-core[Maven]リポジトリにあります。

それでは、単純なインターセプターを作成しましょう。

3 CDIインターセプター の紹介

インターセプトする必要があるクラスを指定するために、インターセプターバインディングを作成しましょう:

@InterceptorBinding
@Target( { METHOD, TYPE } )
@Retention( RUNTIME )
public @interface Audited {
}

インターセプターバインディングを定義したら、実際のインターセプター実装を定義する必要があります。

@Audited
@Interceptor
public class AuditedInterceptor {
    public static boolean calledBefore = false;
    public static boolean calledAfter = false;

    @AroundInvoke
    public Object auditMethod(InvocationContext ctx) throws Exception {
        calledBefore = true;
        Object result = ctx.proceed();
        calledAfter = true;
        return result;
    }
}

すべての @ AroundInvoke メソッドは、 javax.interceptor.InvocationContext 引数を取り、 java.lang.Object を返し、 Exception をスローすることがあります。

そのため、新しい @ Audit インタフェースを使用してメソッドに注釈を付けると、 auditMethod が最初に呼び出され、その後で初めてターゲットメソッドも実行されます。

4 CDIインターセプターを適用する

作成したインターセプターを何らかのビジネスロジックに適用しましょう。

public class SuperService {
    @Audited
    public String deliverService(String uid) {
        return uid;
    }
}

このシンプルなサービスを作成し、傍受したいメソッドに @ Audited アノテーションを付けました。

CDIインターセプタを有効にするには、 META-INF ディレクトリにある beans.xml ファイルで完全なクラス名を指定する必要があります。

<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://java.sun.com/xml/ns/javaee/beans__1__2.xsd">
    <interceptors>
        <class>com.baeldung.interceptor.AuditedInterceptor</class>
    </interceptors>
</beans>

インターセプターが実際に動作したことを検証するために、次のテストを実行しましょう。

public class TestInterceptor {
    Weld weld;
    WeldContainer container;

    @Before
    public void init() {
        weld = new Weld();
        container = weld.initialize();
    }

    @After
    public void shutdown() {
        weld.shutdown();
    }

    @Test
    public void givenTheService__whenMethodAndInterceptorExecuted__thenOK() {
        SuperService superService = container.select(SuperService.class).get();
        String code = "123456";
        superService.deliverService(code);

        Assert.assertTrue(AuditedInterceptor.calledBefore);
        Assert.assertTrue(AuditedInterceptor.calledAfter);
    }
}

このクイックテストでは、最初にコンテナからBean SuperService を取得し、次にその上でビジネスメソッド deliverService を呼び出して、インターセプタ AuditedInterceptor が実際にその状態変数を検証することによって呼び出されたことを確認します。

また、 @ Before および @ After アノテーション付きメソッドがあり、それぞれWeld containerを初期化してシャットダウンします。

5 CDIに関する考慮事項

CDIインターセプターの以下の利点を指摘できます。

  • Java EE仕様の標準機能です

  • 一部のCDI実装ライブラリはJava SEで使用できます

  • 当社が第三者に対して厳しい制限を課している場合に使用可能

図書館

CDIインターセプターの欠点は次のとおりです。

  • ビジネスロジックを持つクラスとインターセプターの間の密接な結合

  • プロジェクトでどのクラスがインターセプトされているのかわかりにくい

  • メソッドのグループにインターセプターを適用するための柔軟なメカニズムの欠如

6. 春のアスペクトJ

SpringはAspectJシンタックスを使った同様のインターセプタ機能の実装をサポートします。

まず、次のSpringとAspectJの依存関係をPOMに追加する必要があります。

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>4.3.1.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.8.9</version>
</dependency>

Spring context 、https://mvnrepository.com/artifact/org.aspectj/aspectjweaver[aspectjweaver]の最新版はMavenリポジトリ

AspectJアノテーション構文を使って簡単なアスペクトを作成することができます:

@Aspect
public class SpringTestAspect {
    @Autowired
    private List accumulator;

    @Around("execution(**  com.baeldung.spring.service.SpringSuperService.** (..))")
    public Object auditMethod(ProceedingJoinPoint jp) throws Throwable {
        String methodName = jp.getSignature().getName();
        accumulator.add("Call to " + methodName);
        Object obj = jp.proceed();
        accumulator.add("Method called successfully: " + methodName);
        return obj;
    }
}

SpringSuperService クラスのすべてのメソッドに適用されるアスペクトを作成しました。これは、簡単にするために次のようになります。

public class SpringSuperService {
    public String getInfoFromService(String code) {
        return code;
    }
}

7. 春のアスペクトJアスペクトの適用

その側面が実際にサービスに適用されることを検証するために、次のユニットテストを書きましょう。

@RunWith(SpringRunner.class)
@ContextConfiguration(classes = { AppConfig.class })
public class TestSpringInterceptor {
    @Autowired
    SpringSuperService springSuperService;

    @Autowired
    private List accumulator;

    @Test
    public void givenService__whenServiceAndAspectExecuted__thenOk() {
        String code = "123456";
        String result = springSuperService.getInfoFromService(code);

        Assert.assertThat(accumulator.size(), is(2));
        Assert.assertThat(accumulator.get(0), is("Call to getInfoFromService"));
        Assert.assertThat(accumulator.get(1), is("Method called successfully: getInfoFromService"));
    }
}

このテストでは、サービスを注入し、メソッドを呼び出して結果を確認します。

設定は次のようになります。

@Configuration
@EnableAspectJAutoProxy
public class AppConfig {
    @Bean
    public SpringSuperService springSuperService() {
        return new SpringSuperService();
    }

    @Bean
    public SpringTestAspect springTestAspect() {
        return new SpringTestAspect();
    }

    @Bean
    public List getAccumulator() {
        return new ArrayList();
    }
}

ここの @ EnableAspectJAutoProxy アノテーションの重要な側面の1つ - これは、AspectJの @ Aspect アノテーションでマークされたコンポーネントの処理のサポートを可能にします。これは、SpringのXML要素に見られる機能と同様です。

8 Spring AspectJに関する考慮事項

Spring AspectJを使用する利点のいくつかを指摘しましょう。

  • インターセプタはビジネスロジックから切り離されています

  • インターセプターは依存性注入から利益を得ることができます

  • インターセプターはそれ自体にすべての構成情報を持っています

  • 新しいインターセプターを追加しても既存のコードを増強する必要はない

  • インターセプターはどの方法を選択するための柔軟なメカニズムを持っています

傍受 ** Java EEがなくても使用可能

そしてもちろん、いくつかの欠点もあります。

  • インターセプターを開発するためにはAspectJの構文を知る必要があります

  • AspectJインターセプターの学習曲線は、

CDIインターセプター

9 CDIインターセプターVS Spring AspectJ

現在のプロジェクトでSpringを使用している場合は、Spring AspectJを検討するのが良い選択です。

本格的なアプリケーションサーバーを使用している場合、またはプロジェクトがSpring(またはGoogle Guiceなどの他のフレームワーク)を使用しておらず、厳密にJava EEである場合は、CDIインターセプターを選択する以外に何もありません。

10結論

この記事では、CDIインターセプターとSpring AspectJの2つのインターセプター・パターンの実装について説明しました。それぞれ長所と短所を取り上げました。

この記事の例のソースコードはhttps://github.com/eugenp/tutorials/tree/master/cdi[GitHub]のリポジトリにあります。