1. 序章

この記事は AspectJの迅速で実用的な紹介。

最初に、アスペクト指向プログラミングを有効にする方法を示し、次に、コンパイル時、コンパイル後、およびロード時のウィービングの違いに焦点を当てます。

アスペクト指向プログラミング(AOP)とAspectJの基本の簡単な紹介から始めましょう。

2. 概要

AOPは、横断的関心事の分離を可能にすることでモジュール性を高めることを目的としたプログラミングパラダイムです。 これは、コード自体を変更せずに、既存のコードに追加の動作を追加することによって行われます。 代わりに、どのコードを変更するかを個別に宣言します。

AspectJ は、Javaプログラミング言語の拡張機能を使用して、横断的関心事と横断的関心事の織り方の両方を実装します。

3. Mavenの依存関係

AspectJは、その使用法に応じてさまざまなライブラリを提供します。 Mavenセントラルリポジトリのグループorg.aspectjの下にMavenの依存関係を見つけることができます。

この記事では、コンパイル時、コンパイル後、およびロード時のウィーバーを使用してアスペクトとウィーバーを作成するために必要な依存関係に焦点を当てます。

3.1. AspectJランタイム

AspectJプログラムを実行する場合、クラスパスには、AspectJランタイムライブラリ aspectjrt.jar とともにクラスとアスペクトが含まれている必要があります。

<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjrt</artifactId>
    <version>1.8.9</version>
</dependency>

この依存関係は、 MavenCentralで利用できます。

3.2. AspectJWeaver

AspectJランタイムの依存関係に加えて、ロード時にJavaクラスにアドバイスを導入するためにaspectjweaver.jarも含める必要があります。

<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.8.9</version>
</dependency>

依存関係は、 MavenCentralでも利用できます。

4. アスペクトの作成

AspectJはAOPの実装を提供し、3つのコアコンセプトを持っています:

  • ジョインポイント
  • ポイントカット
  • アドバイス

ユーザーアカウントの残高を検証するための簡単なプログラムを作成することにより、これらの概念を示します。

まず、指定された残高と撤回するメソッドを使用してAccountクラスを作成しましょう。

public class Account {
    int balance = 20;

    public boolean withdraw(int amount) {
        if (balance < amount) {
            return false;
        } 
        balance = balance - amount;
        return true;
    }
}

AccountAspect.aj ファイルを作成して、アカウント情報をログに記録し、アカウントの残高を検証します(AspectJファイルは「 .aj 」ファイル拡張子で終わることに注意してください)。

public aspect AccountAspect {
    final int MIN_BALANCE = 10;

    pointcut callWithDraw(int amount, Account acc) : 
     call(boolean Account.withdraw(int)) && args(amount) && target(acc);

    before(int amount, Account acc) : callWithDraw(amount, acc) {
    }

    boolean around(int amount, Account acc) : 
      callWithDraw(amount, acc) {
        if (acc.balance < amount) {
            return false;
        }
        return proceed(amount, acc);
    }

    after(int amount, Account balance) : callWithDraw(amount, balance) {
    }
}

ご覧のとおり、withdrawメソッドに pointcut を追加し、定義されたpointcutを参照する3つのadvisesを作成しました。

以下を理解するために、以下の定義を紹介します。

  • アスペクト:複数のオブジェクトにまたがる懸念事項のモジュール化。 各側面は、特定の分野横断的な機能に焦点を当てています
  • ジョインポイント:メソッドやプロパティアクセスの実行など、スクリプトの実行中のポイント
  • アドバイス:特定のジョインポイントでアスペクトによって実行されるアクション
  • Pointcut :ジョインポイントに一致する正規表現。 アドバイスはポイントカット式に関連付けられており、ポイントカットに一致する任意の参加ポイントで実行されます

これらの概念とその特定のセマンティクスの詳細については、次のリンクを確認してください。

次に、アスペクトをコードに織り込む必要があります。 以下のセクションでは、AspectJでのコンパイル時のウィービング、コンパイル後のウィービング、およびロード時のウィービングの3種類のウィービングについて説明します。

5. コンパイル時の織り

ウィービングの最も簡単なアプローチは、コンパイル時のウィービングです。 アスペクトのソースコードとアスペクトを使用しているコードの両方がある場合、AspectJコンパイラはソースからコンパイルし、出力として織りクラスファイルを生成します。 その後、コードの実行時に、ウィービングプロセスの出力クラスが通常のJavaクラスとしてJVMにロードされます。

AspectJ開発ツールにはAspectJコンパイラがバンドルされているため、ダウンロードできます。 AJDTの最も重要な機能の1つは、横断的関心事を視覚化するためのツールです。これは、ポイントカット仕様のデバッグに役立ちます。 コードがデプロイされる前であっても、複合効果を視覚化する場合があります。

MojoのAspectJMavenプラグインを使用して、AspectJコンパイラーを使用してAspectJアスペクトをクラスに織り込みます。

<plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>aspectj-maven-plugin</artifactId>
    <version>1.7</version>
    <configuration>
        <complianceLevel>1.8</complianceLevel>
        <source>1.8</source>
        <target>1.8</target>
        <showWeaveInfo>true</showWeaveInfo>
        <verbose>true</verbose>
        <Xlint>ignore</Xlint>
        <encoding>UTF-8 </encoding>
    </configuration>
    <executions>
        <execution>
            <goals>
                <!-- use this goal to weave all your main classes -->
                <goal>compile</goal>
                <!-- use this goal to weave all your test classes -->
                <goal>test-compile</goal>
            </goals>
        </execution>
    </executions>
</plugin>

AspectJコンパイラのオプションリファレンスの詳細については、次のリンクを確認してください。

Accountクラスのテストケースをいくつか追加しましょう。

public class AccountTest {
    private Account account;

    @Before
    public void before() {
        account = new Account();
    }

    @Test
    public void given20AndMin10_whenWithdraw5_thenSuccess() {
        assertTrue(account.withdraw(5));
    }

    @Test
    public void given20AndMin10_whenWithdraw100_thenFail() {
        assertFalse(account.withdraw(100));
    }
}

テストケースを実行すると、コンソールに表示される以下のテキストは、ソースコードが正常に作成されたことを意味します。

[INFO] Join point 'method-call
(boolean com.baeldung.aspectj.Account.withdraw(int))' in Type
'com.baeldung.aspectj.test.AccountTest' (AccountTest.java:20)
advised by around advice from 'com.baeldung.aspectj.AccountAspect'
(AccountAspect.class:18(from AccountAspect.aj))

[INFO] Join point 'method-call
(boolean com.baeldung.aspectj.Account.withdraw(int))' in Type 
'com.baeldung.aspectj.test.AccountTest' (AccountTest.java:20) 
advised by before advice from 'com.baeldung.aspectj.AccountAspect' 
(AccountAspect.class:13(from AccountAspect.aj))

[INFO] Join point 'method-call
(boolean com.baeldung.aspectj.Account.withdraw(int))' in Type 
'com.baeldung.aspectj.test.AccountTest' (AccountTest.java:20) 
advised by after advice from 'com.baeldung.aspectj.AccountAspect'
(AccountAspect.class:26(from AccountAspect.aj))

2016-11-15 22:53:51 [main] INFO  com.baeldung.aspectj.AccountAspect 
-  Balance before withdrawal: 20
2016-11-15 22:53:51 [main] INFO  com.baeldung.aspectj.AccountAspect 
-  Withdraw ammout: 5
2016-11-15 22:53:51 [main] INFO  com.baeldung.aspectj.AccountAspect 
- Balance after withdrawal : 15
2016-11-15 22:53:51 [main] INFO  com.baeldung.aspectj.AccountAspect 
-  Balance before withdrawal: 20
2016-11-15 22:53:51 [main] INFO  com.baeldung.aspectj.AccountAspect 
-  Withdraw ammout: 100
2016-11-15 22:53:51 [main] INFO  com.baeldung.aspectj.AccountAspect 
- Withdrawal Rejected!
2016-11-15 22:53:51 [main] INFO  com.baeldung.aspectj.AccountAspect 
- Balance after withdrawal : 20

6. コンパイル後の織り

コンパイル後のウィービング(バイナリウィービングとも呼ばれる)は、既存のクラスファイルとJARファイルをウィービングするために使用されます。 コンパイル時のウィービングと同様に、ウィービングに使用されるアスペクトは、ソース形式またはバイナリ形式である場合があり、それ自体がアスペクトによって織り込まれている場合があります。

MojoのAspectJMavenプラグインでこれを行うには、プラグイン構成で織り込みたいすべてのJARファイルをセットアップする必要があります。

<configuration>
    <weaveDependencies>
        <weaveDependency>  
            <groupId>org.agroup</groupId>
            <artifactId>to-weave</artifactId>
        </weaveDependency>
        <weaveDependency>
            <groupId>org.anothergroup</groupId>
            <artifactId>gen</artifactId>
        </weaveDependency>
    </weaveDependencies>
</configuration>

ウィーブするクラスを含むJARファイルは、次のようにリストする必要があります。 <dependencies/> Mavenプロジェクトで、次のようにリストされています <weaveDependencies/> の中に <configuration> AspectJMavenプラグインの

7. ロードタイムウィービング

ロード時のウィービングは、クラスローダーがクラスファイルをロードしてクラスをJVMに定義するまで、単純にバイナリウィービングが延期されます。

これをサポートするには、1つ以上の「ウィービングクラスローダー」が必要です。 これらは、ランタイム環境によって明示的に提供されるか、「ウィービングエージェント」を使用して有効にされます。

7.1. ロードタイムウィービングの有効化

