代理模式概念:
理模式的定義
1、爲其他對象提供一種代理以控制對這個對象的訪問。在某些情況下,一個對象不適合或者不能直接引用另一個對象,而代理對象可以在客戶端和目標對象之間起到中介的作用,比如我們要去法院申訴,中間需要一個律師幫忙代理一下注意事項等,這個律師就是代理類。
2、當一個複雜對象的多份副本須存在時,代理模式可以結合享元模式以減少存儲器用量。典型作法是創建一個複雜對象及多個代理者,每個代理者會引用到原本的複雜對象。而作用在代理者的運算會轉送到原本對象。一旦所有的代理者都不存在時,複雜對象會被移除。
抽象角色:通過接口或抽象類聲明真實角色實現的業務方法。
代理角色:實現抽象角色,是真實角色的代理,通過真實角色的業務邏輯方法來實現抽象方法,可以附加自己的操作。
真實角色:實現抽象角色,定義真實角色所要實現的業務邏輯,供代理角色調用。
一個是真正的你要訪問的對象(目標類),一個是代理對象,真正對象與代理對象實現同一個接口,先訪問代理類再訪問真正要訪問的對象。
代理模式分爲靜態代理、動態代理。
靜態代理是由程序員創建或工具生成代理類的源碼,再編譯代理類。所謂靜態也就是在程序運行前就已經存在代理類的字節碼文件,代理類和委託類的關係在運行前就確定了。
動態代理是在實現階段不用關心代理類,而在運行階段才指定哪一個對象。
只看概念有點懵,現在舉個例子,比如我們人要去法院申訴用代碼演示
靜態代理:
1、我們先創建一個接口類打官司接口Law
//打官司接口
public interface Law {
public void law();
}
2、現在是我們進行申訴 目標類
public class Person implements Law {
@Override
public void law() {
// TODO Auto-generated method stub
System.out.println("目標類去法庭申述");
}
}
3、現在是靜態代理的核心部分,律師代理類
//代理類
public class Lawyer implements Law {
//引入目標類
private Person person;
@Override
//核心
public void law() {
// TODO Auto-generated method stub
this.a();
//person對象實例化,只有實例化之後,才能將這個對象放到內存中,然後才能在規定的範圍內來調用
if(person==null) {
person=new Person();
}
//調用目標類的方法
person.law();
this.b();
}
//律師要處理的事情
public void a() {
System.out.println("開庭前收集證據");
}
public void b() {
System.out.println("案件結案處理");
}
}
靜態代理類比較好理解,現在直接創建客戶端測試一下
4、創建客戶
public class test {
public static void main(String[] args) {
// TODO Auto-generated method stub
Lawyer lawyer=new Lawyer();
lawyer.law();
}
}
運行結果:
上面就是靜態代理模式,比較簡單,我們就不解釋了,動態代理不同於靜態代理的特點是它更爲靈活,因爲動態代理就是在運行期間動態生成代理類。我們沿用上面的例子,假設有五百個不一樣的人要打官司,都交給律師來操辦,那麼按照靜態代理的思路來做,我們需要寫五百個真實角色,並且代理角色持有這五百個真實角色。這顯然不合邏輯。這時候動態代理就應運而生了。下面我們要演示動態代理模式。
動態代理類的接口類和目標類跟靜態代理類一樣
1、打官司接口Law
//打官司接口
public interface Law {
public void law();
}
2、目標類
public class Person implements Law {
@Override
public void law() {
// TODO Auto-generated method stub
System.out.println(this.getClass().getSimpleName()+"目標類去法庭申述");
}
}
3、現在我們需要創建一個對象類去實現動態代理的一個核心接口InvocationHandler,動態代理本質採用的Java反射機制實現
InvocationHandler 是一個接口,官方文檔解釋說,每個代理的實例都有一個與之關聯InvocationHandler 實現類,如果代理的方法被調用,那麼代理便會通知和轉發給內部的 InvocationHandler 實現類,由它決定處理。
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
//實現動態代理的一個核心接口,動態代理本質採用的Java反射機制實現
//InvocationHandler 是一個接口,官方文檔解釋說,每個代理的實例都有一個與之關聯的 InvocationHandler 實現類,
//如果代理的方法被調用,那麼代理便會通知和轉發給內部的 InvocationHandler 實現類,由它決定處理。
public class MyHandler implements InvocationHandler {
private Object object;
public MyHandler(Object object) {
this.object=object;
}
// InvocationHandler 內的 invoke() 方法決定了怎麼樣處理代理傳遞過來的方法調用。
// proxy 代理對象
// method 代理對象調用的方法
// args 調用的方法中的參數
// Proxy 動態產生的代理會調用 InvocationHandler 實現類,因此 InvocationHandler 是實際執行者。
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// TODO Auto-generated method stub
System.out.println("開庭前律師收集證據");
Object obj=method.invoke(object, args);
System.out.println("目標類陳述事實");
return obj;
}
上面用到 InvocationHandler 內的 invoke() 方法決定了怎麼樣處理代理傳遞過來的方法調用,裏面的三個參數分別代表
// proxy 代理對象
// method 代理對象調用的方法
// args 調用的方法中的參數
// Proxy 動態產生的代理會調用 InvocationHandler 實現類,因此 InvocationHandler 是實際執行者。
4、我們直接去創建一個客戶端去實現動態代理:
import java.lang.reflect.Proxy;
public class test {
public static void main(String[] args) {
// TODO Auto-generated method stub
//代理目標類
Law law=new Person();
//生成代理對象,Proxy類通過調用newProxyInstance這個方法生產代理對象
//動態代碼涉及了一個非常重要的類 Proxy,通過 Proxy 的靜態方法 newProxyInstance 纔會動態創建代理。
//它的 3 個參數意義。
//loader即這裏的Law.class.getClassLoader() 自然是類加載器
//interfaces即new Class[]{Law.class} 代碼要用來代理的接口
//h即new MyHandler(law) 一個 InvocationHandler 對象
Law proxy=(Law)Proxy.newProxyInstance(Law.class.getClassLoader(), new Class[] {Law.class}, new MyHandler(law));
proxy.law();
}
// 上面執行person類的結果,那麼如果律師接待第二個人Other呢?我們直接載加一個Other對象,然後客戶端直接加下面代碼就可以了
Law law1=new Other();
Law proxy1=(Law)Proxy.newProxyInstance(Law.class.getClassLoader(), new Class[]{Law.class}, new MyHandler(law1));
proxy1.law();
}
生成代理對象Proxy類通過調用newProxyInstance這個方法生產代理對象
動態代碼涉及了一個非常重要的類 Proxy,通過 Proxy 的靜態方法 newProxyInstance 纔會動態創建代理。
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
//它的 3 個參數意義。
//loader即這裏的Law.class.getClassLoader() 自然是類加載器
//interfaces即new Class[]{Law.class} 代碼要用來代理的接口
//h即new MyHandler(law) 一個 InvocationHandler 對象
添加的第二個人Other:
public class Other implements Law {
@Override
public void law() {
// TODO Auto-generated method stub
System.out.println(this.getClass().getSimpleName()+"目標類去法庭申述");
}
}
運行結果:
上面就演示完靜態代理模式和動態代理模式了,如果還是很懵,可以去https://www.jianshu.com/p/d9e1641fbb6c查看,那裏有詳細介紹Proxy.newInstance(…)、getProxyClass0(loader, intfs)源碼
動態代理模式和靜態代理模式的應用場景:
靜態代理主要用來處理少部分類的託管或者擴展。靜態代理對於被代理的對象很固定,我們只需要去代理一個類或者若干固定的類,數量不是太多的時候,可以使用,而且其實效果比動態代理更好。
動態代理在運行期間動態生成代理類,需要消耗的時間會更久一點。優點是可以做很多類的擴展,而且如果一個類的接口發生了變化,那麼靜態代理這時候修改起來就很麻煩了。這就是說動態代理靈活的原因。
動態代理主流的實現有兩種,一種是基於接口的Java Proxy的代理機制,一種是基於繼承的cglib代理機制。兩種也都有其應用場景。