Spring Framework 入門記 AOPその9 After Returning Advice

前回はなんとも中途半端に終わらせてしまいました.心より恥じる.
気を取り直して今回は,「5.3.2.4. After Returning advice」へ進みます.
After Returning adviceは,メソッドJoinpointが例外をスローすることなくリターンした場合に呼び出されるAdviceです.
After Returning adviceを実装するには,AfterReturningAdviceというinterfaceimplementsします.このinterfaceは,次のメソッドを持っています.

  • afterReturning(Object, Method, Object[], Object)

引数はそれぞれ,Joinpointの戻り値,Joinpointメソッド,Joinpointの引数,Joinpointのターゲットであるオブジェクト,です.
Joinpointの戻り値は普通にObject型なので,不変オブジェクトの場合は変更できませんね.コレクションなんかだと変更できるかも(unmodifiable〜でなければ).でも,変更するならやはりAround adviceを使えということだと思われます.
After Returning adviceの使い道ですぐに思いついたのは事後条件です.とはいえ,これもまたAround adviceがあれば済んでしまうというか,@preを実現したければAround adviceでなくてはならない感じなんで,やっぱり使い道が... 無念だ.
ともかくですね,近頃は戻り値nullが話題になっている様子.ということで,戻り値nullなんて極悪コードを跳ね飛ばす事後条件をAfter Returning adviceとして作ってみることにしましょう.
こんな感じ.

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

public class NullNotAllowAfterAdvice implements AfterReturningAdvice {
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        assert(returnValue != null);
    }
}

次にこれを適用するターゲット.

package study;

public class Foo {
    public String good() {
        return "nullなんて戻しません.";
    }
    public String bad() {
        return null;
    }
}

そして定義ファイル.

<?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.NullNotAllowAfterAdvice">
    </bean>
</beans>

最後に実行用のクラス.

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");
            System.out.println(foo.good());
            System.out.println(foo.bad());

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

これを実行!!

nullなんて戻しません.
null

しまった,assertを有効にしていませんでした.心より恥じる*1
-ea:study...を付けて改めて実行!!!

nullなんて戻しません.
java.lang.AssertionError
    at study.NullNotAllowAfterAdvice.afterReturning(NullNotAllowAfterAdvice.java:7)
    at org.springframework.aop.framework.adapter.AfterReturningAdviceInterceptor.invoke(AfterReturningAdviceInterceptor.java:46)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:134)
    at org.springframework.aop.framework.Cglib2AopProxy.intercept(Cglib2AopProxy.java:144)
    at study.Foo$$EnhancerByCGLIB$$b893bd2a.bad()
    at study.Main.main(Main.java:16)

おっけー!

*1:演出じゃありませんよ,本当にやっちゃったんです.決して一日一Zaizenのためにわざとやったのではありません.