Java代理實現方式詳解

1. 簡介

代理模式是常用的Java設計模式,其主要作用是爲其他對象提供一種代理以控制對這個對象的訪問。在某些情況下,一個對象不想或者不能直接引用另一個對象,而代理對象可以在調用對象和被調用對象起到中介的作用,代理模式的思想是爲了提供額外的處理或者不同的操作。

代理模式中,基本上有Subject角色,RealSubject角色,Proxy角色。其中:Subject角色負責定義RealSubject和Proxy角色應該實現的接口;RealSubject角色用來完成業務服務功能;Proxy角色負責調用RealSubject對應的方法完成業務功能。

按照代理的創建時期,代理可以分爲兩種:

  • 靜態代理:由程序創建或特定工具自動生成源代碼,再對其編譯。在程序運行前,代理類的.class文件就已經存在了。
  • 動態代理:程序創建其不知道需要創建哪些類的代理類,代理類的定義,.class文件的生成需要在程序運行期動態創建。

2. 靜態代理


在代碼階段確定了代理關係,Proxy類通過jdk編譯器編譯成class文件,當系統運行時,此class已經存在了。


下面簡單實現靜態代理:

a. 業務接口類

<span style="font-size:14px;">public interface Count {
	
	public void count();
}</span>

b. 業務實現類

<span style="font-size:14px;">public class CountImpl implements Count{

	@Override
	public void count() {
		System.out.println("計算當前賬戶餘額。。。。");
	}

}</span>

c. 代理類

<span style="font-size:14px;">/**
 * <pre>
 * 項目名: java-proxy-statics
 * 類名: CountProxy.java
 * 類描述: 靜態代理實現:由程序員創建或特定工具自動生成源代碼,再對其編譯。在程序運行前,代理類的.class文件就已經存在了
 * 備註: 每個代理類只能爲一個接口服務,這樣就會導致代理類太多,代理類和實際類除了類名不一致,其他的都一致,重複代碼太多,過於臃腫
 * 創建日期:2015-6-26
 * 創建時間:上午9:21:00
 * </pre>
 */
public class CountProxy implements Count{

	private Count count;
	
	public CountProxy(Count count){
		this.count = count;
	}
	
	@Override
	public void count() {
		System.out.println("開啓事務。。。。");
		count.count();
		System.out.println("結束事務。。。。");
	}
}</span>

從上面的代碼可以看到,這種靜態的代理模式在訪問無法訪問的資源,增加現有的接口業務功能方面有很大的有點,但是大量使用這種靜態代理,會使系統內的類的規模增大,並且不易維護;並且由於Proxy和RealSubject的功能本質上是相同的,只是類名不同,這種代理在系統的存在,會導致系統結構比較臃腫和鬆散。
爲了解決這個問題,就有了動態地創建Proxy的想法:在運行狀態中,需要代理的地方,根據Subject和RealSubject,動態的創建一個Proxy,用完之後就會銷燬,這樣就可以避免了Proxy角色的class在系統中冗餘的問題了。

3. 動態代理


與靜態代理相對應的就是動態代理,動態代理類的字節碼是在程序運行時動態生成,無需程序員手工編寫它的源代碼。動態代理類不僅簡化了變成的功能,而且提供了軟件系統的可擴展性。在這種模式之中:代理Proxy 和RealSubject 應該實現相同的功能,這一點相當重要。(我這裏說的功能,可以理解爲某個類的public方法)

在面向對象的編程之中,如果我們想要約定Proxy 和RealSubject可以實現相同的功能,有兩種方式:


  • 一個比較直觀的方式,就是定義一個功能接口,然後讓Proxy 和RealSubject來實現這個接口。
  • 還有比較隱晦的方式,就是通過繼承。因爲如果Proxy 繼承自RealSubject,這樣Proxy則擁有了RealSubject的功能,Proxy還可以通過重寫RealSubject中的方法,來實現多態。
其中JDK中提供的創建動態代理的機制,是以a 這種思路設計的,而cglib 則是以b思路設計的。