AspectJのロード時ウィービングは、クラスのロードプロセスに関与し、VMで定義される前に任意のタイプをウィービングできるAspectJエージェントを使用して有効にできます。 javaagentオプションをJVM-javaagent:pathto / aspectjweaver.jar に指定するか、Mavenプラグインを使用してjavaagentを構成します。

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-surefire-plugin</artifactId>
    <version>2.22.2</version>
    <configuration>
        <argLine>
            -javaagent:"${settings.localRepository}"/org/aspectj/
            aspectjweaver/${aspectj.version}/
            aspectjweaver-${aspectj.version}.jar
        </argLine>
        <useSystemClassLoader>true</useSystemClassLoader>
        <forkMode>always</forkMode>
    </configuration>
</plugin>

7.2. 構成ウィーバー

AspectJのロード時ウィービングエージェントは、aop.xmlファイルを使用して構成されます。 META-INFディレクトリのクラスパスで1つ以上のaop.xmlファイルを検索し、内容を集約してウィーバー構成を決定します。

aop.xml ファイルには、次の2つの主要なセクションが含まれています。

  • アスペクト:ウィーバーに1つ以上のアスペクトを定義し、ウィービングプロセスで使用されるアスペクトを制御します。 アスペクト要素には、オプションで1つ以上のincludeおよびexclude要素を含めることができます(デフォルトでは、定義されたすべてのアスペクトがウィービングに使用されます)
  • Weaver :ウィーバーにウィーバーオプションを定義し、織り込む必要のあるタイプのセットを指定します。 インクルード要素が指定されていない場合、ウィーバーに表示されるすべてのタイプが織り込まれます

ウィーバーのアスペクトを構成しましょう。

<aspectj>
    <aspects>
        <aspect name="com.baeldung.aspectj.AccountAspect"/>
        <weaver options="-verbose -showWeaveInfo">
            <include within="com.baeldung.aspectj.*"/>
        </weaver>
    </aspects>
</aspectj>

ご覧のとおり、 AccountAspect を指すアスペクトを構成しており、com.baeldung.aspectjパッケージのソースコードのみがAspectJによって作成されます。

8. 注釈の側面

おなじみのAspectJコードベースのアスペクト宣言スタイルに加えて、AspectJ5は注釈ベースのアスペクト宣言スタイルもサポートしています。 この開発スタイルをサポートする一連のアノテーションを非公式に「@AspectJ」アノテーションと呼びます。

注釈を作成しましょう:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Secured {
    public boolean isLocked() default false; 
}

@Secured アノテーションを使用して、メソッドを有効または無効にします。

public class SecuredMethod {

    @Secured(isLocked = true)
    public void lockedMethod() {
    }

    @Secured(isLocked = false)
    public void unlockedMethod() {
    }
}

次に、AspectJアノテーションスタイルを使用してアスペクトを追加し、@Securedアノテーションの属性に基づいて権限を確認します。

@Aspect
public class SecuredMethodAspect {
    @Pointcut("@annotation(secured)")
    public void callAt(Secured secured) {
    }

    @Around("callAt(secured)")
    public Object around(ProceedingJoinPoint pjp, 
      Secured secured) throws Throwable {
        return secured.isLocked() ? null : pjp.proceed();
    }
}

AspectJアノテーションスタイルの詳細については、次のリンクを確認してください。

次に、ロード時ウィーバーを使用してクラスとアスペクトをウィービングし、aop.xmlMETA-INFフォルダーの下に配置します。

<aspectj>
    <aspects>
        <aspect name="com.baeldung.aspectj.SecuredMethodAspect"/>
        <weaver options="-verbose -showWeaveInfo">
            <include within="com.baeldung.aspectj.*"/>
        </weaver>
    </aspects>
</aspectj>

最後に、単体テストを追加して結果を確認します。

@Test
public void testMethod() throws Exception {
	SecuredMethod service = new SecuredMethod();
	service.unlockedMethod();
	service.lockedMethod();
}

テストケースを実行するとき、コンソール出力をチェックして、ソースコードにアスペクトとクラスが正常に織り込まれていることを確認できます。

[INFO] Join point 'method-call
(void com.baeldung.aspectj.SecuredMethod.unlockedMethod())'
in Type 'com.baeldung.aspectj.test.SecuredMethodTest'
(SecuredMethodTest.java:11)
advised by around advice from 'com.baeldung.aspectj.SecuredMethodAspect'
(SecuredMethodAspect.class(from SecuredMethodAspect.java))

2016-11-15 22:53:51 [main] INFO com.baeldung.aspectj.SecuredMethod 
- unlockedMethod
2016-11-15 22:53:51 [main] INFO c.b.aspectj.SecuredMethodAspect - 
public void com.baeldung.aspectj.SecuredMethod.lockedMethod() is locked

9. 結論

この記事では、AspectJに関する入門的な概念について説明しました。 詳しくは、AspectJホームページをご覧ください。

この記事のソースコードはGitHubにあります。