Spring中のダイナミックエージェント

6521 ワード

1.Spring AOPは目標に対して代理対象の作成を行います.Spring AOPは動的代理に基づいています.動的代理機構があります.JDKダイナミックエージェントとCGLOIBダイナミックエージェントがあります. 
ダイナミックプロキシ:仮想マシン内部で、動作時にプロキシ類を動的に生成します.本当に存在するクラスではありません.
一般書式:
Proxy$$ (Proxy$$Customer)
静的エージェント:実際にプロキシ類が存在する(例えば、struts 2 Actionのプロキシ類アクションProxy、struts 2のスクリーンセーバー)
 
JDKダイナミックエージェント
JDKダイナミックエージェントは、ターゲットオブジェクトのインターフェースに対してエージェントを行い、インターフェースの実現クラスを動的に生成する. !(インターフェースが必要です.)
1、インターフェースに対して代理を生成しなければならない.
2、Proxy類を採用し、newProxyInstance方法を通じて代理対象を作成する.ターゲットインターフェースをプロキシするstatic ObjectnewProxyInstance(ClassLoader loader, Class>[] interfaces, InvocationHandler h)          指定されたインターフェースのプロキシクラスの例を返します.このインターフェースは、方法を指定された呼び出し処理プログラムに割り当てることができます.
この方法は三つのパラメータを受信する.
  (1)対象対象となるキャパシタ
  (2)対象オブジェクトの実装インターフェース
  (3)プロキシ後の処理プログラムInvocationHandlerは、プロキシ例の呼び出し処理プログラムで実現されるインターフェースです.
 
方法は、指定されたクラスのキャリアによって定義され、指定されたインターフェースを実現するプロキシクラスの指定された呼び出し処理プログラムのプロキシの例を返します.
3、InvocationHandlerインターフェースにおけるinvoke方法を実現し、ターゲットオブジェクトの各メソッド呼び出し時にinvokeを実行します.
 
コードの実装:
インターフェースに対して代理を行う:
//  (         )
public interface ICustomerService {
	//  
	public void save();
	//  
	public int find();
}

//   
public class CustomerServiceImpl implements ICustomerService{

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

	public int find() {
		System.out.println("       。。。。。");
		return 100;
	}
}
インターフェースに対して動的エージェントを実現する:
  jdk      1:
public class JdkProxyFactory{
	//    
	private Object target;
	//  target    
	public JdkProxyFactory(Object target) {
		this.target = target;
	}
	
	public Object getProxyObject(){
		//  1:         
		//  2:         
		//  3:      
		/**   :     new InvocationHandler(),     */
		return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler(){

			public Object invoke(Object proxy, Method method, Object[] args)
					throws Throwable {
				//        ,        
				if(method.getName().equals("save")){
					writeLog();
				}
				//           
				Object object = method.invoke(target, args);//           ,              
				return object;
			}
			
		});
	}
	
	//    
	private static void writeLog(){
		System.out.println("    :    。。。");
	}

}

//         2:              ,  InvocationHandler  
public class JdkProxyFactory implements InvocationHandler{
	//    
	private Object target;
	//  target
	public JdkProxyFactory(Object target) {
		this.target = target;
	}
	
	public Object getProxyObject(){
		//  1:         
		//  2:         
		//  3:      
		/**   :              ,  InvocationHandler  */
		return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),this);
	}
	
	//    
	private static void writeLog(){
		System.out.println("    :    。。。");
	}

	//  1:    
	//  2:       
	//  3:        
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		//        ,        
		if(method.getName().equals("save")){
			writeLog();
		}
		//           
		Object object = method.invoke(target, args);//           ,        
		return object;
	}

}

テスト:
 //  :      ,            ,          。
	//JDK    :     (     ,      !)
	@Test
	public void testJdkProxy(){
		//target(    )
		ICustomerService target = new CustomerServiceImpl();
		//         
		JdkProxyFactory jdkProxyFactory = new JdkProxyFactory(target);
		//  Proxy Object    :                     
		ICustomerService proxy = (ICustomerService)jdkProxyFactory.getProxyObject();
		//         
		proxy.save();
		System.out.println("————————————————————————————————————————");
		proxy.find();
	}
JDKダイナミックエージェントの欠点:インターフェースエージェントだけに向かって直接ターゲットクラスを代理することはできません.インターフェイスがないとJDKエージェントは使えません.
 
CGLOIBエージェント
Cglibエージェントの導入は、クラスの直接代理問題(代理子クラスを生成する)を解決するために、インターフェースがなくても代理できます.このプロキシ方式は、対応するjarパッケージを必要とするが、導入する必要はない.Spring coreパッケージは既にcglibを含んでいますので、cglib依存のasmのカバン(ダイナミックバイトコードの操作クラス)も同時に含んでいます.
 
1.インターフェースがない種類の直接代理について:
//      
public class ProductService {
	public void save() {
		System.out.println("     。。。。。");
		
	}

	public int find() {
		System.out.println("       。。。。。");
		return 99;
	}
}
2.cglibを使って代理する.
public class CglibProxyFactory implements MethodInterceptor {

	private Object target;

	public CglibProxyFactory(Object target) {
		this.target = target;
	}

	public Object getProxyObject() {
		//       
		Enhancer enhancer = new Enhancer();
		
		//      
		enhancer.setSuperclass(target.getClass());
		
		//      
		enhancer.setCallback(this);	
		
		//        
		Object object = enhancer.create();
		
		return object;

	}

	
	//    (       )
	//  1:    
	//  2:         
	//  3:            
	//  4:         
	public Object intercept(Object proxy, Method method, Object[] args,
			MethodProxy methodProxy) throws Throwable {
		//        ,        
		if(method.getName().equals("save")){
			writeLog();
		}
		//           
		Object object = method.invoke(target, args);//           ,        
		return object;
	}

//     
	public static void writeLog() {
		System.out.println("    ");
	}

}
テスト:
//cglib    :     (      )      
	@Test
	public void testCglibProxy(){
		//target  :
		ProductService target = new ProductService();
		//weave  ,  proxy    
		//      ,    
		CglibProxyFactory cglibProxyFactory = new CglibProxyFactory(target);
		//  proxy:  :     
		//    ,             
		ProductService proxy=(ProductService) cglibProxyFactory.getProxyObject();
		//         
		proxy.save();
		System.out.println("————————————————————————————————————————");
		proxy.find();
		
	}
まとめ:
springは運行期間中に動的代理オブジェクトを生成します.特別なコンパイラは必要ありません.
springには2つの代理方式があります.
    1.ターゲットがいくつかのインターフェースを実現したら、springはJDKのjava.lang.reflect.Proxy類代理を使用する.
    2.ターゲットが何のインターフェースも実現していない場合、springはCGLOIBライブラリを使用してターゲットのサブクラスを生成する.
この方式を使う時は注意が必要です.
   1.インターフェース作成エージェントはクラス作成代理より優れています.より緩やかな結合が生じるため、springはデフォルトではJDKエージェントを使用しています.クラスエージェントは、レガシーシステムまたはインターフェースを実装することができないサードパーティクラスのライブラリであり、同様に通知されることができ、この方法はバックアップスキームであるべきである.
    2.finalと表記する方法は通知されない.スプリングはターゲットクラスのためにサブクラスを生成します.通知が必要な方法はすべて複写され、編入されることを通知します.finalメソッドは書き換えが許されない.
3.springは方法接続点のみをサポートします.属性アクセスポイントを提供しません.springの観点は属性ブロックがパッケージを破壊したということです.対象に向けた概念は対象が自分で仕事を処理し、他のオブジェクトはメソッドでしか呼び出しられない結果です.