3.1 JDK動態代理


JDK動態代理有兩個很重要的類,Proxy和InvocationHandler。

3.1.1 Porxy類


Proxy類是JDK動態代理的核心累,提供了一個非常重要的方法:newProxyInstance,該方法能夠在運行期動態創建實現了指定接口的類,具體得創建功能是由ProxyGenerator類來實現,這個類我們後面再講述。

<span style="font-size:14px;">public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h) throws IllegalArgumentException </span>

參數說明:
ClassLoader loader:類加載器
Class<?> interfaces:代理類需要實現的接口,也是目標類實現的接口
InvocationHandler h:實現InvocationHandler接口的實例。

3.1.2 InvocationHandler接口


每一個代理類都會關聯一個實現InvocationHandler接口的子類對象,該子類對象是對目標類的又一層代理。InvocationHanlder只提供了一個方法:

<span style="font-size:14px;">    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;</span>

參數說明:
Object proxy:生成的動態代理對象
Method method:需要代理的方法
Object[] args:代理方法調用的參數

對於目標接口裏的每個方法實現,都是通過調用InvocationHandler接口的子類對象的反射調用。也就是是說,動態代理類實際上是InvocationHandler子類對象的一個代理,InvocationHandler子類對象又是目標類的代理,通過這樣層層代理就能夠實現上述的功能了。之間的關係如下圖示:



從上圖可看出,JDK動態代理並不是簡單的一層代理,而是通過層層代理,最終通過Method的反射來調用目標對象的方法,而需要額外添加的功能可放在InvocationHandler的實現類。

3.1.3  jdk動態代理實現


同樣的,我們也來實現一個JDK動態代理的代理方式。

a. 接口類

<span style="font-size:14px;">public interface Count {

	public void count();
}</span>

<span style="font-size:14px;">public interface Money {

	public void update();
}</span>

b. 業務類

<span style="font-size:14px;">public class CountImpl implements Count, Money{

	@Override
	public void count(){
		System.out.println("計算賬戶餘額。。。");
	}

	@Override
	public void update() {
		System.out.println("更新賬戶餘額。。。");
	}
}</span>

c. 實現InvocationHanlder的子類對象

<span style="font-size:14px;">public class MyInvocationHandler implements InvocationHandler{

	private Object target;
	
	public MyInvocationHandler(Object target){
		this.target = target;
	}

	/**
	 * @param proxy:代理類的實例
	 * @param method:要調用的被代理類的方法
	 * @param args:被調用方法的參數
	 */
	@Override
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		System.out.println("開始記錄日誌。。。");
		//反射調用目標對象的方法
		method.invoke(target, args);
		System.out.println("結束記錄日誌。。。");
		return null;
	}

}</span>

d. 獲取代理類的工廠類

<span style="font-size:14px;">public class ProxyFactory {

	public static Object getProxy(Object object){
		MyInvocationHandler invocationHandler = new MyInvocationHandler(object);
		return Proxy.newProxyInstance(object.getClass().getClassLoader(), object.getClass().getInterfaces(), invocationHandler);
	}
}</span>

e. 測試類

<span style="font-size:14px;">public class CountProxyTest {

	public static void main(String[] args) {
		Count count = new CountImpl();
		Count proxy = (Count) ProxyFactory.getProxy(count);
		proxy.count();
	}
}</span>

f. 輸出結果

開始記錄日誌。。。
計算賬戶餘額。。。
結束記錄日誌。。。

從結果可以看到,我們已經在原有的業務功能上,通過動態代理的方式加入了日誌功能,這就是spring aop的實現方式。代理類生成了,但這個代理的背後都是怎麼樣實現的呢?下面通過跟蹤源碼來一步步解析:

3.1.4  jdk動態代理源碼分析


代理類是通過Proxy.newProxyInstance方法來完成,那麼先看看newProxyInstance方法。

<span style="font-size:14px;">/** 
 * loader:類加載器 
 * interfaces:目標對象實現的接口 
 * h:InvocationHandler的實現類 
 */  
