JDK动态代理模式这篇就够了
JDK動態代理
第一章 代理的介紹
介紹:我們需要掌握的程度
動態代理(理解) 基于反射機制
掌握的程度:
1.什么是動態代理?
2.動態代理能夠做什么?
1.1 什么是代理
-
代理,在我們日常生活之中就有體現,代購,中介,換ip,商家等等.
-
比如有一家美國的大學,可以對全世界招生.留學中介(代理 )
-
留學中介(代理):幫助這家美國的學校招生,中介是學校的代理中介是代替學校完成招生功能
-
代理特點
- 中介和代理他們要做的事情是一致的:招生
- 中介是學校代理,學校是目標
- 家長-------->中介(學校介紹,辦理入學手續)---------->美國學校
- 中介是代理,不能白干活,需要收取費用
- 代理不讓你訪問到目標
1.2 為什么要找中介
為什么要找中介?
1.中介是專業的,方便.
2.家長現在不能自己去找學校。家長沒有能力訪問學校.或者美國學校不接收個人來訪
買東西都是商家賣, 商家是某個商品的代理, 你個人買東西,肯定不會讓你接觸到廠家的.
1.3 代理在開發中的應用
1.你有a類,本來是調用c類的方法,完成某個功能。但是c不讓a調用。
? a——不能調用c的方法
? 在a和c直接創建一個b代理,c讓b訪問
? a——訪問b——訪問c
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-d5t7z5Hp-1648124617810)(C:\Users\Dell\AppData\Roaming\Typora\typora-user-images\image-20220324103732980.png)]
2.實際例子:登錄,注冊有驗證碼,驗證碼是手機短信
? 中國移動、聯通能發短信
? 中國移動聯通有子公司或者關聯公司,他們面向社會提供短信的發送功能
3.張三項目發送短信——子公司或者關聯公司——中國移動、聯通
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-tMAmvfki-1648124617811)(C:\Users\Dell\AppData\Roaming\Typora\typora-user-images\image-20220324103923767.png)]
第二章 靜態代理
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-2P1WTaWK-1648124617811)(C:\Users\Dell\AppData\Roaming\Typora\typora-user-images\image-20220324104224384.png)]
2.1 實現靜態代理的方式
2.1.1 靜態代理
1)代理類是自己手工實現的,自己創建一個java類,表示代理類
2)同時你所要代理的目標
- 特點:1.實現簡單
? 2.容易理解
-
案例一:模擬一個用戶購買u盤的行為。
用戶:客戶端類
商家:代理,代理某個品牌的u盤。
廠家:目標類。
三者的關系:用戶(客戶端)-—-商家(代理)-—-廠家(目標)
商家和廠家都是賣u盤的,他們完成的功能是一致的,都是賣u盤。
2.1.2 實現步驟
1.創建一個接口,定義賣u盤的方法,表示你的廠家和商家做的事情
2.創建廠家類,實現1步驟的接口
3.創建商家,就是代理,也需要實現1步驟中的接口
4.創建客戶端類,調用商家的方法買一個u盤
2.3 具體實現
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-omhUDZHI-1648124617812)(C:\Users\Dell\AppData\Roaming\Typora\typora-user-images\image-20220324111535407.png)]
1.創建一個接口,定義賣u盤的方法,表示廠家和商家做的事情
package com.gz.service;public interface UsbSell {/*** 定義一個方法 參數 amount:表示一次購買的數量,暫時不用* 返回值表示一個u盤的價格* @param* @return*/float sell(int amount); }2.創建廠家類,實現1的接口
package com.gz.factory;import com.gz.service.UsbSell;//目標類:金士頓廠家,不接受用戶的單獨購買 public class UsbKinFactory implements UsbSell {@Overridepublic float sell(int count) {//一個128G的U盤是85元.//后期根據amount,可以實現不同的價格,例如10000個,單價是80,50000個75return 85.0f;} }3.創建商家(代理),也需要實現1接口
package com.gz.shangjia;import com.gz.factory.UsbKinFactory; import com.gz.service.UsbSell; //淘寶是一個商家,代理金士頓U盤的銷售 public class TaoBaoKin implements UsbSell {// 聲明 商家代理的廠家具體是誰private UsbKinFactory factory = new UsbKinFactory();// 實現銷售U盤功能@Overridepublic float sell(int amount) {// 向廠家發送訂單,告訴廠家,我買了U盤,廠家發貨// 發送給工廠,我需要的訂單,返回報價float money = factory.sell(amount);// 商家需要加價也就是代理要增加價格money = money + 20;//在目標類的方法調用后,你做的其他功能,都是增強的意思System.out.println("給用戶一個返現紅包");//功能增強,商家加價賣出,或者給用戶返現紅包return money;} }4.創建客戶類,調用商家的方法買U盤
package com.gz;import com.gz.shangjia.TaoBaoKin; import com.gz.shangjia.WeiShang;public class Main {public static void main(String[] args) {// write your code here// 創建代理的商家淘寶對象TaoBaoKin taoBaoKin = new TaoBaoKin();// 我只向淘寶買一件產品,得到報價float money = taoBaoKin.sell(1);System.out.println("淘寶買一個金士頓U盤價格:"+money);WeiShang weiShang = new WeiShang();float sell = weiShang.sell(1);System.out.println("微商買一個金士頓U盤價格:"+sell);} }2.4 使用代理的作用
2.5靜態代理的優缺點
-
優點:
- 實現簡單
- 容易簡單
-
缺點:當你的項目中,目標類的代理類很多的時候,有一下的缺點
-
當目標類增加了,代理類可能也需要成倍的增加
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-BUP9M6lk-1648124617813)(C:\Users\Dell\AppData\Roaming\Typora\typora-user-images\image-20220324112348643.png)]
-
當你的接口中功能在增加了,或者修改了,會影響眾多的實現類,廠家類,代理都需要修改,影響比較多.
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-E2Zpdibs-1648124617814)(C:\Users\Dell\AppData\Roaming\Typora\typora-user-images\image-20220324112253369.png)]
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-vO8F9rVw-1648124617816)(C:\Users\Dell\AppData\Roaming\Typora\typora-user-images\image-20220324112423290.png)]
第三章 動態代理
2.1 靜態和動態模式的對比
在靜態代理中目標很多的時候,可以使用動態代理,避免靜態代理的缺點
在靜態代理中目標類很多時候,可以使用動態代理,避免靜態代理的缺點。
-
動態代理的特點:
- 動態代理中目標類即使很多,代理類數量可以很少
- 當你修改了接口中的方法時,不會影響代理類。
-
動態代理的作用:
在程序執行過程中,使用jdk的反射機制,創建代理類對象,并動態的指定要代理目標類。換句話說:動態代理是一種創建java象的能力,讓你不用創建 TaoBao類就能創建代理類對象,除去了中間商,在java中,要想創建對象就必須:
2.2 動態代理的介紹
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-yKQcbMDD-1648124617817)(C:\Users\Dell\AppData\Roaming\Typora\typora-user-images\image-20220324184245436.png)]
動態代理的實現:
2.3 JDK動態代理
jdk動態代理:
1.反射, Method類,表示方法。類中的方法。通過Method可以執行某個方法
2.jdk動態代理的實現
反射包java.lang. reflect,里面有三個類:InvocationHandler,Method,Proxy
2.3.1 Interface InvocationHandler
public interface InvocationHandlerInvocationHandler是由代理實例的調用處理程序實現的接口 。
每個代理實例都有一個關聯的調用處理程序。 當在代理實例上調用方法時,方法調用將被編碼并分派到其調用處理程序的invoke方法。
1)InvocationHandler接口(調用處理器):就一個方法 invoke()
? invoke():表示代理對象要執行的功能代碼。你的代理類要完成的功能就寫在invoke()方法中。
- 代理類完成的功能
- 調用目標方法,執行目標方法的功能
- 功能增強,在目標方法調用時,增加功能
方法原型:
參數:object proxy:jdk創建的代理對象,無需賦值。Method method:目標類中的方法,jdk提供method對象的object[]args:目標類中方法的參數,jdk提供的。 Object invoke(Object proxy,方法 method,Object[] args)throws Throwable處理代理實例上的方法調用并返回結果。2.3.2 Method類
2)Method類:表示方法的,確切的說就是目標類中的方法。
作用:通過 Method可以執行某個目標類的方法, method. invoke(目標對象,方法的參數);
object ret= method. invoke(service22,"李四")
說明:method.invoke()就是為了用來執行目標方法的,等同于靜態代理中的
// 向廠家發送訂單,告訴廠家,我買了U盤,廠家發貨// 發送給工廠,我需要的訂單,返回報價float price = factory.sell(amount);2.3.3 Proxy類
3)proxy類:核心的對象,創建代理對象。之前創建對象都是new類的構造方法()
現在我們是使用proxy類的方法,代替new的使用。
方法:靜態方法 newProxyInstance();
作用:創建代理對象,等同于靜態代理中的TaoBao taoBao=new TaoBao()
我們來觀察方法原型
public static Object newProxyInstance( ClassLoader loader,Class<?>[] interfaces,InvocationHandler h) throws IllegalArgumentException參數含義:
ClassLoader loader 類加載器,負責向內存中加載對象的,使用反射機制獲取對象的ClassLoader,如何獲取? 類 a, a.getCalss().getClassLoader(),目標對象的類加載器。
這里我們細分:每一個類都繼承Object類,在Object中有一個getClass方法,表示 類對象的運行時類的Class對象。 而Class類里面有一個public ClassLoader getClassLoader()方法
Class<?>[] interfaces: 接口,目標對象實現的接口,也是反射獲取的
InvocationHandler h : 我們自己寫的,代理類要完成的功能
返回值:也就是代理對象
第四章 實現動態代理步驟
第一步:創建接口,定義目標所需功能
public interface UsbSell {float sell(int amount); }第二步:創建目標類實現接口
public class UsbKingFactory implements UsbSell {@Overridepublic float sell(int amount) {System.out.println("目標類中,執行了sell目標方法");return 85.02f;} }我們寫了接口類,定義了功能,寫了代理類,實現了接口功能,按照以前的操作,現在就需要寫一個真正的代理類,創建對象.
第三步:創建Invocationhandler實現類
1.調用目標的方法
2.增強功能
public class MyHandle implements InvocationHandler {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {return null;} }我們就在以前代理類的實現方法上進行修改
//之前的代理類public class TaoBao implements usbSell {// 聲明 商家代理的廠家具體是誰private UsbKingFactory factory =new UsbKingFactory();@Override// 實現銷售U盤功能public float sell(int amount) {// 向廠家發送訂單,告訴廠家,我買了U盤,廠家發貨// 發送給工廠,我需要的訂單,返回報價float price = factory.sell(amount);// 商家需要加價也就是代理要增加價格price = price + 25;//在目標類的方法調用后,你做的其他功能,都是增強的意思System.out.println("淘寶再給你返回一個優惠券,或者紅包");// 增加的價格return price;}}調用目標的方法
//傳入是誰的對象,就給誰創建代理public MyHandle(Object target) {this.target = target;} invoke里面的設置Object res =null;// 向廠家發送訂單,告訴廠家,我買了U盤,廠家發貨// 發送給工廠,我需要的訂單,返回報價 // float price = factory.sell(amount);res = method.invoke(target,args); //待執行目標方法,執行后返回值第四步:創建代理對象,并把返回值轉換成接口類型
public class MainShop {public static void main(String[] args){ // 1.創建對象,使用Proxy // 2.創建目標對象UsbSell factory = new UsbKingFactory(); // 3.創建Invocationhandler對象InvocationHandler myHandle = new MyHandle(factory);// 4.創建代理對象UsbSell proxy = (UsbSell) Proxy.newProxyInstance(factory.getClass().getClassLoader(),factory.getClass().getInterfaces(),myHandle); // 通過代理執行方法float price = proxy.sell(1);System.out.println("通過動態代理對象,調用方法:" +price);} }執行成功.和之前動態代理模式一樣
第五章 動態代理的執行流程
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-NaxR9YCD-1648124617818)(C:\Users\Dell\AppData\Roaming\Typora\typora-user-images\image-20220324192011783.png)]
5.1 執行流程
當執行poxy.sell(1)時候調用的是哪里呢?是執行我們的proxy在創建時,指定的這個handler他里面的invoke()方法。(handler里面的invoke()方法的形參)此時sell()會賦給method,參數1會賦給args
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-0HfCKxBt-1648124617819)(C:\Users\Dell\AppData\Roaming\Typora\typora-user-images\image-20220324193917141.png)]
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-P9BqH7C3-1648124617819)(C:\Users\Dell\AppData\Roaming\Typora\typora-user-images\image-20220324201643212.png)]
然后就到了invoke()方法里面,之后到了method.invoke(target,args),這里所執行的是目標類的方法,誰把target傳給了invoke就執行誰的方法。這里我們傳的是目標類,因此他會去調用目標類中的方法,目標類方法執行完成之后會有一個返回值,返回給調用目標類的方法
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-mWR7mUHx-1648124617820)(C:\Users\Dell\AppData\Roaming\Typora\typora-user-images\image-20220324201421415.png)]
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-f9RzUga5-1648124617821)(C:\Users\Dell\AppData\Roaming\Typora\typora-user-images\image-20220324201852500.png)]
然后hadler里面的invoke方法結束之后,會返回一個值給客戶端的proxy.sell()方法
5.2 動態代理模板
第六章 項目中的應用
我們需要知道代理能做什么?
在不改變原來目標方法功能的前提下,可以在代理中增強自己的功能代碼,程序開發中的意思,
比如:你所在的項目,有一個功能是其他人(公司其他部門,其他小組的人)寫好的,你可以使用
//比如,同事開發一個GoNong類 GoNong.class , GoNong gn=new GoNong()//我們需要增加一個print方法我們發現這個功能現在還存在缺點,不能完全滿足我項目的需要,我需要在print()執行過后,需要自己再增加代理,使用什么方法那,肯定是代理,因為別人不會讓我們看源文件
執行步驟:
1.我們先建立一個接口功能,很簡單的一個功能
public interface HelloService {/*** 打印報表,報表* @param name* @return*/int print (String name); }2.我們再加個接口實現類
public class GoNeng implements HelloService {@Overridepublic int print(String name) {System.out.println("其他人寫好的這個方法!");return 2;} }3.我們如果相對上述功能進行修改,我們不可能直接去在原方法上進行修改
我們設置一個類來使用這個接口
public class MyApp {public static void main(String[] args){GoNeng gn = new GoNeng();int i = gn.print("nihao1");System.out.println("num" +i);} }問題是如果我們想修改這個功能改怎么辦?在不修改源代碼的基礎上,我們可以創建一個代理類,來增強這個類方法,
3.設置一個代理類實現功能的增強和代理
public class MyInvocationHandler implements InvocationHandler {private Object target=null;public MyInvocationHandler(Object target) {this.target = target;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 調用目標方法,執行print()得到2Object res = method.invoke(target,args); //返回為2的結果//我們可以把結果再乘以2if (res!=null){Integer num = (Integer) res;res = num*2; }return null;} }5.我們仍回到主方法里面進行測試
public class MyApp {public static void main(String[] args){ // GoNeng gn = new GoNeng(); // int i = gn.print("nihao1"); // System.out.println("num" +i);GoNeng goNeng = new GoNeng();InvocationHandler handler = new MyInvocationHandler(goNeng);HelloService proxy = (HelloService) Proxy.newProxyInstance(goNeng.getClass().getClassLoader(), goNeng.getClass().getInterfaces(),handler);int num = proxy.print("市場");System.out.println("我們期望得到的 num =="+num);} }測試System.out.println("動態代理類參數接口"+goNeng.getClass().getInterfaces()[0].getName());
動態代理類參數接口com.rango.service.HelloService 其他人寫好的這個方法! 我們期望得到的 num ==4完結
動態代理,必須要有接口出現,如果沒有,我們可以使用cglib實現
總結
以上是生活随笔為你收集整理的JDK动态代理模式这篇就够了的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: TiKV 集群版本的安全迁移
- 下一篇: 高频超声波开发板