ダイナミックエージェント(上)のエージェントを深く分析する方法

6080 ワード

動的エージェントについては、明示的に使用することは少ないかもしれませんが、SpringのInterceptor、さまざまなトランザクション管理といえば、よりよく知られています.間違いありません.これらは、下位実装で使用されている動的エージェントです.正確には、検証、リソース解放、ログ処理など、クラスの方法に動的に機能を追加したいと思っています.ほとんどがダイナミックエージェントを利用しています.
緩やかな遷移のために,まず静的エージェントについて述べる.
スタティツクエージェント
静的エージェントの考え方は簡単です.実際のオブジェクトのインスタンスをエージェントオブジェクトのインスタンスに配置し、エージェントオブジェクトメソッドを呼び出します.エージェントオブジェクトのメソッドが実際のオブジェクトを呼び出すメソッドは、トランザクション管理を例に、次のようにします.
    UserDao
package com.tgb.staticproxy;

public interface UserDao {

	public void add();
	public void deleteAll();
}

    UserDaoImpl
package com.tgb.staticproxy;

public class UserDaoImpl implements UserDao {

	public void add()
	{
		System.out.println("          ");
	}
	public void deleteAll()
	{
		System.out.println("      ");
	}
}

    UserDaoProxy
package com.tgb.staticproxy;

public class UserDaoProxy implements UserDao {

	UserDao userDao=null;
	public UserDaoProxy(UserDao userDao)
	{
		this.userDao=userDao;
	}
	
	public void add()
	{
		System.out.println("      ");
		userDao.add();
		System.out.println("       ");
	}
	public void deleteAll()
	{
		System.out.println("      ");
		userDao.deleteAll();
		System.out.println("       ");
	}
}

    Test
package com.tgb.staticproxy;

public class Test {

	/**
	 * @param args
	 */
	public static void main(String[] args) {

		UserDao userDao=new UserDaoImpl();
		UserDaoProxy userDaoProxy=new UserDaoProxy(userDao);
		
		//    
		userDaoProxy.add();
		System.out.println("..........   ..........");
		//    
		userDaoProxy.deleteAll();
		
	}

}

実行結果
      
          
       
..........   ..........
      
      
       

しかし、静的エージェントがトランザクションを管理する方法には大きな問題があり、各Daoクラスの各メソッドはトランザクションをオンとオフにする必要があります.コードの重複が深刻であるだけでなく、トランザクションはもともとビジネスに関連していないのに、結合されています.
ダイナミックエージェント
JDKダイナミックエージェント
同様に、トランザクション管理を例にとると、次のようになります.
    UserDao
package com.tgb.dynamicproxy;

public interface UserDao {

	public void add();
	public void deleteAll();
}

    UserDaoImpl
package com.tgb.dynamicproxy;

public class UserDaoImpl implements UserDao {

	@Override
	public void deleteAll() {
		System.out.println("        ");
	}
	@Override
	public void add() {
		System.out.println("          ");
	}

}

    Handler
package com.tgb.dynamicproxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class Handler implements InvocationHandler {

	private Object target;
	
	public Handler(Object target)
	{
		this.target=target;
	}
	@Override
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		//    
		before();
		//    
		method.invoke(target, args);
		//       
		after();
		
		return null;
	}
	public void before()
	{
		System.out.println("      ");
	}
	public void after()
	{
		System.out.println("       ");
	}
}

    Test
package com.tgb.dynamicproxy;

import java.lang.reflect.Proxy;

public class Test {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		try{
			UserDao impl=new UserDaoImpl();
			Handler handler=new Handler(impl);
			UserDao proxy=(UserDao)Proxy.newProxyInstance
				(impl.getClass().getClassLoader(), impl.getClass().getInterfaces(), handler);
			
			//    
			proxy.add();
			System.out.println("..........   ..........");
			//    
			proxy.deleteAll();
		}
		catch(Exception e)
		{
			e.printStackTrace();
		}
		
	}

}

実行結果
      
          
       
..........   ..........
      
        
       

JDKの動的エージェントは静的エージェント結合とコード重複の問題を克服したが,JDKのエージェントモードにはUserDaoがJDK動的エージェントを使用するためにインタフェースが必要であるなどの深刻な問題があり,これはJDK動的エージェントの範囲を大きく制限している.
cglibダイナミックエージェント
asmはバイトコードを動的に生成することができ,cglibはasmを再カプセル化し,cglibは動的エージェントのために生まれたわけではないが,その特性を利用して動的エージェントをうまく実現することができる.
UserDaoImpl
package com.tgb.cglib;

public class UserDaoImpl  {

	public void deleteAll() {
		System.out.println("        ");
	}

	public void add() {
		System.out.println("          ");
	}

}

CglibProxy
package com.tgb.cglib;

import java.lang.reflect.Method;

import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

public class CglibProxy implements MethodInterceptor  {
	
	private Object target;
    private CglibProxy(Object target){
       this.target = target;
    }
    //      
    @SuppressWarnings("unchecked")
	public static <T> T proxyTarget(T t){

       Enhancer en = new Enhancer();
       en.setSuperclass(t.getClass());
       en.setCallback((Callback) new CglibProxy(t));
       T tt = (T) en.create();
       return tt;

    }

    //    
    public Object intercept(Object obj, Method method, Object[] args,
           MethodProxy proxy) throws Throwable {
    	
       System.out.println("      ");
       Object o = method.invoke(target, args);
       System.out.println("       ");
       return o;
    }
}

Test
package com.tgb.cglib;

public class Test {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		//      
		UserDaoImpl impl=CglibProxy.proxyTarget(new UserDaoImpl());
		//    
		impl.add();
		System.out.println("..........   ..........");
		//    
		impl.deleteAll();
	}

}

実行結果
      
          
       
..........   ..........
      
        
       

今回のUserDaoImplでは,インタフェースインタフェースによる動的エージェントの機能は実装されていないことがわかる.
まとめ
このブログはもともとJDKとcglibダイナミックエージェントのソースコードの紹介を書くつもりでしたが、書いてあるうちにエージェントの紹介にどのようなタイプと実現方法があるのか、さらに書くと少し長いので、次のブログに置いて説明します.