public static Object newProxyInstance(ClassLoader loader,  
                      Class<?>[] interfaces,  
                      InvocationHandler h)  
    throws IllegalArgumentException  
    {  
    if (h == null) {  
        throw new NullPointerException();  
    }  
  
    /* 
     * Look up or generate the designated proxy class. 
     */  
    Class cl = getProxyClass(loader, interfaces);  
  
    /* 
     * Invoke its constructor with the designated invocation handler. 
     */  
    try {  
            // 調用代理對象的構造方法(也就是$Proxy0(InvocationHandler h))  
        Constructor cons = cl.getConstructor(constructorParams);  
            // 生成代理類的實例並把MyInvocationHandler的實例傳給它的構造方法  
        return (Object) cons.newInstance(new Object[] { h });  
    } catch (NoSuchMethodException e) {  
        throw new InternalError(e.toString());  
    } catch (IllegalAccessException e) {  
        throw new InternalError(e.toString());  
    } catch (InstantiationException e) {  
        throw new InternalError(e.toString());  
    } catch (InvocationTargetException e) {  
        throw new InternalError(e.toString());  
    }  
    }  </span>

再進getProxyClass方法內部

<span style="font-size:14px;">public static Class<?> getProxyClass(ClassLoader loader,   
                                         Class<?>... interfaces)  
    throws IllegalArgumentException  
    {  
    // 如果目標類實現的接口數大於65535個則拋出異常  
    if (interfaces.length > 65535) {  
        throw new IllegalArgumentException("interface limit exceeded");  
    }  
  
    // 聲明代理對象所代表的Class對象(有點拗口)  
    Class proxyClass = null;  
  
    String[] interfaceNames = new String[interfaces.length];  
  
    Set interfaceSet = new HashSet();   // for detecting duplicates  
  
    // 遍歷目標類所實現的接口  
    for (int i = 0; i < interfaces.length; i++) {  
          
        // 拿到目標類實現的接口的名稱  
        String interfaceName = interfaces[i].getName();  
        Class interfaceClass = null;  
        try {  
        // 加載目標類實現的接口到內存中  
        interfaceClass = Class.forName(interfaceName, false, loader);  
        } catch (ClassNotFoundException e) {  
        }  
        if (interfaceClass != interfaces[i]) {  
        throw new IllegalArgumentException(  
            interfaces[i] + " is not visible from class loader");  
        }  
  
        // 中間省略了一些無關緊要的代碼 .......  
          
        // 把目標類實現的接口代表的Class對象放到Set中  
        interfaceSet.add(interfaceClass);  
  
        interfaceNames[i] = interfaceName;  
    }  
  
    // 把目標類實現的接口名稱作爲緩存(Map)中的key  
    Object key = Arrays.asList(interfaceNames);  
  
    Map cache;  
      
    synchronized (loaderToCache) {  
        // 從緩存中獲取cache  
        cache = (Map) loaderToCache.get(loader);  
        if (cache == null) {  
        // 如果獲取不到,則新建地個HashMap實例  
        cache = new HashMap();  
        // 把HashMap實例和當前加載器放到緩存中  
        loaderToCache.put(loader, cache);  
        }  
  
    }  
  
    synchronized (cache) {  
  
        do {  
        // 根據接口的名稱從緩存中獲取對象  
        Object value = cache.get(key);  
        if (value instanceof Reference) {  
            proxyClass = (Class) ((Reference) value).get();  
        }  
        if (proxyClass != null) {  
            // 如果代理對象的Class實例已經存在,則直接返回  
            return proxyClass;  
        } else if (value == pendingGenerationMarker) {  
            try {  
            cache.wait();  
            } catch (InterruptedException e) {  
            }  
            continue;  
        } else {  
            cache.put(key, pendingGenerationMarker);  
            break;  
        }  
        } while (true);  
    }  
  
    try {  
        // 中間省略了一些代碼 .......  
          
        // 這裏就是動態生成代理對象的最關鍵的地方  
        byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces);  
        try {  
            // 根據代理類的字節碼生成代理類的實例  
            proxyClass = defineClass0(loader, proxyName,  
            proxyClassFile, 0, proxyClassFile.length);  
        } catch (ClassFormatError e) {  
            throw new IllegalArgumentException(e.toString());  
        }  
        }  
        // add to set of all generated proxy classes, for isProxyClass  
        proxyClasses.put(proxyClass, null);  
  
    }   
    // 中間省略了一些代碼 .......  
      
    return proxyClass;  
    }  </span>

上面我們提到了ProxyGenerator是完成動態代理類class字節碼功能的類,這裏就調用了它的靜態方法generateProxyClass,進入該方法:

<span style="font-size:14px;">public static byte[] generateProxyClass(final String name,  
                                           Class[] interfaces)  
   {  
       ProxyGenerator gen = new ProxyGenerator(name, interfaces);  
    // 這裏動態生成代理類的字節碼  
       final byte[] classFile = gen.generateClassFile();  
  
    // 如果saveGeneratedFiles的值爲true,則會把所生成的代理類的字節碼保存到硬盤上  
       if (saveGeneratedFiles) {  
           java.security.AccessController.doPrivileged(  
           new java.security.PrivilegedAction<Void>() {  
               public Void run() {  
                   try {  
                       FileOutputStream file =  
                           new FileOutputStream(dotToSlash(name) + ".class");  
                       file.write(classFile);  
                       file.close();  
                       return null;  
                   } catch (IOException e) {  
                       throw new InternalError(  
                           "I/O exception saving generated file: " + e);  
                   }  
               }  
           });  
       }  
  
    // 返回代理類的字節碼  
       return classFile;  
   }  </span>

看到這裏就清楚了,是通過調用generateProxyClass方法來實現代理類字節碼的創建的。好了,通過上述源碼的分析,對於如何創建代理的流程就很清晰了,但是最後生成的代理類的結構是怎麼樣的呢,而且InvocationHandler又是怎麼被調用的呢,下面我們先生成一個代理類,保存到磁盤,然後查看class文件。

<span style="font-size:14px;">public class ProxyGeneratorUtils {  
  
    /** 
     * 把代理類的字節碼寫到硬盤上 
     * @param path 保存路徑 
     */  
    public static void writeProxyClassToHardDisk(String path) {  
        // 第一種方法,這種方式在剛纔分析ProxyGenerator時已經知道了  
        System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", true);  
          
        // 第二種方法  
          
        // 獲取代理類的字節碼  
        byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy11", CountImpl.class.getInterfaces());  
          
        FileOutputStream out = null;  
          
        try {  
            out = new FileOutputStream(path);  
            out.write(classFile);  
            out.flush();  
        } catch (Exception e) {  
            e.printStackTrace();  
        } finally {  
            try {  
                out.close();  
            } catch (IOException e) {  
                e.printStackTrace();  
            }  
        }  
    }  
    
    @Test
    public void test(){
    	writeProxyClassToHardDisk("D:/proxy/$Proxy11.class");
    }
}</span>

執行測試,代理類就保存在d:/proxy目錄下了,用JD-Gui工具打開class文件:

<span style="font-size:14px;">/**
 * 代理類實現了與目標類Count相同的接口,並繼承了Proxy
 * 
 */
public final class $Proxy11 extends Proxy implements Count, Money {

	private static Method m1;
	private static Method m3;
	private static Method m0;
	private static Method m4;
	private static Method m2;

	/**
	 * 代理類的構造方法需要一個InvocationHanlder作爲參數
	 * 
	 * @param paramInvocationHandler
	 */
	public $Proxy11(InvocationHandler paramInvocationHandler) {
		super(paramInvocationHandler);
	}

