1. 程式人生 > >java中的代理(靜態代理和動態代理)

java中的代理(靜態代理和動態代理)

之前本人在設計模式中有寫過靜態代理和動態代理的相關程式碼測試,可以看下。
今天我們主要學一下理論相關知識。

AOP的原理就是動態代理機制。RPC框架也是實現了AOP機制。

在這裡插入圖片描述

靜態代理

靜態代理:在程式碼編譯時就確定了被代理的類是哪一個。

這個靜態代理比較簡單,代理類和被代理類實現了同一介面,在代理類的建構函式中定義一個被代理類的物件即可。
Student類:實現Person介面(介面中有sayHello()方法)。
被代理類和Person類就比較簡單,這裡就不附上原始碼了。

代理類:StudentProxy類 實現Person介面

/**
 * 靜態代理,這個代理類也必須要實現和被代理類相同的Person介面
 *
 */
public class ProxyTest implements Person{	
	private Person o;	
	public ProxyTest(Person o){
		this.o = o;
	}
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		//s為被代理的物件,某些情況下 我們不希望修改已有的程式碼,我們採用代理來間接訪問
		Student s = new Student();
		//建立代理類物件
		ProxyTest proxy = new ProxyTest(s);
		//呼叫代理類物件的方法
		proxy.sayHello("welcome to java", 20);
	}
 
	@Override
	public void sayHello(String content, int age) {
		// TODO Auto-generated method stub
		System.out.println("ProxyTest sayHello begin");
		//在代理類的方法中 間接訪問被代理物件的方法
		o.sayHello(content, age);
		System.out.println("ProxyTest sayHello end");
	}
}

動態代理

動態代理:java的動態代理機制中,有兩個重要的類或者介面,一個是InvocationHandler(Interface),還有一個是Proxy(是一個class)。

其中InvocationHandler是每一個動態代理類都必須要實現的介面,我們通過代理物件呼叫一個方法的時候,該方法就會被轉發由InvocationHandler這個介面的invoke方法來呼叫。
Proxy類,動態的建立一個代理物件的類,它提供了許多方法,我們用的最多的是newProxyInstance方法,該方法的作用就是得到一個動態的代理物件。

動態代理模式主要由四個元素共同組成:
1.介面:定義具體實現的方法
2.被代理類:實現上述介面,執行介面中的方法
3.代理類:實現InvocationHandler,幫助被代理類實現方法。

代理類中要實現的內容有:

  1. 因為動態代理不知道被代理的類是哪一個,所以在實現了InvocationHandler的代理類中定義了一個Object類,在代理類的構函式中作為引數傳遞進來。
  2. 實現InvocationHandler中的invoke方法。
  3. 寫main方法,並在main方法中根據定義的被代理類實現代理類的生成。

動態代理的步驟:

1. 首先獲得一個被代理物件的引用,

2. 獲得該引用的介面

3. 生成一個類,這個類實現了我們給的代理物件所實現的介面

4. 上述類編譯生成了.class位元組碼供JVM使用

5. 呼叫上述生成的class

/**
 * 動態代理類
 * @author Martina
 *
 */
public class MyInvocationHandler implements InvocationHandler{
	
	private Object object;
	
	public MyInvocationHandler(Object object){
		this.object = object;
	}
 
	@Override
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		// TODO Auto-generated method stub
		System.out.println("MyInvocationHandler invoke begin");
		System.out.println("proxy: "+ proxy.getClass().getName());
		System.out.println("method: "+ method.getName());
		for(Object o : args){
			System.out.println("arg: "+ o);
		}
		//通過反射呼叫 被代理類的方法
		method.invoke(object, args);
		System.out.println("MyInvocationHandler invoke end");
		return null;
	}
	
	public static void main(String [] args){
		//建立需要被代理的類
		Student s = new Student();
		//這一句是生成代理類的class檔案,前提是你需要在工程根目錄下建立com/sun/proxy目錄,不然會報找不到路徑的io異常
		System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");
		//獲得載入被代理類的 類載入器
		ClassLoader loader = Thread.currentThread().getContextClassLoader();
		//指明被代理類實現的介面
		Class<?>[] interfaces = s.getClass().getInterfaces();
		// 建立被代理類的委託類,之後想要呼叫被代理類的方法時,都會委託給這個類的invoke(Object proxy, Method method, Object[] args)方法
		MyInvocationHandler h = new MyInvocationHandler(s);
		//生成代理類
		//注意newProxyInstance的三個引數所代表的含義
		Person proxy = (Person)Proxy.newProxyInstance(loader, interfaces, h);
		//通過代理類呼叫 被代理類的方法
		proxy.sayHello("yujie.wang", 20);
		proxy.sayGoodBye(true, 100);
		System.out.println("end");
	}
 
}

上述程式碼中的Proxy.newProxyInstance所建立的代理類的位置和怎麼輸出其名字:
(6)反編譯Proxy.newProxyInstance所建立的代理類

//這一句是生成代理類的class檔案,前提是你需要在工程根目錄下建立com/sun/proxy目錄,不然會報找不到路徑的io異常
System.getProperties().put(“sun.misc.ProxyGenerator.saveGeneratedFiles”,“true”);

我們在程式碼中加入上述程式碼,程式碼就會儲存生成的代理類,名稱為$Proxy0.class

在這裡插入圖片描述
 對於生成的 P r o x y 0. c l a s s j a v a j d g u i 0.3.3. w i n d o w s   P r o x y . n e w P r o x y I n s t a n c e j v m I n v o c a t i o n H a n d l e r Proxy0.class檔案,可以用java反編譯工具jd-gui-0.3.3.windows檢視   要記住,通過 Proxy.newProxyInstance 建立的代理物件是在jvm執行時動態生成的一個物件,它並不是我們的InvocationHandler型別,也不是我們定義的那組介面的型別,而是在執行是動態生成的一個物件,並且命名方式都是這樣的形式,以 開頭,proxy為中,最後一個數字表示物件的標號。

代理:https://www.cnblogs.com/xiaoluo501395377/p/3383130.html
https://blog.csdn.net/u011784767/article/details/78281384
https://www.cnblogs.com/lfdingye/p/7717063.html