代理模式:爲其餘對象提供一種代理以控制對這個對象的訪問。程序員
小明是一個程序員,在公司負責項目的研發工做。有一天,客戶打電話進來,溝通以後,原來客戶是有個模塊需求要變更一下。小明卻沒有應允,而是讓客戶去找產品經理老王溝通。bash
是小明偷懶不想幹活嗎?顯然不是。咱們把這個事例對應到上面的定義上,程序員小明能夠映射爲其餘對象,產品經理老王是小明的代理。它來控制小明這個對象的訪問。app
咱們看上面的類圖,能夠簡單概括如下角色。函數
咱們經過代碼來重現上面的場景。測試
首先定義一個 工程師的接口,它有一個編碼的方法。ui
public interface Engineer {
void coding();
}
複製代碼
小明是個Java碼農,啊呸。是個Java工程師,要實現工程師這個接口。this
public class JavaEngineer implements Engineer{
private String name;
public JavaEngineer(String name){
this.name = name;
}
public void coding() {
System.out.println(name+" :正在努力的coding...");
System.out.println(name+" :終於改完了!");
}
}
複製代碼
產品經理老王也讓他實現工程師的接口。他代理了公司裏的Java工程師對象,當客戶有需求提出來,他要整理評估一下,當須要coding的時候,他直接轉交給具體的工程師去處理。編碼
public class ProductManager implements Engineer{
private String name;
private JavaEngineer engineer;
public ProductManager(JavaEngineer engineer,String name){
this.engineer = engineer;
this.name = name;
}
public void coding() {
arrange();
engineer.coding();
appease();
}
private void arrange(){
System.out.println(this.name+":整理客戶需求中...");
System.out.println(this.name+":輸出需求文檔,交給碼農去完成!");
}
private void appease(){
System.out.println(this.name+":哎,需求變好屢次了.得安撫一下這個碼農纔好!");
}
}
複製代碼
咱們來重現一下這個場景。spa
//有一個美麗的Java工程師,他的名字叫小明。
JavaEngineer engineer = new JavaEngineer("XiaoMing");
//一樣,還有一個猥瑣的老王。
ProductManager manager = new ProductManager(engineer,"老王");
//有新需求的時候,老王負責去溝通搞定。
manager.coding();
System.out.println("------------------輸出結果-----------------------");
//老王:整理客戶需求中...
//老王:輸出需求文檔,交給碼農去完成!
//XiaoMing :正在努力的coding...
//XiaoMing :終於改完了!
//老王:哎,需求變好屢次了.得安撫一下這個碼農纔好!
複製代碼
JDK經過反射機制給咱們提供了動態代理的實現,容許開發人員在運行時刻動態的建立出代理類及其對象。當使用者調用了代理對象所代理的接口中的方法的時候,這個調用的信息會被傳遞給InvocationHandler的invoke方法。在 invoke方法的參數中能夠獲取到代理對象、方法對應的Method對象和調用的實際參數。invoke方法的返回值被返回給使用者。這種作法實際上至關於對方法調用進行了攔截。代理
關鍵有兩個類,Proxy和InvocationHandler 。
上面的事例咱們改爲動態代理方式來看一下。先定義一個調用處理程序
public class ProxyHandler implements InvocationHandler{
private Object target;
private String name;
public ProxyHandler(Object target,String name){
this.target = target;
this.name = name;
}
public Object invoke(Object proxy, Method method, Object[] args)throws Throwable {
arrange();
method.invoke(target, args);
appease();
return null;
}
private void arrange(){
System.out.println(this.name+":整理客戶需求中...");
System.out.println(this.name+":輸出需求文檔,交給工程師去完成!");
}
private void appease(){
System.out.println(this.name+":哎,需求變好屢次了.得安撫一下這個碼農纔好!");
}
}
複製代碼
而後咱們來測試一下
//有一個美麗的Java工程師,他的名字叫小明。
Engineer engineer = new JavaEngineer("XiaoMing");
//代理實例的調用處理程序
ProxyHandler handler = new ProxyHandler(engineer, "老王");
//返回指定接口的代理類實例,該接口能夠將方法指派到指定的handler
Engineer proxy = (Engineer) Proxy.newProxyInstance(Engineer.class.getClassLoader(),
new Class[]{Engineer.class}, handler);
proxy.coding();
System.out.println("------------------輸出結果-----------------------");
//老王:整理客戶需求中...
//老王:輸出需求文檔,交給碼農去完成!
//XiaoMing :正在努力的coding...
//XiaoMing :終於改完了!
//老王:哎,需求變好屢次了.得安撫一下這個碼農纔好!
複製代碼
咱們看到,Proxy類經過靜態方法newProxyInstance就生成了一個代理類的實例。先無論它是怎麼樣生成的,可是我想關心它到底長什麼樣子呢?把它拿出來看看。 JDK生成的代理類以$Porxy
開頭,後面跟一個從0開始的自增加數字。好比,$Proxy0
,經過下面這段代碼,能夠將代理類實例輸出到$Proxy0.class文件中。
byte[] data = ProxyGenerator.generateProxyClass("$Proxy0",new Class[] {Engineer.class});
FileOutputStream fileOutputStream = new FileOutputStream("$Proxy0.class");
fileOutputStream.write(data);
fileOutputStream.close();
複製代碼
經過反編譯class文件,獲得代理類刪減整理以下:
public class $Proxy0 extends Proxy implements Engineer {
private static final long serialVersionUID = 1L;
private static Method m3;
protected $Proxy0(InvocationHandler h) {
//經過構造方法 把代理實例的調用處理程序傳進來
super(h);
}
public final void coding() {
try {
//此處的h是父類Proxy的屬性,對應的就是ProxyHandler
//invoke就至關於ProxyHandler.invoke(this, m3, null);
this.h.invoke(this, m3, null);
return;
} catch (RuntimeException localRuntimeException) {
throw localRuntimeException;
} catch (Throwable localThrowable) {
throw new UndeclaredThrowableException(localThrowable);
}
}
static {
try {
//經過反射拿到指定接口的方法
m3 = Class.forName("proxy.proxy2.Engineer").getMethod("coding",
new Class[0]);
} catch (NoSuchMethodException localNoSuchMethodException) {
throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
} catch (ClassNotFoundException localClassNotFoundException) {
throw new NoClassDefFoundError(
localClassNotFoundException.getMessage());
}
}
}
複製代碼
若是咱們把經過反編譯獲得的class文件寫成一個Java類,調用它一樣能夠實現代理功能。
Engineer engineer = new JavaEngineer("XiaoMing");
ProxyHandler handler = new ProxyHandler(engineer, "老王");
$Proxy0 p0 = new $Proxy0(handler);
p0.coding();
System.out.println("------------------輸出結果-----------------------");
//老王:整理客戶需求中...
//老王:輸出需求文檔,交給碼農去完成!
//XiaoMing :正在努力的coding...
//XiaoMing :終於改完了!
//老王:哎,需求變好屢次了.得安撫一下這個碼農纔好!
複製代碼
關於動態代理建立對象的過程,咱們大概能夠這樣總結一下。
Proxy類的newProxyInstance方法封裝了2-4,只需2步就完成了代理對象的建立。 生成的代理對象集成Proxy,實現被代理對象接口。被代理對象接口的方法實際調用處理器的invoke方法,而處理器的invoke方法利用反射調用的是被代理對象的的方法method.invoke(target,args)。