AOPの定番 Spring Frameworkの動的プロキシ部分を見る

前回のAOPの基礎 動的プロキシを触るをふまえて、 Spring FrameworkAOP部分(spring-aop-2.5.jar)を見てみます。


注目するクラスは3つ

  1. org.springframework.aop.framework.JdkDynamicAopProxy(java.lang.reflect.InvocationHandlerを実装した動的プロキシ)
  2. org.springframework.aop.framework.ReflectiveMethodInvocation(実行対象メソッドとそのインターセプターを呼び出す)
  3. org.springframework.aop.framework.ProxyFactoryBean(Target,Interface,Interceptor情報を保持する)


注目する処理は3つ

  1. org.springframework.aop.framework.JdkDynamicAopProxy#invoke()
    • 実行対象メソッドとインターセプターを実行
  2. org.springframework.aop.framework.ReflectiveMethodInvocation#proceed()
  3. それぞれのインターセプター#invoke()
    • インターセプターはinvoke()の中で実行対象メソッドの前後処理を行い、引数で受け取ったReflectiveMethodInvocationのproceed()を呼び出す。


つまり処理の流れは?
proceedメソッドとinvokeメソッドが連鎖することで上記2〜3が、全てのインターセプターの前処理が終わるまでループして、最後に実行対象が呼び出される。その後上記の逆順でインターセプターの後処理が実行される。
という感じ。


参考インターセプターは例えば以下がおすすめ。

  • ログ出力インターセプター(org.springframework.aop.interceptor.AbstractTraceInterceptorのサブクラス)
    • org.springframework.aop.interceptor.SimpleTraceInterceptor
  • トランザクション管理インターセプター(spring-tx-2.5.jar)
    • org.springframework.transaction.interceptor.TransactionInterceptor


それと抑えておきたいのは、「AOP Alliance」というAOPの標準化規約。
AOPフレームワークは、この規約に則って作成しましょうという規約でSpring FrameworkSeasar2などはこの規約通りに作られている。
Spring Frameworkでaopalliance-1.0.jarのインターフェイスを実装しているところは、例えば以下の場所。

  • org.springframework.aop.framework.ReflectiveMethodInvocationがorg.aopalliance.intercept.MethodInvocationを実装している。
  • 各インターセプターがorg.aopalliance.intercept.MethodInterceptorを実装している。


ということで、AOPの基礎 動的プロキシを触るのソースをSpring FrameworkAOP部分っぽく実装してみましょう。
かなりはしょっていて、しかもコメント少ないですがメソッド名などはそのままにしてあります。
AOP Allianceにも微妙に則っていますのでAOP Alliance (Java/J2EE AOP standard) download | SourceForge.netからaopalliance.jarをダウンロードしましょう。


メイン実行クラス

import java.util.ArrayList;
import java.util.List;
import org.aopalliance.intercept.MethodInterceptor;

public class Aop002 {

    public static void main(String... args) throws Exception {

        // インターフェイス名
        String proxyInterfaces = "MyService";
        // 実装クラス名
        String targetName = "MyServiceImpl";
        // インターセプター名
        String[] interceptorNames = {
                "BarMethodInterceptorA", 
                "BarMethodInterceptorB"};
        
        ClassLoader loader = Aop002.class.getClassLoader();
        
        // 実装クラス、インターフェイス、インターセプター情報の構築
        BarProxyFactoryBean config = new BarProxyFactoryBean();
        config.setTarget(loader.loadClass(targetName).newInstance());
        config.setInterfaces(new Class[] {loader.loadClass(proxyInterfaces)});
        List <MethodInterceptor>interceptorList = new ArrayList<MethodInterceptor>();
        for (String interceptorName : interceptorNames) {
            interceptorList.add((MethodInterceptor)loader.loadClass(interceptorName).newInstance());
        }
        config.setInterceptors(interceptorList);
        
        // 動的プロキシ生成
        MyService service = (MyService)new BarJdkDynamicAopProxy(config).getProxy(loader);

        // メイン処理メソッド実行
        service.execute();
    }
}


サービスインターフェイス


サービスインターフェイスの実装クラス


簡略JdkDynamicAopProxy

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.List;

public class BarJdkDynamicAopProxy implements InvocationHandler {
  private final BarProxyFactoryBean advised;
  
  public BarJdkDynamicAopProxy(BarProxyFactoryBean config) {
      this.advised = config;
  }
  
  /* (non-Javadoc)
   * @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object, java.lang.reflect.Method, java.lang.Object[])
   */
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    
    List chain = this.advised.getInterceptors();
    Object target = this.advised.getTarget();
    Object retVal = null;
    if(chain.isEmpty()) {
        retVal = method.invoke(target, args);
    } else {
        BarReflectiveMethodInvocation invocation 
            = new BarReflectiveMethodInvocation(proxy, target, method, args, chain);
        retVal = invocation.proceed();
    }
    return retVal;
  }

  public Object getProxy(ClassLoader classLoader) {
    return Proxy.newProxyInstance(
            classLoader, 
            this.advised.getInterfaces(), 
            this);
  }
}