	/**
	 * 實現了目標類Count的equals方法
	 */
	public final boolean equals(Object paramObject) {
		try {
			// 實際上是通過調用InvaocationHandler.invoke(proxy, equals, args)來調用目標類Count的equals方法
			return ((Boolean) this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
		} catch (Error | RuntimeException localError) {
			throw localError;
		} catch (Throwable localThrowable) {
			throw new UndeclaredThrowableException(localThrowable);
		}
	}

	/**
	 * 實現了目標類Count的count方法
	 */
	public final void count() {
		try {
			// 實際上是通過調用InvaocationHandler.invoke(this, count, args)來調用目標類Count的count方法
			this.h.invoke(this, m3, null);
			return;
		} catch (Error | RuntimeException localError) {
			throw localError;
		} catch (Throwable localThrowable) {
			throw new UndeclaredThrowableException(localThrowable);
		}
	}

	/**
	 * 實現了目標類Count的hashCode方法
	 */
	public final int hashCode() {
		try {
			// 實際上是通過調用InvaocationHandler.invoke(this, hashCode, args)來調用目標類Count的count方法
			return ((Integer) this.h.invoke(this, m0, null)).intValue();
		} catch (Error | RuntimeException localError) {
			throw localError;
		} catch (Throwable localThrowable) {
			throw new UndeclaredThrowableException(localThrowable);
		}
	}

	/**
	 * 實現了目標類Count的update方法
	 */
	public final void update() {
		try {
			// 實際上是通過調用InvaocationHandler.invoke(this, update, args)來調用目標類Count的count方法
			this.h.invoke(this, m4, null);
			return;
		} catch (Error | RuntimeException localError) {
			throw localError;
		} catch (Throwable localThrowable) {
			throw new UndeclaredThrowableException(localThrowable);
		}
	}

	/**
	 * 實現了目標類Count的toString方法
	 */
	public final String toString() {
		try {
			// 實際上是通過調用InvaocationHandler.invoke(this, toString, args)來調用目標類Count的count方法
			return (String) this.h.invoke(this, m2, null);
		} catch (Error | RuntimeException localError) {
			throw localError;
		} catch (Throwable localThrowable) {
			throw new UndeclaredThrowableException(localThrowable);
		}
	}

	/**
	 * 代理類需要代理目標類的方法,包括父類,接口,目標類自己的所有公共方法
	 */
	static {
		try {
			m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
			m3 = Class.forName("cn.test.proxy.Count").getMethod("count", new Class[0]);
			m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
			m4 = Class.forName("cn.test.proxy.Money").getMethod("update", new Class[0]);
			m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
			return;
		} catch (NoSuchMethodException localNoSuchMethodException) {
			throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
		} catch (ClassNotFoundException localClassNotFoundException) {
			throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
		}
	}
}</span>

仔細觀察可以看出生成的動態代理類有以下特點:

1.繼承自 java.lang.reflect.Proxy,實現了 Count,MONEY 這兩個CountImp實現的接口;
2.類中的所有方法都是final 的;
3.所有的方法功能的實現都統一調用了InvocationHandler的invoke()方法。


3.2 Cglib動態代理


JDK的動態代理機制只能代理實現了接口的類,而沒有實現接口的類就不能實現JDK的動態代理,cglib是針對此類來實現代理的,它的原理就是對指定的目標類生成一個子類,並覆蓋其中的方法來實現增強功能,但因爲採用的是繼承,所以不能對final修飾的類進行代理。

3.2.1  什麼是Cglib


CGLIB是一個強大的高性能的代碼生成包。
  • 廣泛的被許多AOP的框架使用,例如:Spring AOP和dynaop,爲他們提供方法的interception(攔截);
  • hibernate使用CGLIB來代理單端single-ended(多對一和一對一)關聯(對集合的延遲抓取,是採用其他機制實現的);
  • EasyMock和jMock是通過使用模仿(moke)對象來測試java代碼的包。

它們都通過使用CGLIB來爲那些沒有接口的類創建模仿(moke)對象。

CGLIB包的底層是通過使用一個小而快的字節碼處理框架ASM(Java字節碼操控框架),來轉換字節碼並生成新的類。除了CGLIB包,腳本語言例如 Groovy和BeanShell,也是使用ASM來生成java的字節碼。當不鼓勵直接使用ASM,因爲它要求你必須對JVM內部結構包括class文件的格式和指令集都很熟悉。所以cglib包要依賴於asm包,需要一起導入。下圖爲cglib與一些框架和語言的關係(CGLIB Library and ASM Bytecode Framework)。



Spring AOP和Hibernate同時使用JDK的動態代理和CGLIB包。Spring AOP,如果不強制使用CGLIB包,默認情況是使用JDK的動態代理來代理接口。

3.2.2 Cglib動態代理實現


a.  業務類

<span style="font-size:14px;">public class CountImpl {

	public void count(){
		System.out.println("計算賬戶餘額。。。");
	}
}</span>

b. 方法攔截器

<span style="font-size:14px;">public class MyMethodIntercaptor implements MethodInterceptor {

	@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;
	}
}</span>

c. 代理類生成工廠

<span style="font-size:14px;">public class ProxyFactory {

	public static Object getProxy(Object target){
		MethodInterceptor methodInterceptor = new MyMethodIntercaptor();
		Enhancer enhancer = new Enhancer();
		enhancer.setSuperclass(target.getClass());
		enhancer.setCallback(methodInterceptor);
		return enhancer.create();
	}
}</span>

d. 測試類

<span style="font-size:14px;">public class CountProxyTest {

	public static void main(String[] args){
		CountImpl countImpl = new CountImpl();
		CountImpl countProxy = (CountImpl) ProxyFactory.getProxy(countImpl);
		countProxy.count();
	}
}</span>

e. 輸出結果

<span style="font-size:14px;">開始記錄日誌。。。
計算賬戶餘額。。。
結束記錄日誌。。。</span>

Cglib的源碼分析暫不分析了。

3.3  javassist實現動態代理


動態代理本質上就是在程序運行期間根據目標類重新創建一個代理類,而通過代理類去反射調用目標類的業務邏輯。理解了這個,我們就很容易用javassist來實現動態代理,

關於javassist的介紹,前面文章有介紹:Javassist詳解

3.3.1 javassist實現動態代理


a.  業務類接口

<span style="font-size:14px;">public interface Count {
	public void count();
	public void sayHello();
}</span>

b. 業務類實現類

<span style="font-size:14px;">public class CountImpl implements Count{

	@Override
	public void count() {
		System.out.println("計算賬戶餘額。。。");
	}
	
	@Override
	public void sayHello(){
		System.out.println("你好。。。。");
	}
}</span>

c. 自定義Interacptor

<span style="font-size:14px;">public interface Interceptor {
	
	public int intercept(Object proxy, Method method, Object[] Args);
	
}</span>

d. 代理類生成工廠

<span style="font-size:14px;">/**
 * 基於Javassist動態生成字節碼實現簡單的動態代理 
 **/
public class Proxy {
	/**
	 * 動態生成的代理類名前綴 
	 * */
	private static final String PACKAGE_NAME = Proxy.class.getPackage().getName();
	
	/**
	 * 代理類名索引 用於標示一個唯一的代理類(具體的代理類名爲PACKAGE_NAME$n)
	 * class
	 * */
	private static final AtomicLong PROXY_CLASS_COUNTER = new AtomicLong(0);
	
	/**
	 * 代理攔截器(利用繼承減少動態構造的字節碼) 
	 * */
	protected Interceptor interceptor;

	protected Proxy(Interceptor interceptor) {
		this.interceptor = interceptor;
	}

