目次

対象ソースファイル

ソースはこんな感じ。
public class HelloAspectWorld? { 
 
        public static void main(String [] args){
                new HelloAspectWorld?().sayhello();
        }

        void sayhello(){
                System.out.println("Hello World");
        }
}
ajcで作ったクラスをjodeで戻してみた。
あとでバイトコードを出しているが、そっちから判断できる通りのソースになってる。
public class HelloAspectWorld?
{
    public static void main(String[] args) {
        HelloAspectWorld? helloaspectworld = new HelloAspectWorld?();
        try {
            Trace.aspectOf().ajc$before$Trace$1$705b637b();
            helloaspectworld.sayhello();
        } catch (Throwable throwable) {
        // ここの処理は確かにbytecode上、合ってそうだが、何でこうなってるのか意味不明
            Trace.aspectOf().ajc$after$Trace$2$705b637b();
            throw throwable;
        }
        Trace.aspectOf().ajc$after$Trace$2$705b637b();
    }
    
    void sayhello() {
        System.out.println("Hello World");
    }
}

で、Aspectのクラスはこんな感じ。

aspect Trace {
        pointcut atSayHello(): call(void HelloAspectWorld?.sayhello());

        before(): atSayHello() {
                System.out.println("[Before sayhello()]");
        }
        after(): atSayHello() {
                System.out.println("[After sayhello()]");
        }
}
こっちもjodeで戻して?みた。
jodeの逆コンパイルコードで、生成ミスってる所は適当に修正済
こちらはテストコードを試した時は気にならなかったが、Aspectはクラスロードの時点で、Aspectのインスタンスを singletonで用意してるコードになっているようだ。

import org.aspectj.lang.NoAspectBoundException?;

public class Trace
{
    private static Throwable ajc$initFailureCause;
    public static Trace ajc$perSingletonInstance;
    
    static {
        try {
            ajc$postClinit();
        } catch (Throwable throwable) {
            ajc$initFailureCause = throwable;
        }
    }
    
    Trace() {
        /* empty */
    }
    
    public void ajc$before$Trace$1$705b637b() {
        System.out.println("[Before sayhello()]");
    }
    
    public void ajc$after$Trace$2$705b637b() {
        System.out.println("[After sayhello()]");
    }
    
    public static Trace aspectOf() {
        Trace trace = ajc$perSingletonInstance;
        if (trace != null)
            return trace;
        throw new NoAspectBoundException?("Trace", ajc$initFailureCause);
    }
    
    public static boolean hasAspect() {
        if (ajc$perSingletonInstance != null)
            return true;
        return false;
    }
    
    private static void ajc$postClinit() {
        ajc$perSingletonInstance = new Trace();
    }
}

コンパイルと実行

コンパイルは相当微妙で、こんな感じ。 AspectJ コンパイラは 1.2版で配布されている。
コンパイル時にCLASSPATHに同梱の aspectjrt.jarが必須。 aspectjrt.jarが無いと、ajc自体が動かない。
 % ajc *.java
ちなみに ajcもそうだが、配布パッケージ内のコマンドはshell scriptで実体は aspectjtools.jarに あるクラスのpublic static void main()を実行するようになってる。ajcの場合はこれ。
"$JAVA_HOME/bin/java" -classpath" $ASPECTJ_HOME/lib/aspectjtools.jar:$JAVA_HOME/lib/tools.jar:$CLASSPATH" -Xmx64M org.aspectj.tools.ajc.Main "$@"
注意:何でそうしてるのか分からないが、Aspectのファイルのsuffixは.javaで無いとajcのコンパイル対象にならないようだ。

バイトコードを見てみる

予想される動作からすると、バイトコードに無理矢理 Aspect 呼び出しを差し込んでるだろうって事で、サクサクバイトコードを見てみる。
どうも違うっぽい気がしてきたので、後で調査。
ちなみにバイトコードは javap -c じゃなく、D-Javaの出力。
まずはajcで作成した物。
Major version: 46
Minor version: 0

public synchronized class HelloAspectWorld? extends Object
{

Method public void <init>()
>> max_stack=1, max_locals=1 <<
    0 aload_0
    1 invokenonvirtual #9 <Method Object.<init>():void>
    4 return
Local variable table:
  start length   slot
     0      5      0  this:HelloAspectWorld?
Line number table:
    pc   line
     0      6

Method public static void main(String[])
>> max_stack=2, max_locals=2 <<
    0 new #2 <Class HelloAspectWorld?>
    3 dup
    4 invokenonvirtual #16 <Method HelloAspectWorld?.<init>():void>
    7 invokestatic #43 <Method Trace.aspectOf():Trace>
   10 invokevirtual #46 <Method Trace.ajc$before$Trace$1$705b637b():void>
   13 invokevirtual #19 <Method HelloAspectWorld?.sayhello():void>
   16 goto 28
   19 astore_1
   20 invokestatic #43 <Method Trace.aspectOf():Trace>
   23 invokevirtual #49 <Method Trace.ajc$after$Trace$2$705b637b():void>
   26 aload_1
   27 athrow
   28 nop
   29 invokestatic #43 <Method Trace.aspectOf():Trace>
   32 invokevirtual #49 <Method Trace.ajc$after$Trace$2$705b637b():void>
   35 nop
   36 return
Exception table:
   from   to  target type
     7    16    19   <Class Throwable>
Local variable table:
  start length   slot
     0     37      0  args:String[]
Line number table:
    pc   line
     0      9
    36     10

Method void sayhello()
>> max_stack=2, max_locals=1 <<
    0 getstatic #27 <Field System.out:PrintStream?>
    3 ldc #29 <String "Hello World">
    5 invokevirtual #35 <Method PrintStream?.println(String):void>
    8 return
Local variable table:
  start length   slot
     0      9      0  this:HelloAspectWorld?
Line number table:
    pc   line
     0     13
     8     14
}
んで次は javacで作成した物
Major version: 46
Minor version: 0

public synchronized class HelloAspectWorld? extends Object
{

Method public void <init>()
>> max_stack=1, max_locals=1 <<
    0 aload_0
    1 invokenonvirtual #1 <Method Object.<init>():void>
    4 return
Line number table:
    pc   line
     0      6

Method public static void main(String[])
>> max_stack=2, max_locals=1 <<
    0 new #2 <Class HelloAspectWorld?>
    3 dup
    4 invokenonvirtual #3 <Method HelloAspectWorld?.<init>():void>
    7 invokevirtual #4 <Method HelloAspectWorld?.sayhello():void>
   10 return
Line number table:
    pc   line
     0      9
    10     10

Method void sayhello()
>> max_stack=2, max_locals=1 <<
    0 getstatic #5 <Field System.out:PrintStream?>
    3 ldc #6 <String "Hello World">
    5 invokevirtual #7 <Method PrintStream?.println(String):void>
    8 return
Line number table:
    pc   line
     0     13
     8     14
}

Last-modified: 2006-01-13 17:39:31