AspectJ 環境を構築しよう

Advice

(1.1rc1 OK)

3 つの advice があります。

before before(Formals): Pointcut { Body }
after after(Formals) : Pointcut { Body }
after(Formals) returning [ (Formal) ]: Pointcut { Body }
after(Formals) throwing [ (Formal) ]: Pointcut { Body }
around Type around(Formals) [ throws TypeList ] : Pointcut { Body }

以下では AspectJ でサポートされているこれら 3 つの advice についての豊富なサンプルコードを紹介します。

クイックリンク:

adivce についての公式なドキュメントについては以下を参照してください: 以下で紹介する advice の使い方のサンプルコードの量については、この記事のほうが上記のドキュメントより優っています。しかし adivce とは何かやその他の正式な advice で何ができて何ができないのか、あるいは、どんな書き方ができてどんな書き方ができないのかの記述については、上記のドキュメントに目を通すことをお勧めします。

before advice

before advice は、大きく分けて 3 つある advice の中で、一番単純なものです。before advice では join point の 前で どのようなことをするのかを、アスペクト作成者は定義します。たとえば、メソッドの呼び出しの前にそのメソッドに渡された引数が事前条件を満たしていなければ、その時には実行時例外をスローする、といったコードを書くことができます。

before advice - その 1

(1.1rc1 OK)

最初の before advice の例は、とてもシンプルなものです。通常のクラスである Hello クラスは "Hello!" を出力するだけのメソッドである hello() を定義しています。BeforeHelloAspect アスペクトを使って、hello() メソッドの 呼び出し に "Before!" という文字列を出力することがこの例での目的です。

public class Hello {
	public void hello() {
		System.out.println("Hello!");
	}
}
public class Main {
	public static void main(String[] args) {
		new Hello().hello();
	}
}
以下の BeforeHelloAspect アスペクトは、1 つの pointcut と 1 つの before advice を定義しています。pointcut beforeHello() では、call pointcut を使って、Hello クラスの void hello() メソッドの呼び出しを選び出しています:
public aspect BeforeHelloAspect {

	pointcut beforeHello():
		call( void Hello.hello() ); // Hello クラスの hello() メソッドが呼ばれた (call) 地点

	before(): beforeHello() {
		System.out.println("Before!");
	}
}
プログラムの実行結果は以下のようになります。
java> java -cp dest aoptest.Main
Before!
Hello!
また、pointcut の定義は省略することもできます:
public aspect BeforeHelloAspect {

	before(): call( void Hello.hello() ) {
		System.out.println("Before!");
	}
}

before advice - その 2

(1.1rc1 OK)

この例では、例外をスローするかもしれないメソッドに対して before advice を適用している場合に、実際にその例外がスローされたらどういう結果になるのかを見てみます。


public class Main {
	public static void main(String[] args) {
		try {
			Class.forName("xxx"); // xxx は存在しないクラス名なので
			                      // ClassNotFoundException をスローする。
		} catch (ClassNotFoundException e) {
			System.out.println(e);
		}
	}
}
public aspect MainAspect {
	before(): call( Class Class.forName(String) ) {
		System.out.println("before");
	}
}
java> java -cp dest aoptest.Main
before
java.lang.ClassNotFoundException: xxx

after advice

after advice は、大きく分けて 3 つある advice のうちの 1 つです。基本的には、before advice の逆の意味を持っています。つまり、プログラマが pointcut を使って指定した特定の地点 (join point) の後に実行されるコードを書きます。

とはいえ after advice は before advice と比べるとやや複雑です。その複雑さは、たとえば、メソッドの呼び出しを考えてみた場合でも、いくつかの特別な状況が考えられるためです。1 つ目は、メソッドが例外をスローした後に、advice のコードを実行する場合です。もう 1 つは、メソッドが通常通りに終了した後に、advice のコードを実行する場合です。最後は、メソッドが例外をスローしたとしても、メソッドが通常に終了したとしても、その後に advice のコードを実行する場合です。

AspectJ では、これら 3 つの状況を別々に扱うことができます。

以下の after advice の例では、まず、メソッドが例外をスローしたとしても、メソッドが通常に終了したとしても、その後に advice のコードを実行するケースについてを扱っています。その次に、メソッドが通常通りに終了した後のケースを、最後に、例外がスローされた後のケースについてを扱っています。

after advice - その 1