	/**
	 * 創建動態代理的工廠方法 static factory method for create proxy
	 * 
	 * @param targetClass:被代理的類型
	 * @param interceptor:攔截器實例
	 * @return 返回動態代理實例 它實現了targerClass的所有接口。 因此可以向上轉型爲這些之中的任意接口
	 * */
	@SuppressWarnings("unchecked")
	public static Object createProxy(Class<?> targetClass, Interceptor interceptor) {
		int index = 0;
		/* 獲得運行時類的上下文 */
		ClassPool pool = ClassPool.getDefault();
		/* 動態創建代理類 */
		CtClass ctClass = pool.makeClass(PACKAGE_NAME + ".$proxy" + PROXY_CLASS_COUNTER.getAndIncrement());

		try {
			/* 獲得Proxy類作爲代理類的父類 */
			CtClass superclass = pool.get(Proxy.class.getName());
			ctClass.setSuperclass(superclass);
			/* 獲得被代理類的所有接口 */
			CtClass[] interfaces = pool.get(targetClass.getName()).getInterfaces();
			for (CtClass i : interfaces) {
				/* 動態代理實現這些接口 */
				ctClass.addInterface(i);
				/* 獲得結構中的所有方法 */
				CtMethod[] methods = i.getDeclaredMethods();
				for (int n = 0; n < methods.length; n++) {
					CtMethod m = methods[n];
					/* 構造這些Method參數 以便傳遞給攔截器的interceptor方法 */
					StringBuilder fields = new StringBuilder();
					fields.append("private static java.lang.reflect.Method method"+ index);
					fields.append("=Class.forName(\"");
					fields.append(i.getName());
					fields.append("\").getDeclaredMethods()[");
					fields.append(n);
					fields.append("];");
					/* 動態編譯之 */
					CtField cf = CtField.make(fields.toString(), ctClass);
					ctClass.addField(cf);
					GenerateMethods(pool, ctClass, m, index);
					index++;
				}
			}
			/* 創建構造方法以便注入攔截器 */
			CtConstructor cc = new CtConstructor(new CtClass[] { pool.get(Interceptor.class.getName()) }, ctClass);
			cc.setBody("{super($1);}");
			ctClass.addConstructor(cc);
			ctClass.writeFile("d:/proxy/"+ctClass.getSimpleName());
			return ctClass.toClass().getConstructor(Interceptor.class).newInstance(interceptor);
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}

	/**
	 * 動態生成生成方法實現(內部調用)
	 * */
	private static void GenerateMethods(ClassPool pool, CtClass proxy,
			CtMethod method, int index) {
		try {
			CtMethod cm = new CtMethod(method.getReturnType(),method.getName(), method.getParameterTypes(), proxy);
			/* 構造方法體 */
			StringBuilder mbody = new StringBuilder();
			mbody.append("{super.interceptor.intercept(this,method");
			mbody.append(index);
			mbody.append(",$args);}");
			cm.setBody(mbody.toString());
			proxy.addMethod(cm);
		} catch (CannotCompileException e) {
			e.printStackTrace();
		} catch (NotFoundException e) {
			e.printStackTrace();
		}
	}
}</span>

e. 測試程序

<span style="font-size:14px;">public class CountProxyTest {

	public static void main(String[] args) {
		Count count = new CountImpl();
		Count countProxy = (Count) Proxy.createProxy(count.getClass(), new MyInterceptor(count));
		countProxy.count();
		countProxy.sayHello();
	}

}</span>

f. 輸出結果

<span style="font-size:14px;">開始記錄日誌。。。。
計算賬戶餘額。。。
結束記錄日誌。。。。
開始記錄日誌。。。。
你好。。。。
結束記錄日誌。。。。</span>

從結果可以看到,我們已經用javassist實現了一個動態代理,然後我們查看生成的代理類字節碼文件:

<span style="font-size:14px;">public class $proxy0 extends Proxy  implements Count
{
  private static Method method0 = java.lang.Class.forName("cn.test.proxy.Count").getDeclaredMethods()[0];
  private static Method method1 = java.lang.Class.forName("cn.test.proxy.Count").getDeclaredMethods()[1];
  
  public void count()
  {
    this.interceptor.intercept(this, method0, new Object[0]);
  }
  
  public void sayHello()
  {
    this.interceptor.intercept(this, method1, new Object[0]);
  }
  
  public $proxy0(Interceptor paramInterceptor)
  {
    super(paramInterceptor);
  }
}</span>

生成的代理類和jdk動態代理形式差不多,實現了目標類的接口,繼承了我們自定義的Proxy類以及實現了目標類的所有公共方法。


發佈了73 篇原創文章 · 獲贊 18 · 訪問量 23萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章