簡略ReflectiveMethodInvocation

import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Method;
import java.util.List;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

public class BarReflectiveMethodInvocation implements MethodInvocation {
  
  protected final Object proxy;
  protected final Object target;
  protected final Method method;
  protected Object[] arguments;    
  private int currentInterceptorIndex = -1;    
  protected final List interceptors;
  
  public BarReflectiveMethodInvocation(
          Object proxy, 
          Object target, 
          Method method, 
          Object[] arguments, 
          List interceptors) {

    this.proxy = proxy;
    this.target = target;
    this.method = method;
    this.arguments = arguments;
    this.interceptors = interceptors;
  }

  /* (non-Javadoc)
   * @see org.aopalliance.intercept.Joinpoint#proceed()
   */
  public Object proceed() throws Throwable {
    
    if (this.currentInterceptorIndex == this.interceptors.size() - 1) {
        return this.method.invoke(this.target, this.arguments);
    }
    Object interceptorOrInterceptionAdvice =
        this.interceptors.get(++this.currentInterceptorIndex);        
    return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
  }

  /* (non-Javadoc)
   * @see org.aopalliance.intercept.MethodInvocation#getMethod()
   */
  public Method getMethod() {
    return this.method;
  }

  /* (non-Javadoc)
   * @see org.aopalliance.intercept.Invocation#getArguments()
   */
  public Object[] getArguments() {
    return this.arguments;
  }

  /* (non-Javadoc)
   * @see org.aopalliance.intercept.Joinpoint#getStaticPart()
   */
  public AccessibleObject getStaticPart() {
    return this.method;
  }

  /* (non-Javadoc)
   * @see org.aopalliance.intercept.Joinpoint#getThis()
   */
  public Object getThis() {
    return this.target;
  }
}


簡略ProxyFactoryBean

import java.util.ArrayList;
import java.util.List;

public class BarProxyFactoryBean {

  private List interceptors = new ArrayList();
  private Object target = null;
  private Class[] interfaces;
  
  public List getInterceptors() {
    return interceptors;
  }
  public void setInterceptors(List interceptors) {
    this.interceptors = interceptors;
  }
  public Class[] getInterfaces() {
    return interfaces;
  }
  public void setInterfaces(Class... interfaces) {
    this.interfaces = interfaces;
  }
  public Object getTarget() {
    return target;
  }
  public void setTarget(Object target) {
    this.target = target;
  }
}


インターセプターA

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

public class BarMethodInterceptorA implements MethodInterceptor {

  public Object invoke(MethodInvocation invocation) throws Throwable {
    
    Object returnValue = null;
    System.out.println("BarMethodInterceptorA で" + invocation.getMethod().getName() + "開始ログ");
    
    returnValue = invocation.proceed();

    System.out.println("BarMethodInterceptorA で" + invocation.getMethod().getName() + "終了ログ");
    return returnValue;
  }
}


インターセプターB

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

public class BarMethodInterceptorB implements MethodInterceptor {

  public Object invoke(MethodInvocation invocation) throws Throwable {
    
    Object returnValue = null;
    System.out.println("BarMethodInterceptorB で" + invocation.getMethod().getName() + "開始ログ");
    
    returnValue = invocation.proceed();

    System.out.println("BarMethodInterceptorB で" + invocation.getMethod().getName() + "終了ログ");
    return returnValue;
  }
}


とかなり長くなりましたが、これでAop002を実行すると以下のように出力されます。

BarMethodInterceptorA でexecute開始ログ
BarMethodInterceptorB でexecute開始ログ
MyServiceImpl で execute処理
BarMethodInterceptorB でexecute終了ログ
BarMethodInterceptorA でexecute終了ログ


新たにBarMethodInterceptorCをつくり、Aop002の以下の部分を修正すれば新たにBarMethodInterceptorCも実行されるようになりますね。

// インターセプター名
  String[] interceptorNames = {
      "net.jemomo.aop.BarMethodInterceptorA", 
      "net.jemomo.aop.BarMethodInterceptorB", 
      "net.jemomo.aop.BarMethodInterceptorC"};

BarMethodInterceptorCを追加すればこのように出力されるようになります。

BarMethodInterceptorA でexecute開始ログ
BarMethodInterceptorB でexecute開始ログ
BarMethodInterceptorC でexecute開始ログ
MyServiceImpl で execute処理
BarMethodInterceptorC でexecute終了ログ
BarMethodInterceptorB でexecute終了ログ
BarMethodInterceptorA でexecute終了ログ