(1.1rc1 OK)

after advice の最初の例は before advice の最初の例とほとんど同じです。アスペクトを使ってやりたいことは Hello クラスの hello() メソッドが呼ばれた後になんらかの処理を行うことです。ここでは、単に "After!" という文字列を出力します。

public class Hello {
	public void hello() {
		System.out.println("Hello!");
	}
}
public class Main {
	public static void main(String[] args) {
		new Hello().hello();
	}
}
public aspect AfterHelloAspect {

	after(): call( void Hello.hello() ) {
		System.out.println("After!");
	}
}
java> java -cp dest aoptest.Main
Hello!
After!

after advice - その 2

(1.1rc1 OK)

この例では、例外を考慮しています。あるメソッドの呼び出しがチェックされる例外 (checked exception) をスローするかもしれない場合に after advice を適用したらどうなるのでしょうか?

この例では、チェックされる例外をスローするかもしれないメソッドの呼び出しとして、Class クラスの static なメソッドである Class forName(String className) メソッドを考えます。

以下の Hello クラスのコードから見られるように、"xxx" という存在しないクラス名を引数として、Class クラスの static なメソッドである forName(String className) メソッドを呼び出しています。当然、存在しないクラス名を引数としているので forName メソッドは ClassNotFoundException 例外をスローします:

public class Main {
	public static void main(String[] args) {
		try {
			Class.forName("xxx"); // "xxx" というクラスは存在しないので
                                                  // ClassNotFoundException がスローされる。
		} catch (ClassNotFoundException e) {
			System.out.println(e);
		}
	}
}
考えるアスペクト自体は、単純なものです。今までのサンプルコードと同じように、call pointcut を使って Class クラスの Class forName(String) メソッドを選び出します。そして "after" という文字列を出力します:
public aspect MainAspect {

	after(): call( Class Class.forName(String) ) {
		System.out.println("after");
	}
}
実行結果は以下のようになります:
java> java -cp dest aoptest.Main
after
java.lang.ClassNotFoundException: xxx

after advice - その 3

(1.1rc1 OK)

この例では、実行時例外 (run-time exception) に対する after advice を考えます。

public class Main {
	
	public static void main(String[] args) {
		try {
			new Main().method();

		} catch (Exception e) {
			System.out.println(e);
		}
	}

	public void method() {
		throw new RuntimeException();
	}
}
method() メソッドのように、実行時例外をスローするメソッドに対して、after advice を適用したとすると、その実行結果はどのようなものになるのでしょうか?
public aspect MainAspect {
	after(): call( void Main.method() ) {
		System.out.println("after");
	}
}
以下のような結果になります。
java> java -cp dest aoptest.Main
after
java.lang.RuntimeException

after returning advice

before advice と違って、after advice にはさらに 3 つの細かい種類があります。
after advice の種類
after after(Formals) : Pointcut { Body }
after(Formals) returning [ (Formal) ]: Pointcut { Body }
after(Formals) throwing [ (Formal) ]: Pointcut { Body }

以下のいくつかの例では returning を伴う after advice についてを考えます。

after returning advice を使えば、例えば、メソッドが正常に終了した後で何らかの処理を行うことができます。もし、そのメソッドが例外をスローしたとすると advice は実行されません。

また after returning advice は、メソッドが戻り値を返す場合、その値にアクセスすることもできます。

以下では、これらのケースのそれぞれについてサンプルコードを紹介しています。

after advice - その 4 : returning - その 1

(1.1rc1 OK)

この例での、クラス (Main) とアスペクト (MainAspect) は、前の「例 3」のものとほとんど同じです。「例 3」では、Main クラスの method() メソッドが例外をスローした後でも、MainAspect アスペクトで定義されたコード ("after" と表示する) は実行されました。

一方、この例では after returning advice を考えているので、定義されたコード ("after" と表示する) は、メソッドが正常に終了した場合にのみにしか実行されません。以下の Main クラスの実装から見られるように method() メソッドは例外をスローしています。したがって、文字列は表示されません。

public class Main {
	public static void main(String[] args) {
		try {
			new Main().method();
		} catch (Exception e) {
			System.out.println(e);
		}
	}

	public void method() {
		throw new RuntimeException();
	}
}
public aspect MainAspect {

	after() returning : call( void Main.method() ) {
		System.out.println("after");
	}

}
java> java -cp dest aoptest.Main
java.lang.RuntimeException
もし method() メソッドが以下のように例外をスローしないとすると advice は期待したとおりに実行されます:
public class Main {
	public static void main(String[] args) {
		new Main().method();
	}

	public void method() {
		System.out.println("method");
	}
}
public aspect MainAspect { // 前と一緒

	after() returning : call( void Main.method() ) {
		System.out.println("after");
	}

}
java> java -cp dest aoptest.Main
method
after

after advice - その 5 : returning - その 2

(1.1rc1 OK)

returning を伴った after advice の 1 つの特徴に、advice の本体内でその戻り値にアクセスできる、というものがあります。この例では、そのような例を紹介します。

public class Main {
	public static void main(String[] args) {

		String str = new Main().getString();
	
		System.out.println(str);
	}

	public String getString() {
		return "aaa";
	}
}
public aspect MainAspect {

	after() returning(String str) : call( String Main.getString() ) {

		System.out.println("after");
		System.out.println("str: " + str);
	}

}
以下のような実行結果になります。
java> java -cp dest aoptest.Main
after
str: aaa
aaa

after advice - その 6 : returning - その 3

(1.1rc1 OK)

public class Main {
	public static void main(String[] args) {
		String str = new Main().getString();
	
		System.out.println(str);
	}

	public String getString() {
		return "aaa";
	}
}
public aspect MainAspect {

	after() returning(Object obj) : call( String Main.getString() ) {
		System.out.println("after - obj: " + obj);
	}

}
java> java -cp dest aoptest.Main
after - obj: aaa
aaa
例その 2:
public class Main {
	public static void main(String[] args) {

		System.out.println( new Main().getString() );

		System.out.println( new Main().getInteger() );
	}

	public String getString() {
		return "aaa";
	}

	public Integer getInteger() {
		return new Integer(10);
	}
}
public aspect MainAspect {

	after() returning(Object obj) : call( * Main.*() ) {
		System.out.println("after - obj: " + obj);
	}

}
java> java -cp dest aoptest.Main
after - obj: aaa
aaa
after - obj: 10
10
例その 3:
public class Main { // 前と変更なし

	public static void main(String[] args) {

		System.out.println( new Main().getString() );

		System.out.println( new Main().getInteger() );
	}

	public String getString() {
		return "aaa";
	}

	public Integer getInteger() {
		return new Integer(10);
	}
}
public aspect MainAspect {

	after() returning(String str) : call( * Main.*() ) {

		System.out.println("after - str: " + str);
	}

}
java> java -cp dest aoptest.Main
after - str: aaa
aaa
10

after advice - その 7 : returning - その 4

(1.1rc1 OK)

public class Main {
	public static void main(String[] args) {

		Object obj = new Main().getObject();

		System.out.println( obj );
	}

	public Object getObject() {
		return "aaa";
	}
}
public aspect MainAspect {

	after() returning(String str) : call( Object Main.getObject() ) {
		System.out.println("after - str: " + str);
		System.out.println("     length: " + str.length() );
	}

}
java> java -cp dest aoptest.Main
after - str: aaa
     length: 3
aaa
例 2:
public class Main {
	public static void main(String[] args) {

		Object obj = new Main().getObject();

		System.out.println( obj );
	}

	public Object getObject() {
		return new Integer(10);
	}
}
public aspect MainAspect { // 前と変更なし

	after() returning(String str) : call( Object Main.getObject() ) {
		System.out.println("after - str: " + str);
		System.out.println("     length: " + str.length());
	}

}
java> java -cp dest aoptest.Main
10

after advice - その 8 : returning - その 5

(1.1rc1 OK)

public class Main {
	public static void main(String[] args) {

		int i = new Main().getInt();

		System.out.println( i );
	}

	public int getInt() {
		return 10;
	}
}
public aspect MainAspect {

	after() returning(int i) : call( int Main.getInt() ) {
		System.out.println("after - int: " + i);
	}

}
java> java -cp dest aoptest.Main
after - int: 10
10
できそうでできない例 - その 1:
public class Main {
	public static void main(String[] args) {

		int i = new Main().getInt();

		System.out.println( i );
	}

	public int getInt() {
		return 10;
	}
}
public aspect MainAspect {

	after() returning(Integer i) : call( int Main.getInt() ) {
		System.out.println("after - integer: " + i);
	}

}
java> java -cp dest aoptest.Main
10
できそうでできない例 - その 2:
public class Main {
	public static void main(String[] args) {

		Integer i = new Main().getInteger();

		System.out.println( i );
	}

	public Integer getInteger() {
		return new Integer(10);
	}
}
public aspect MainAspect {

	after() returning(int i) : call( Integer Main.getInteger() ) {
		System.out.println("after - int: " + i);
	}

}
java> java -cp dest aoptest.Main
10
できなさそうだけど、できる例:
public class Main {
	public static void main(String[] args) {

		int i = new Main().getInt();

		System.out.println( i );
	}

	public int getInt() {
		return 10;
	}
}
public aspect MainAspect {

	after() returning(Object obj) : call( int Main.getInt() ) {
		System.out.println("after - obj: " + obj);
		System.out.println("       type: " + obj.getClass());
	}

}
after - obj: 10
       type: class java.lang.Integer
10

after advice - その 9 : throwing - その 1

(1.1rc1 OK)

after throwing advice は、例外がスローされた場合にのみに実行されます。

public class Main {
	public static void main(String[] args) {
		new Main().method();
	}

	public void method() {
		System.out.println("method");
	}
}
public aspect MainAspect {

	after() throwing : call( void Main.method() ) {
		System.out.println("after");
	}
}
java> java -cp dest aoptest.Main
method

after advice - その 10 : throwing - その 2

(1.1rc1 OK)

public class Main {
	public static void main(String[] args) {
		try {
			new Main().method();

		} catch (Exception e) {
			System.out.println(e);
		}
	}

	public void method() throws Exception {
		throw new Exception();
	}
}
public aspect MainAspect {

	after() throwing : call( void Main.method() ) {
		System.out.println("after");
	}
}
after throwing advice は例外がスローされた後に実行されるので、以下のような結果になります。
java> java -cp dest aoptest.Main
after
java.lang.Exception

after advice - その 11 : throwing - その 3

(1.1rc1 OK)

実行時例外がスローされる場合について。

public class Main {
	public static void main(String[] args) {
		try {
			new Main().method();

		} catch (Exception e) {
			System.out.println(e);
		}
	}

	public void method() {
		throw new RuntimeException();
	}
}
public aspect MainAspect {

	after() throwing : call( void Main.method() ) {
		System.out.println("after");
	}
}
java> java -cp dest aoptest.Main
after
java.lang.RuntimeException

after advice - その 12 : throwing - その 4

(1.1rc1 OK)

例外オブジェクトへのアクセスについて。

public class Main {
	public static void main(String[] args) {
		try {
			new Main().method();

		} catch (Exception e) {
			System.out.println(e);
		}
	}

	public void method() throws Exception {
		throw new Exception();
	}
}
public aspect MainAspect {

	after() throwing(Exception e) : call( void Main.method() ) {
		System.out.println("after - exception: " + e);
	}
}
java> java -cp dest aoptest.Main
after - exception: java.lang.Exception
java.lang.Exception

after advice - その 13 : throwing - その 5

(1.1rc1 OK)

実行時例外オブジェクトへのアクセスについて。

public class Main {
	public static void main(String[] args) {
		try {
			new Main().method();

		} catch (Exception e) {
			System.out.println(e);
		}
	}

	public void method() {
		throw new RuntimeException();
	}
}
public aspect MainAspect {

	after() throwing(RuntimeException e) : call( void Main.method() ) {
		System.out.println("after - exception: " + e);
	}
}
java> java -cp dest aoptest.Main
after - exception: java.lang.RuntimeException
java.lang.RuntimeException
例その 2: RuntimeException は Exception クラスを継承しているので Exception の型としてもアクセスできます:
public class Main { // 上と変更なし
	public static void main(String[] args) {
		try {
			new Main().method();

		} catch (Exception e) {
			System.out.println(e);
		}
	}

	public void method() {
		throw new RuntimeException();
	}
}
public aspect MainAspect {

	after() throwing(Exception e) : call( void Main.method() ) {
		System.out.println("after - exception: " + e);
	}
}
public class Main {
	public static void main(String[] args) {
		try {
			new Main().method();

		} catch (Exception e) {
			System.out.println(e);
		}
	}

	public void method() {
		throw new RuntimeException();
	}
}
java> java -cp dest aoptest.Main
after - exception: java.lang.RuntimeException
java.lang.RuntimeException

after advice - その 14 : throwing - その 6

(1.1rc1 OK)

public class MyException extends Exception { }
public class Main {
	public static void main(String[] args) {
		try {
			new Main().method();

		} catch (Exception e) {
			System.out.println(e);
		}
	}

	public void method() throws Exception {
		throw new MyException();
	}
}
public aspect MainAspect {

	after() throwing(MyException e) : call( void Main.method() ) {
		System.out.println("after - exception: " + e);
	}
}
java> java -cp dest aoptest.Main
after - exception: aj.MyException
aj.MyException
例その 2:
public class MyExceptionA extends Exception { }
public class MyExceptionB extends Exception { }
public class Main {
	public static void main(String[] args) {
		try {
			new Main().method(true);

		} catch (Exception e) {
			System.out.println(e);
		}

		System.out.println("---");

		try {
			new Main().method(false);

		} catch (Exception e) {
			System.out.println(e);
		}
	}

	public void method(boolean b) throws Exception {
		if (b) {
			throw new MyExceptionA();
		} else {
			throw new MyExceptionB();
		}
	}
}
public aspect MainAspect {

	after() throwing(MyExceptionA e) : call( void Main.method(boolean) ) {
		System.out.println("after - exception: " + e);
	}
}
java> java -cp dest aoptest.Main
after - exception: aj.MyExceptionA
aj.MyExceptionA
---
aj.MyExceptionB

around advice

before advice と after advice に加え、AspectJ で使用できる 3 つめの advice のタイプが around advice です。around advice は、join point を実行するかどうか、を決めることができます。たとえば、完全に異なる動作をするようなコードや、元々のコードに加えて around の中でさらなるコードを追加するなどができます。

around advice - その 1 : なにもしない

(1.1rc1 OK)

この例では、オリジナルのコード (Java 側のコード) ではなんらかの処理が行われるようになっているのに、around advice を使うことで、何もしないようなコードに変更できることを紹介します。

public class Main {
	public static void main(String[] args) {
		new Main().method();
	}

	public void method() {
		System.out.println("method");
	}
}
public aspect MainAspect {

	void around() : call( void Main.method() ) {
		// 元のメソッドの代わりに、何もしない
	}

}
java>java -cp dest aoptest.Main
// 何もなし

around advice - その 2 : オリジナルを呼び出す

(1.1rc1 OK)

前の例では、元々なんらかの処理が期待されていたコードを around advice を使うことで完全に何の処理も行わないコードで置き換えました。この例では、around advice からオリジナルのコード (join point) を呼び出すことができる特別な proceed() 構文の使い方を紹介します。

public class Main {
	public static void main(String[] args) {
		new Main().method();
	}

	public void method() {
		System.out.println("method");
	}
}
public aspect MainAspect {

	void around() : call( void Main.method() ) {
		proceed();
	}
}
以下のような実行結果になります。
java> java -cp dest aoptest.Main
method

around advice - その 3 : 何度も呼び出す

(1.1rc1 OK)

proceed() は、何度でも呼び出すことができます。

public class Main {
	public static void main(String[] args) {
		new Main().method();
	}

	public void method() {
		System.out.println("method");
	}
}
public aspect MainAspect {

	void around() : call( void Main.method() ) {
		proceed();
		proceed();
	}
}
java> java -cp dest aoptest.Main
method
method

around advice - その 4 : 自由に

(1.1rc1 OK)

around advice では、proceed() 構文を使うことで、join point を いつでも 呼び出すことができます。つまり、新たなコードを追加して join point の前後で好きなことができます。この例では、そのような例を紹介します。

public class Main {
	public static void main(String[] args) {
		new Main().method();
	}

	public void method() {
		System.out.println("method");
	}
}

public aspect MainAspect {

	void around() : call( void Main.method() ) {
		System.out.println("before");
		proceed();
		System.out.println("after");
	}
}
java> java -cp dest aoptest.Main
before
method
after

around advice - その 5

(1.1rc1 OK)

この例では、join point がなんらかの戻り値を伴っている場合の around advice の使い方を紹介します。

public class Main {
	public static void main(String[] args) {

		String str = new Main().getString();
	
		System.out.println(str);
	}

	public String getString() {
		return "aaa";
	}
}
public aspect MainAspect {

	String around() : call( String Main.getString() ) {
		return "xxx";
	}
}
java> java -cp dest aoptest.Main
xxx
例 2: この例では、around advice の戻り値の型とメソッド呼び出しの戻り値の型が一致していますが、一致しないようなケースも許されます。
public class Main { // 上と変更なし
	public static void main(String[] args) {
		String str = new Main().getString();
	
		System.out.println(str);
	}

	public String getString() {
		return "aaa";
	}
}
public aspect MainAspect {

	Object around() : call( String Main.getString() ) {
		return "xxx";
	}
}
java> java -cp dest aoptest.Main
xxx
しかし、このようなケースでは、もし型が一致していないと ClassCastException がスローするかもしれません:
public class Main { // 上と変更なし
	public static void main(String[] args) {
		String str = new Main().getString();
	
		System.out.println(str);
	}

	public String getString() {
		return "aaa";
	}
}
public aspect MainAspect {

	Object around() : call( String Main.getString() ) {
		return new Integer(10);
	}
}
java> java -cp dest aoptest.Main
Exception in thread "main" java.lang.ClassCastException
        at aj.Main.main(Main.java:6)

around advice - その 6

(1.1rc1 OK)

proceed() が、引数をとるケースもあります。この例では、そのようなコードを紹介します。

public class Main {
	public static void main(String[] args) {
		new Main().setString("aaa");
	}

	public void setString(String str) {
		System.out.println(str);
	}
}
public aspect MainAspect {

	void around(String str) :
		call( void Main.setString(String) ) && args(str)
	{
		proceed("xxx");
	}
}
java> java -cp dest aoptest.Main
xxx

advice の書き方

以下では、上記で扱った以外の advice に関連する事柄について紹介します:

pointcut の省略

(1.1rc1 OK)

今までの例で見てきたように、pointcut を直接的に指定して advice を書くこともできます:

public class Main {

	public static void main(String[] args) {
		new Main().method();
	}

	public void method() {
		System.out.println("method");
	}
}
public aspect MainAspect {

	before(): call( void Main.method() ) {
		System.out.println("before");
	}
}
java> java -cp dest aoptest.Main
before
method

複数の advice

(1.1rc1 OK)

ひとつのアスペクトに複数の advice を同時に書こともできます。

public class Main {
	public static void main(String[] args) {
		new Main().method();
	}

	public void method() {
		System.out.println("method");
	}
}
public aspect MainAspect {

	before() : call( void Main.method() ) {
		System.out.println("before");
	}

	after() : call( void Main.method() ) {
		System.out.println("after");
	}
}
java> java -cp dest aoptest.Main
before
method
after

クラスからの pointcut の定義の参照

(1.1rc1 OK)

pointcut の定義は、通常のクラスないにも書くことができます。アスペクト内でその pointcut を指定するには ClassName.pointcutName とします:

public class Main {

	public static void main(String[] args) {
		new Main().method();
	}

	public void method() {
		System.out.println("method");
	}

	pointcut methodCall() : call( void Main.method() );
}
public aspect MainAspect {

	before() : Main.methodCall() {
		System.out.println("before");
	}
}
java> java -cp dest aoptest.Main
before
method
一方 advice そのものは、通常のクラス内には、定義できません:
public class Main {

	public static void main(String[] args) {
		new Main().method();
	}

	public void method() {
		System.out.println("method");
	}

	// コンパイルエラー!
	before() : call( void Main.method() ) {
		System.out.println("before");
	}
}
しかし、アスペクト自体は、通常のクラス内に出来ます。次の節の例を見てください。

通常の Java クラスの中での advice

(1.1rc1 OK)

アスペクトはインナーアスペクトとして通常のクラス内に書くこともできます:

public class Main {

	public static void main(String[] args) {
		new Main().method();
	}

	public void method() {
		System.out.println("method");
	}

	private static aspect MainAspect {

		before(): call( void Main.method() ) {
			System.out.println("before");
		}
	}
}
java> java -cp dest aoptest.Main
before
method
ただし、クラス内で定義するアスペクトは static で宣言されていなければならないことに注意してください:
public class Main {

	// ... 上と同じ

	// static で宣言されていないのでコンパイルエラー!
	private aspect MainAspect {

		before(): call( void Main.method() ) {
			System.out.println("before");
		}
	}
}

更新履歴

todo