Java進階之 JDK動態代理與Cglib動態代理

小呂-ICE發表於2015-01-19
一、動態代理概述:

    與靜態代理對照(關於靜態代理的介紹 可以閱讀上一篇:JAVA設計模式之 代理模式【Proxy Pattern】),

    動態代理類的位元組碼是在程式執行時由Java反射機制動態生成。

    注意: 
      1、AspectJ是採用編譯時生成AOP代理類,具有更好的效能,但是需要使用特定的編譯器進行處理

      2、Spring AOP採用執行時生成AOP代理類,無需使用特定編譯器進行處理,但是效能相對於AspectJ較差


二、JDK動態代理 [對有實現介面的物件做代理]

    1、JDK動態代理中 需要了解的兩個重要的類或介面 [InvocationHandler 和 Proxy]

         ① InvocationHandler介面

public interface InvocationHandler { 
    public Object invoke(Object proxy,Method method,Object[] args) throws Throwable; 
} 
引數說明: 
Object proxy:指被代理的物件 
Method method:我們所要呼叫被代理物件的某個方法的Method物件
Object[] args:被代理物件某個方法呼叫時所需要的引數 

可以將InvocationHandler介面的子類想象成一個代理的最終操作類。

說明:每一個動態代理類都必須要實現InvocationHandler這個介面,並且每個代理類(Proxy)的例項都關聯到了一個handler,當我們通過代理物件呼叫一個方法的時候,這個方法的呼叫就會被轉發為由InvocationHandler這個介面的 invoke 方法來進行呼叫。同時在invoke的方法裡 我們可以對被代理物件的方法呼叫做增強處理(如新增事務、日誌、許可權驗證等操作)。

         ② Proxy類

 Proxy類是專門完成代理的操作類,可以通過此類為一個或多個介面動態地生成實現類,該類常用的呼叫方法如下:

 

newProxyInstance方法引數說明如下:

ClassLoader loader:類載入器,定義了由哪個ClassLoader物件來對生成的代理物件進行載入
Class<?>[] interfaces:得到被代理類全部的介面,如果我提供了一組介面給它,那麼這個代理物件就宣稱實現了該介面,這樣我就能呼叫這組介面中的方法了
InvocationHandler h:得到InvocationHandler介面的子類例項 


    2、JDK動態代理程式碼示例:

首先我們定義了一個Subject型別的介面:Subject.java

public interface Subject {
    public void visit();
}
接著定義一個介面的實現類,這個類就是我們示例中的被代理物件:RealSubject.java
/**
 * 被代理類
 * @author lvzb.software@qq.com
 *
 */
public class RealSubject implements Subject {

	@Override
	public void visit() {
	     System.out.println("I am 'RealSubject',I am the execution method");
	}

}
第三步 定義一個動態代理類(必須要實現 InvocationHandler 這個介面):DynamicProxy.java
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
 * JDK動態代理類
 * @author lvzb.software@qq.com
 *
 */
public class DynamicProxy implements InvocationHandler {

	// 我們要代理的真實物件(委託物件)
	private Object subject;
	
	// 構造方法,給我們要代理的真實物件賦初值
	public DynamicProxy(Object obj){
		this.subject = obj;
	}
	
	@Override
	public Object invoke(Object object, Method method, Object[] args)
			throws Throwable {
		// 在代理真實物件操作前 我們可以新增一些自己的操作
		System.out.println("before proxy invoke");
		
		// 當代理物件呼叫真實物件的方法時,其會自動的跳轉到代理物件關聯的handler物件的invoke方法來進行呼叫
		method.invoke(subject, args);
		
		// 在代理真實物件操作後 我們也可以新增一些自己的操作
		System.out.println("after proxy invoke");
		return null;
	}

}
最後代理測試類:Client.java
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

public class Client {

	public static void main(String[] args) {
		// 我們要代理的真實物件
		Subject realSubject = new RealSubject();
		// 我們要代理哪個真實物件,就將該物件傳進去,最後是通過該真實物件呼叫方法的
		InvocationHandler handler = new DynamicProxy(realSubject);
		/*
		 * 通過Proxy的newProxyInstance方法來動態建立我們的代理物件,我們來看看其三個引數<br/>
		 * 引數一:我們這裡使用handler這個類的ClassLoader物件來載入我們的代理物件<br/>
		 * 引數二:我們這裡為代理物件提供的介面是真實物件所實行的介面,表示我要代理的是該真實物件,這樣我就能呼叫這組介面中的方法了<br/>
		 * 引數三:我們這裡將這個代理物件關聯到了上方的 InvocationHandler 這個物件上
		 */
		Subject proxyInstance = (Subject)Proxy.newProxyInstance(handler.getClass().getClassLoader(), 
				RealSubject.class.getInterfaces(), 
				handler);
		
		System.out.println(proxyInstance.getClass().getName());
		proxyInstance.visit();

	}

}
執行->控制檯輸出結果如下:

com.sun.proxy.$Proxy0
before proxy invoke
I am 'RealSubject',I am the execution method
after proxy invoke

三、Cglib(Code Generation Library)動態代理 [對沒有實現介面的普通類做代理]

     1、概述:

         Cglib是一個優秀的動態代理框架,它的底層使用ASMJAVA位元組碼處理框架在記憶體中動態的生成被代理類的子類。使用CGLIB即使被代理類沒有實現任何介面也可以實現動態代理功能。但是不能對final修飾的類進行代理。

     2、原理:

         通過位元組碼技術為一個類建立子類,並在子類中採用方法攔截的技術攔截所有父類方法的呼叫。

         <JDK動態代理與CGLib動態代理均是實現Spring AOP的基礎>

     3、使用:

          使用Cglib前需要匯入以下兩個jar檔案:
          asm.jar – Cglib的底層實現。

        【cglib包的底層是使用位元組碼處理框架ASM來轉換位元組碼並生成新的類,所以cglib包要依賴於asm包】
          cglib.jar - Cglib的核心jar包。


     4、Cglib動態代理程式碼示例:

首先定義一個沒有實現介面的代理委託類:CglibRealSubject.java

/**
 * 沒有實現介面的代理委託類
 * @author lvzb.software@qq.com
 *
 */
public class CglibRealSubject{

	public void visit() {
		System.out.println("I am 'RealSubject',I am the execution method");
	}

}
接著定義一個Cglib動態代理類: CglibDynamicProxy.java

import java.lang.reflect.Method;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
/**
 * 使用cglib動態代理
 * @author lvzb.software@qq.com
 *
 */
public class CglibDynamicProxy implements MethodInterceptor {

	private Object target;
	
	/**
	 * 建立代理物件
	 * @param target 被代理的物件
	 * @return
	 */
	public Object getProxyInstance(Object target){
		this.target = target;
		// 宣告增強類例項
		Enhancer enhancer = new Enhancer(); 
		// 設定被代理類位元組碼,CGLIB根據位元組碼生成被代理類的子類
                enhancer.setSuperclass(this.target.getClass());  
                // 設定要代理的攔截器,回撥函式,即一個方法攔截   new MethodInterceptor()
                enhancer.setCallback(this); 
                // 建立代理物件 例項 
                return enhancer.create();  
	}
	
	@Override
	public Object intercept(Object obj, Method method, Object[] args,
			MethodProxy proxy) throws Throwable {
		// 在代理真實物件操作前 我們可以新增一些自己的操作
		System.out.println("前置代理,增強處理");
		
		proxy.invokeSuper(obj, args);
		
		// 在代理真實物件操作後 我們也可以新增一些自己的操作
		System.out.println("後置代理,增強處理");
		return null;
	}

}
最後測試客戶端類:CglibClient.java

public class CglibClient {

	public static void main(String[] args) {
		CglibDynamicProxy cglib = new CglibDynamicProxy();
		CglibRealSubject realSubject = (CglibRealSubject) cglib.getProxyInstance(new CglibRealSubject());
		realSubject.visit();
	}

}
執行->控制檯輸出結果如下:

前置代理,增強處理
I am 'RealSubject',I am the execution method
後置代理,增強處理

四、擴充套件學習

    關於什麼是AOP思想?Spring AOP實現原理?及什麼樣的系統業務場景我們可以使用到AOP這種動態代理技術?
    小呂推薦一篇非常值得閱讀和學習的文章: Spring AOP實現原理與CGLIB應用



—————————————————————————————————————

如果文章內容對您有幫助, 不要忘記 頂 一下,來支援一下哦!   

如果您對文章內容有任何疑問或有更好的見解, 可以通過評論留言或發郵件的方式聯絡我: 

ice_zhengbin@163.com


如果需要轉載,請註明出處,謝謝!!

—————————————————————————————————————



相關文章