Spring Framework 入門記 AOPその7 Before Advice

今回はBefore adviceについて学習します.
Before adviceは,Joinpointの前に呼び出されるAdviceで,Springで使用できるAdviceの中でももっとも単純なものだそうです.
Before adviceを実装するには,MethodBeforeAdviceというinterfaceimplementsします.このinterfaceAOP Allianceのものではなく,Spring独自のものです.
MethodBeforeAdviceは,次のメソッドを一つだけ持っています.

  • before(Method, Object[], Object)

最初の引数はJoinpointであるメソッド,2番目の引数はJoinpointであるメソッドの引数,最後の引数はAdviceが適用されるターゲットです.Around Adviceと違って,メソッドJoinpointをproceedする必要はありません.というか,できません.それがBefore adviceなので.というわけで,Around adviceのようにMethodInvocationを受け取ったりはしないのですが,それゆえシグネチャ的にはAround adviceよりも複雑に見えてしまうのはなんだかなーという感じ?
ともかく,メソッドJoinpointの引数を受け取っていますから,そこで細工をしたりとかいろいろできることはありそうです.
そこで,引数が文字列型だったら置換するようなBefore adviceを作ってみましょう.

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

public class RegexReplaceAdvice implements MethodBeforeAdvice {
    private String pattern;
    private String replacement;
    public void before(Method m, Object[] args, Object target) throws Throwable {
        for (int i = 0; i < args.length; ++i) {
            if (args[i] instanceof String) {
                args[i] = ((String) args[i]).replaceAll(pattern, replacement);
            }
        }
    }
    public String getPattern() {
        return pattern;
    }
    public void setPattern(String pattern) {
        this.pattern = pattern;
    }
    public String getReplacement() {
        return replacement;
    }
    public void setReplacement(String replacement) {
        this.replacement = replacement;
    }
}

このBefore Adviceは,メソッドJoinpointのString型の引数について,プロパティで指定されたpatternreplacementに置換します.
次にこのBefore Adviceを適用するクラス.引数の文字列を出力するだけのメソッドを一つ持っています.

package study;

public class Foo {
    public void print(String text) {
        System.out.println(text);
    }
}

そして定義ファイル.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
    <bean id="foo" class="org.springframework.aop.framework.ProxyFactoryBean">
        <property name="target">
            <bean class="study.Foo"/>
        </property>
        <property name="interceptorNames"><value>advice</value></property>
    </bean>

    <bean id="advice" class="study.RegexReplaceAdvice">
        <property name="pattern"><value>恋</value></property>
        <property name="replacement"><value>Seasar</value></property>
    </bean>
</beans>

最初,MethodBeforeAdviceInterceptorではないので,ProxyFactoryBeaninterceptorNamesプロパティに書いていいのか不安になりましたが大丈夫なようです.マジでプロパティ名を変えてくれ.
最後に実行用のクラス.

package study;
import org.springframework.beans.factory.access.BeanFactoryLocator;
import org.springframework.beans.factory.access.BeanFactoryReference;
import org.springframework.context.ApplicationContext;
import org.springframework.context.access.ContextSingletonBeanFactoryLocator;

public class Main {
    public static void main(String[] args) {
        try {
            BeanFactoryLocator locator = ContextSingletonBeanFactoryLocator.getInstance();
            BeanFactoryReference ref = locator.useBeanFactory("context");
            ApplicationContext context = (ApplicationContext) ref.getFactory();

            Foo foo = (Foo) context.getBean("foo");
            foo.print("恋のからさわぎ");

            ref.release();
        }
        catch (Exception e) {
            e.printStackTrace();
        }
    }
}

これを実行すると...

Seasarのからさわぎ

サクっと出来ました.めでたしめでたし.