使用一個代理將對象包裝起來, 而後用該代理對象取代原始對象。任何對原始對象的調用都要經過代理。代理對象決定是否以及什麼時候將方法調用轉到原始對象上。java
簡單例子:api
小強意外成爲了一個網紅明星,此時他請了一個專業的經紀人進行各類事務的管理,小強的演出活動啊都得通過經紀人的贊成。此時小強是被代理對象,經紀人是代理對象。數組
此時快樂大本營想請小強去參加節目,須要找到小強的經紀人,小強的經紀人而後進行排擋,找小強去完成唱歌,才藝展現等動做。ide
代理對象和被代理對象(目標對象)實現同一接口。優勢是不用修改目標對象的源碼,擴展其功能。測試
建立一個接口,這個接口是代理工廠(代理類)和Nike工廠(被代理類)的公共接口,this
經過代理工廠的方法調用同時也調用了Nike工廠的同名方法,完成不修改被代理類的源碼而擴展其功能。.net
共同接口代理
interface ClothFactory{ void produceCloth(); }
代理類code
// 代理類 class ProxyClothFactory implements ClothFactory { private ClothFactory factory;//用被代理類對象進行實例化 public ProxyClothFactory(ClothFactory factory) { this.factory = factory; } @Override public void produceCloth() { System.out.println("代理工廠作一些準備工做"); factory.produceCloth(); System.out.println("代理工廠作一些後續的工做"); } }
被代理類對象
// 被代理類 class NikeClothFactory implements ClothFactory{ @Override public void produceCloth() { System.out.println("nike工廠生產運動服"); } }
測試代碼
public class StaticProxyTest { public static void main(String[] args) { // 建立被代理類對象 NikeClothFactory nikeClothFactory = new NikeClothFactory(); // 建立代理類對象 ProxyClothFactory proxyClothFactory = new ProxyClothFactory(nikeClothFactory); proxyClothFactory.produceCloth(); } }
輸出結果
代理工廠作一些準備工做
nike工廠生產運動服
代理工廠作一些後續的工做
動態代理是指客戶經過代理類來調用其它對象的方法,而且是在程序運行時根據須要動態建立目標類的代理對象。
代理對象不須要實現接口,可是要求被代理對象(目標對象)必須實現接口,不然不能使用動態代理。
Proxy
:專門完成代理的操做類,是全部動態代理類的父類。經過此類爲一個或多個接口動態地生成實現類。
static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h)
直接建立一個動態代理對象
參數詳解:
ClassLoader loader
:和被代理對象使用相同的類加載器Class<?>[] interfaces
:和被代理對象具備相同的行爲,實現相同的接口InvocationHandler h
:獲得InvocationHandler接口的實現類實例實現動態代理,要解決的問題
具體步驟以下:
建立被代理類和接口
接口:Human接口
//共同接口 interface Human{ String getBelief(); void eat(String food); }
被代理類:SuperMan類
// 被代理類 class SuperMan implements Human{ @Override public String getBelief(){ return "我能飛"; } @Override public void eat(String food){ System.out.println("我喜歡吃"+food); } }
建立一個用來生產代理類的工廠,該工廠有一個靜態方法getProxyInstance
能夠返回一個代理類的對象,該方法需傳入被代理類對象。
class ProxyFactory{ // 調用此方法,返回一個代理類的對象。須要傳入被代理類對象 public static Object getProxyInstance(Object obj){ MyInvocationHandler handler = new MyInvocationHandler(); handler.bind(obj); // 哪一個類加載的?被代理類 // 被代理類實現了哪一個接口?獲得被代理類實現的所有接口 // 獲得InvocationHandler接口的實現類實例 return Proxy.newProxyInstance(obj.getClass().getClassLoader(),obj.getClass().getInterfaces(),handler); } }
建立一個實現接口InvocationHandler
的類,實現inoke方法,以完成代理的具體操做。當咱們經過代理類的對象調用方法a時,就會自動的調用以下方法invoke(),將被代理類要執行的方法a的功能聲明在invoke()中。
class MyInvocationHandler implements InvocationHandler{ private Object obj;//須要使用被代理類的對象進行賦值 public void bind(Object obj){ this.obj = obj; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // obj:被代理類對象 // method:即爲代理類對象調用的方法,此方法做爲被代理類對象要調用的方法 // args: 方法調用時所須要的參數 Object returnValue = method.invoke(obj,args); //該返回值做爲當前類中的invoke()的返回值 return returnValue; } }
測試類
public class ProxyTest { public static void main(String[] args) { SuperMan superMan = new SuperMan();//建立一個被代理類對象 // 動態建立代理類 Human proxyInstance = (Human)ProxyFactory.getProxyInstance(superMan); String belief = proxyInstance.getBelief(); System.out.println(belief); proxyInstance.eat("肉夾饃"); } }
輸出結果
咱們發現,只須要知道接口和被代理類,就能實現建立一個代理類。把上面靜態代理部分的例子拿過來
NikeClothFactory nike = new NikeClothFactory(); ClothFactory proxyInstance1 = (ClothFactory) ProxyFactory.getProxyInstance(nike); proxyInstance1.produceCloth(); //nike工廠生產運動服
動態代理
特色:字節碼隨用隨建立,隨用隨加載;
做用:不修改源碼的基礎上對方法加強。
提供者: JDK 官方的 Proxy 類。
要求:被代理類最少實現一個接口。
實現步驟
建立一個接口,該接口能夠理解成對生產廠家的規範
/** * 對生產廠家要求的接口 */ public interface IProducer { /** * 銷售 * @param money */ public void saleProduct(float money); /** * 售後 * @param money */ public void afterService(float money); }
代理類:是一個生產者,它實現了接口,具備接口中的方法,也就是說符合生產廠家的要求,產品銷售和售後是ok的。
/** * 一個生產者 */ public class Producer implements IProducer{ /** * 銷售 * @param money */ public void saleProduct(float money){ System.out.println("銷售產品,並拿到錢:"+money); } /** * 售後 * @param money */ public void afterService(float money){ System.out.println("提供售後服務,並拿到錢:"+money); } }
模擬一個消費者,若是該消費者若是沒有經過代理商去買電腦,而是直接去到廠家處購買。但現實中,不多有人這樣購買。通常都是廠家把產品交給代理商進行銷售以及提供售後服務,顧客只需跟代理商進行交易,而不直接與廠家進行聯繫。
沒有代理以前
Producer producer1 = new Producer(); producer.saleProduct(10000f);//銷售產品,並拿到錢:10000.0
有了代理,代理要從中提取20%的手續費。那麼生產者只能獲得80%的錢
public class Client { public static void main(String[] args) { final Producer producer = new Producer(); IProducer proxyProducer = (IProducer) Proxy.newProxyInstance(producer.getClass().getClassLoader(), producer.getClass().getInterfaces(), new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //提供加強的代碼 Object returnValue = null; //1.獲取方法執行的參數 Float money = (Float)args[0]; //2.判斷當前方法是否是銷售 if("saleProduct".equals(method.getName())) { returnValue = method.invoke(producer, money*0.8f); } return returnValue; } }); proxyProducer.saleProduct(10000f); } }
涉及的參數:
Proxy類
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) ClassLoader:類加載器 代理誰寫誰的類加載器,它是用於加載代理對象字節碼的。和被代理對象使用相同的類加載器。固定寫法。 Class[]:字節碼數組,它是用於讓代理對象和被代理對象有相同方法。固定寫法。 InvocationHandler:用於提供加強的代碼 它是讓咱們寫如何代理。咱們通常都是些一個該接口的實現類,一般狀況下都是匿名內部類,但不是必須的。此接口的實現類都是誰用誰寫。
InvocationHandler接口
public Object invoke(Object proxy, Method method, Object[] args) 做用:執行被代理對象的任何接口方法都會通過該方法 方法參數的含義 proxy 代理對象的引用 method 當前執行的方法 args 當前執行方法所需的參數
提供者:第三方的 cglib。
要求:被代理類不能是最終類(用 final 修飾的類)。
涉及的類:Enhancer
如何建立代理對象:使用Enhancer類中的create(Class,Callback)方法
create方法的參數:
Class:字節碼,它是用於指定被代理對象的字節碼。
Callback:如何代理,即就是用於提供加強的代碼。咱們通常都是寫一個該接口的實現類,一般狀況下都是匿名內部類,但不是必須的。此接口的實現類都是誰用誰寫。咱們通常寫的都是該接口的子接口實現類:MethodInterceptor
仍是消費者買產品的例子,只不過不讓它實現接口。
舉例:
生產者
/** * 一個生產者 */ public class Producer { /** * 銷售 * @param money */ public void saleProduct(float money){ System.out.println("銷售產品,並拿到錢:"+money); } /** * 售後 * @param money */ public void afterService(float money){ System.out.println("提供售後服務,並拿到錢:"+money); } }
消費者
/** * 模擬一個消費者 */ public class Client { public static void main(String[] args) { final Producer producer = new Producer(); Producer cglibProducer = (Producer) Enhancer.create(producer.getClass(), new MethodInterceptor() { /** * 執行被代理對象的任何方法都會通過該方法 * @param proxy * @param method * @param args * 以上三個參數和基於接口的動態代理中invoke方法的參數是同樣的 * @param methodProxy :當前執行方法的代理對象 * @return * @throws Throwable */ @Override public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { //提供加強的代碼 Object returnValue = null; //1.獲取方法執行的參數 Float money = (Float)args[0]; //2.判斷當前方法是否是銷售 if("saleProduct".equals(method.getName())) { returnValue = method.invoke(producer, money*0.8f); } return returnValue; } }); cglibProducer.saleProduct(12000f); } }
參考連接: