java的靜態代理和動態代理(jdk、cglib)


一、代理模式

代理的概念來自於設計模式中的代理模式,先了解一下代理模式

1、結構圖

2、參與者

Subject:接口,定義代理類和實際類的共用接口

RealSubject:實際類,實現Subject這個接口

Proxy:代理類,實現Subject這個接口,內部引用一個RealSubject實際類

3、描述

Proxy實現了Subject接口,內部引用一個RealSubject實際類,RealSubject能做的Proxy都會

於是Proxy代替了RealSubject,實例化到Subject里交給客戶端調用,而客戶端不知道自己實際用的是誰

客戶端調用了Subject接口中的Request()方法,實際上訪問的是Proxy

Proxy類提供了一個客戶端對RealSubject的間接性訪問的過程,在這個過程中可以做很多的控制

4、意圖

控制對象的訪問

5、應用場景

虛代理:根據需要來創建開銷很大的對象,該對象只有在需要的時候才會被真正創建

遠程代理:用來在不同的地址空間上代表同一個對象,這個不同的地址空間可以是在本機,也可以在其他機器上,在java里面最典型的就是RMI技術

copy-on-write代理:在客戶端操作的時候,只有對象確實改變了,才會真的拷貝(或克隆)一個目標對象,算是虛代理的一個分支

保護代理:控制對原始對象的訪問,如果有需要,可以給不同的用戶提供不同的訪問權限,以控制他們對原始對象的訪問

Cache代理:為那些昂貴操作的結果提供臨時的存儲空間,以便多個客戶端可以共享這些結果

防火牆代理:保護對象不被惡意用戶訪問和操作

同步代理:使多個用戶能夠同時訪問目標對象而沒有沖突

 

二、靜態代理

java中的靜態代理就是代理模式最簡單的實現,下面是示例代碼:

//接口
public interface Subject {
	void Request();
}

//實際類
public class RealSubject implements Subject {
	public void Request() {
		System.out.println("執行具體的功能");
	}
}

//代理類
public class Proxy implements Subject {
	private Subject realSubject = null;

	public Proxy(Subject subject) {
		realSubject = subject;
	}

	public void Request() {
		System.out.println("靜態代理開始");
		realSubject.Request();
		System.out.println("靜態代理結束");
	}
}

//客戶端
public class Client {
	public static void main(String[] args) {
		RealSubject realSubject = new RealSubject();
		realSubject.Request();
		
		Subject subject = new Proxy(realSubject);
		subject.Request();
	}
}

執行的結果是:

 

靜態代理的局限:

一個代理類Proxy只能服務Subject一種接口,如果有10個不同類型的對象,需要創建10個代理類,類的數量激增、也增加了代碼的冗余

 

如果只用一個代理類,就能完成全部的代理功能就好了,於是就有了動態代理

 

三、動態代理

靜態代理的代碼,在編譯后就生成了代理類的.class字節碼文件,所以在程序運行以前,代理類和實際類的關系就確定了

而動態代理不同,代理類的字節碼是在程序運行期間,由JVM根據反射等機制動態生成的,代理類和實際類的關系是在程序運行時確定的

依托反射等機制,動態代理可以動態生成任意類型的代理類,一個代理的處理程序就可以處理多種對象,程序更加的靈活,也更容易擴展

spring的AOP、RMI遠程代理就是依靠動態代理來實現的

 

java中的動態代理有兩種,一種是java自帶的jdk動態代理,一種是基於cglib的動態代理

 

1、jdk動態代理

jdk1.3開始,java語言提供了對動態代理的支持,主要會使用到java.lang.reflect包下的Proxy和InvocationHandler類

(1)、Proxy

Proxy類提供了用於創建動態代理類和實例對象的方法,它是所創建的動態代理類的父類

其中newProxyInstance()方法用來獲得動態生成的代理類實例,第一個參數是實際類的類加載器,第二個參數是實際類的接口列表,第三個參數是代理處理程序類

(2)、InvocationHandler

InvocationHandler接口是代理處理程序類的實現接口,每一個被生成的代理類實例都可以提供一個相關的代理處理程序類,代理類實例的方法被調用時會回調invoke()方法

invoke()方法用來處理代理類實例的方法調用並返回結果,實現自己的代理邏輯,第一個參數是代理類實例,第二個參數是需要代理的方法,第三個參數是方法的參數數組

