java的動態代理功能是用來解決現有類功能不足,但咱們又不想去修改現有類方法的問題,或者就是咱們沒法直接使用現有類的狀況。它的實現方式有兩種,第一種是jdk自帶的動態代理功能,它的實現前提是現有類必須擁有一個接口,由於它是經過對現有類接口的實現來完成的。第二種方式是cglib,這是一個開源工具包,它的實現是經過繼承現有類,而後重寫現有類的方法實現的。它們在spring與mybatis框架中均有使用。學習它們的前提是你要對java的反射機制有必定的認知。本篇只介紹jdk原生的動態代理。java
客戶:購買u盤spring
代理商:淘寶mybatis
u盤工廠:金士頓,三星等app
用戶想要購買u盤,是不能夠直接去廠家購買的,須要經過淘寶等代理商進行購買,抽象成程序就是,u盤工程就是目標類,也就是現有類,淘寶就是代理類,客戶就是用戶類.用戶須要調用淘寶的方法進行購買u盤,而淘寶又須要調用工廠的方法進行購買.若是咱們直接建立一個淘寶類,讓它去代理金士頓工廠,就會出現一個問題,那就是三星工廠由誰去代理,總不能再建立一個代理類吧,因此就可使用動態代理的方式,在程序運行時期,根據不一樣的狀況去建立一個合適的代理類框架
首先咱們須要一個工廠的接口UsbFactoryide
package com.hzq.application.targetclass; public interface UsbFactory { //售賣u盤,並返回實際價格的方法 float sell(int num); }
第二步就是建立一個UsbFactory的實現類UsbKingFactory表明金士頓工廠工具
package com.hzq.application.targetclass.impl; import com.hzq.application.targetclass.UsbFactory; public class UsbKingFactory implements UsbFactory { @Override public float sell(int num) { float onePrice = 30.0F; float price = onePrice * num; System.out.println("向廠家購買花費了"+price+"元"); return price; } }
如今須要咱們代理的目標類已經好了,接下來就是重點了,jdk動態代理的實現方法(這個實現套路是固定的)。第三步,建立一個MyInvocationHandler類去實現接口InvocationHandler。這個接口只有一個須要咱們去實現的方法,那就是invoke()方法,它有3個參數,第一個參數其實就是咱們後面經過動態代理去建立的代理類,這個參數不須要咱們的參與,不用去理會,第二個參數是咱們要去加強的目標類的方法,第三個是該方法的參數.invoke內部就是咱們要對目標類的方法加強的具體邏輯,也就是咱們想怎樣去加強它,在本示例中咱們是對用戶購買的商品收取20元中介費,而後送給用戶一張10元的優惠券學習
package com.hzq.application.proxy; import com.hzq.application.targetclass.UsbFactory; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; public class MyInvocationHandler implements InvocationHandler { private UsbFactory factory ; @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //1.調用目標類的售賣方法,而後返回價格 Float price = (Float) method.invoke(factory, args); //2.咱們在以後進行自定義的加強,這裏咱們是抽取20元的中介費用,而後送給客戶一張10元優惠券 price += 20; System.out.println("代理商送您一張10元優惠券"); System.out.println("客戶購買商品花費了"+price+"元"); return price; } public MyInvocationHandler(UsbFactory factory) { this.factory = factory; } }
最後一步就是去使用jdk動態代理來在運行期建立一個代理類對象了,在代碼中第第三步是重點,咱們若是咱們想要建立一個動態代理類,就必需要調用Proxy類的靜態方法newProxyInStance()方法,這個方法會返回給咱們一個代理類對象,咱們實際購買商品也是經過這個代理類來進行購買的。newProxyInstance()方法一共有三個參數,第一個參數是一個類加載器對象,類加載器對象能夠經過目標類的Class對象去調用getClassLoader()方法去獲取,其實也可使用其餘你自定義的類的Class對象去獲取,由於最後得到的都是同一個類加載器實例,但爲了代碼的易讀性,就使用了目標類。第二個參數是目標類的接口類型,第三個參數就是咱們自定義的MyInvocationHandler類的實例對象了,它裏面封裝這咱們具體的加強邏輯代碼。方法調用完成後,若是沒有出現問題就會返回給咱們一個代理類的實例,以後咱們就能夠去使用這個代理類的。this
package com.hzq.application; import com.hzq.application.proxy.MyInvocationHandler; import com.hzq.application.targetclass.UsbFactory; import com.hzq.application.targetclass.impl.UsbKingFactory; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Proxy; public class ProxyMain { public static void main(String[] args) { //1.實例化一個金士頓工廠對象 UsbFactory usbFactory = new UsbKingFactory(); //2.實例化一個MyInvocationHandler對象 InvocationHandler handler = new MyInvocationHandler(usbFactory); //3.經過jdk動態代理建立出代理對象 UsbFactory proxyInstance = (UsbFactory) Proxy.newProxyInstance(usbFactory.getClass().getClassLoader(), usbFactory.getClass().getInterfaces(), handler); //4.經過代理對象來購買商品 float price = proxyInstance.sell(3); System.out.println(price); } }
最後,當咱們須要代理其餘工廠去售賣u盤時,只須要將main方法中的第一步實例化的金士頓工廠改爲其餘品牌的工廠便可,固然前提是這個工廠的類實現了UsbFactory接口。代理
當咱們去使用jdk動態代理時,首先須要確保目標類實現了接口。以後的使用步驟就是: