あのてーしょんなの

いまさらながらにJavaSE5で導入されたアノテーションを独自に作っていぢってみようと。
いままであまり必要ないかなぁとか思ってたんだけど、AOPと組み合わせると以外に使えるかも?てなわけで逝ってみようw
サンプルの環境として普通にやってはいまひとつ自分のためにならんので、SpringのAOPも使ってみる。

package sample.aop;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Message {
    String value() default "";
}

パッケージが怪しいけど^^;
実行時にリフレクションを使って参照したいので「@Retention」をつけて、Messageアノテーションはメソッドのみに付けれるてことで「@Target」で指定。
メソッドをvalue()にしておくと、省略して「@Message("hoge")」と書けると。
次にAOPで割り込みを書けるインタセプタを作ってみた。

package sample.aop;

import java.lang.reflect.Method;
import org.springframework.aop.MethodBeforeAdvice;

public class MessageAdvice implements MethodBeforeAdvice {
    public void before(Method arg0, Object[] arg1, Object arg2) throws Throwable {
        Message mes = arg0.getAnnotation(Message.class);
        if (mes != null) {
            System.out.println("aop insert value = [" + mes.value() + "]");
        } else {
            throw new IllegalArgumentException("annotation no define");
        }
    }
}

標準的なBeforeAdvice。
アノテーションが付いてるかは「Method#getAnnotation(Class c)」で取得。
あとはクライアントとなる実行クラスを作成して、実行してみるだけと。
Springの設定ファイルは省略w

package sample.service;

import sample.aop.Message;

public interface SampleService {
    @Message("hello")
    void dummyMethod();
}
package sample.service;

public class SampleServiceImpl implements SampleService {
    public void dummyMethod() {
        System.out.println("dummyMethod() called");
    }
}
package sample;

import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import sample.service.SampleService;

public class SampleClient {
    public static void main(String[] args) {
        try {
            BeanFactory bf = 
                new ClassPathXmlApplicationContext("/applicationContext.xml");
            SampleService service = (SampleService) bf.getBean("sampleService");
            service.dummyMethod();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

実行結果はこんな感じ。

aop insert value = [hello]
dummyMethod() called


やってみるとなぁんだ、簡単だね?と思いきや実はこうなるまでにおおはまり。
というのも、MessageアノテーションをSampleServiceImplのメソッドに付けたらうごかなぁい;;
インタフェースベースで動いてるから、アノテーションはそっちに付けないとダメ?
とはいえ変な感じもするし…。
ここらはもう少し調べてみないとだめかorz


追記:
原因判明。
インタセプタで引数のMethodクラスからアノテーションを取得してたからだった模様。
Methodクラスの中身を良く見たら、SampleServiceクラス。
args2の中身が実体?であるSampleServiceImplが入ってたんで、こっちからメソッドを取得してみたらちゃんとアノテーションも取得できたyp!

package sample.aop;

import java.lang.reflect.Method;
import org.springframework.aop.MethodBeforeAdvice;

public class MessageAdvice implements MethodBeforeAdvice {
    public void before(Method arg0, Object arg1, Object arg2) throws Throwable {
//        Message mes = arg0.getAnnotation(Message.class);
        Message mes = arg2.getClass().getMethod(arg0.getName(), new Class{}).getAnnotation(Message.class);
        if (mes != null) {
            System.out.println("aop insert value = [" + mes.value() + "]");
        } else {
            throw new IllegalArgumentException("annotation no define");
        }
    }
}

無理やりだけど、こんなで取れましたとさ。