//接口
public interface Subject {
	void Request();
}

//實際類
public class RealSubject implements Subject {
	public void Request() {
		System.out.println("執行具體的功能");
	}
}

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

//代理處理程序類
public class ProxyHandler implements InvocationHandler {
	
	private Object target;
	
	public ProxyHandler(Object target) {
		this.target = target;
	}
	
	@Override
	public Object invoke(Object proxy, Method method, Object[] param) throws Throwable {
		System.out.println("jdk動態代理開始");
		Object result = method.invoke(target, param);
    	System.out.println("jdk動態代理結束");
    	
		return result;
	}
}

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

//客戶端
public class Client {
	public static void main(String[] args) {
		RealSubject realSubject = new RealSubject();
		
		InvocationHandler handler = new ProxyHandler(realSubject);
		Subject subject = (Subject)Proxy.newProxyInstance(realSubject.getClass().getClassLoader(), realSubject.getClass().getInterfaces(), handler);
		
		subject.Request();
	}
}

執行的結果是:

 

通過"realSubject.getClass().getInterfaces()"這句可以看到,jdk動態代理的實現是依賴接口的

如果是抽象類或者一般的類需要進行代理,jdk動態代理就無法滿足了,只有使用cglib動態代理

 

2、cglib動態代理

cglib是一個強大的高性能的代碼生成包,廣泛的被許多AOP的框架使用,它的開源地址在https://github.com/cglib/cglib

通過它實現動態代理,主要用到import net.sf.cglib.proxy包下的MethodInterceptor、MethodProxy和Enhancer類

(1)、MethodInterceptor

MethodInterceptor類是方法攔截器,代理類實例的方法被調用時會回調MethodInterceptor的intercept()方法攔截,用來實現自己的代理邏輯,類似於jdk動態代理的InvocationHandler接口

(2)、MethodProxy

MethodProxy是intercept()方法中的第四個參數的類型,它是實際類方法的代理引用,使用methodProxy比使用jdk自身的method在效率上會有提升

(3)、Enhancer

Enhancer用來動態創建實際類子類的代理類實例,setSuperclass()方法設置實際類為父類,setCallback()方法建立方法回調,create()方法創建一個代理類實例

//實際類
public class RealSubject {
	public void Request() {
		System.out.println("執行具體的功能");
	}
	
	public final void RequestFinal() {
		System.out.println("執行具體的功能(final)");
	}
}

import java.lang.reflect.Method;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

//方法攔截器
public class ProxyInterceptor implements MethodInterceptor {
	public Object intercept(Object proxy, Method method, Object[] params, MethodProxy methodProxy) throws Throwable {
		System.out.println("cglib動態代理開始");
		Object result = methodProxy.invokeSuper(proxy, params);
		System.out.println("cglib動態代理開始");
		
		return result;
	}
	
	//創建代理類實例
	public Object newProxy(Object target) 
	{
		Enhancer enhancer = new Enhancer();
		enhancer.setSuperclass(target.getClass());
		enhancer.setCallback(this);
		return enhancer.create();
	}
}

//客戶端
public class Client {
	public static void main(String[] args) {
		RealSubject realSubject = new RealSubject();
		
		ProxyInterceptor proxyInterceptor = new ProxyInterceptor();
		RealSubject subject = (RealSubject)proxyInterceptor.newProxy(realSubject);
		
		subject.Request();
		subject.RequestFinal();
	}
}

執行的結果是:

 

通過輸出的結果可以發現final的方法cglib是無法處理的,因為cglib是基於繼承的代理,final的方法無法被重寫

 

3、jdk、cglib動態代理的區別

jdk動態代理是由java內部的反射機制生成一個實現代理接口的匿名類,被代理的類必須要實現一個接口,如果某個類沒有實現接口則不能生成代理對象,實際使用中會有一些局限性

cglib動態代理底層是借助asm來實現的,加載實際類的.class文件,修改其字節碼生成子類來處理,比java反射效率要高一些,不過生成子類的方式也帶來小問題:目標類不能聲明成final,因為final類不能被繼承,無法生成代理;目標方法也不能聲明成final,final方法不能被重寫,無法得到處理

在實際使用中cglib的應用更加廣泛,效率更有優勢

 

實例代碼地址:https://github.com/ctxsdhy/cnblogs-example

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM