动态代理 ---- 框架基础技术
JSK動態代理
內容導航
- 代理模式 proxy pattern
- proxy
- proxy pattern
- proxy role
- 靜態代理 static proxy
- 靜態代理的缺點
- dynamic proxy 動態代理
- 反射 -- Method
- 動態代理步驟
- 動態代理實例
Javaweb — 深化一下反射,之后會再看一下線程和并發
java設計模式 — 代理模式: 動態代理jdk實現
JQuery雖然被新的技術取代了,但是通過實例就發現相比原生的JS,JQuery簡化了很多;接下來再來看看動態代理— 其實就是之前的反射機制,J2EE中使用過多次了,這里正式來康康
動態代理 — 基于反射機制的;之前使用過多次反射,尤其是幾個工具類,第一個就是DBUtil使用了簡單的forname方式進行類加載;之后再JSONUtil中使用了Class和Field;和方法一樣設置可以access變可獲取私有的屬性,正常的情況下是獲取不到的
代理模式 proxy pattern
proxy
代理就比如中介一樣,幫助去實現某些操作,也就是允許客戶端通過這個服務與另外一個end system進行非直接的服務,所以說路由器等就相當于是一個代理
從中介來分析: 比如房產中介,中介和代理所做的事情都是一致的,那就是招攬客人; 中介是房源的代理,房源是目標;這個過程就是 【客戶 -----> 中介 ------> 房源】,中介是代理,會收費的;盡管如此,還是有很多中介存在;因為中介是專業的、非常的方便;其次就是客戶不能獨自找到房源,或者找房源十分困難
在實際開發中,也就有了代理的情況,也就是對客戶----代理 ---- 目標鏈式關系的抽象,比如有一個類A,還有一個類C,如果A類的功能需要調用C類,但是C類不允許A調用,所以這個時候可以找一個代理類B,B訪問C;A再訪問B即可,完成功能 【比如項目的短信驗證碼的功能,項目是不可能直接發送短信的,短信只有電信運營商才能發送,所以找一個中介關聯公司 項目 ------------- 關聯公司、中介 ------ 中國移動、電信、聯通】
proxy pattern
為其他對象提供一種代理可以控制對這個對象的訪問,在某些情況下,一個對象不舍和或者不能直接引用另一個對象,而代理對象可以在客戶類和目標對象之間起到中介的作用。使用代理模式,是為了在不修改目標對象的基礎上,增強業務邏輯,客戶類對目標對象的訪問時通過訪問代理對象來實現
proxy role
上面介紹了代理模式,那么其作用是什么呢?
代理模式分為靜態代理和動態代理,二者的不同?
靜態代理 static proxy
這里來做一個例子來模擬一下代理,比如現在抽象一個關系鏈條 : 用戶到商家位置買雪糕; 商家的雪糕從廠家進的;現在用戶想要購買雪糕,實現功能【不能直接從廠家拿雪糕】,那么實現這個功能: 首先需要創建一個接口,定義賣雪糕的方法,展示商家和廠家的做的事情;其次創建廠家和商家類都要實現接口,最后創建客戶類,調用商家的方法買一個雪糕
通過代理模式才能進行功能增強,也就是擴展功能;對擴展開發,對修改關閉
package cfeng.proxy;//表示功能,廠家和商家都要實現這個方法 public interface IcecreamSell {//定義方法,售賣雪糕param表示單價float sell(int amount);//可以定義其他的方法 }這里就是接口,商家和廠家都會實現這個接口
package cfeng.proxy;public class CfengFactory implements IcecreamSell { //廠家不支持用戶的單獨購買@Overridepublic float sell(int amount) {//簡單return即可return 85.0f;}}廠家繼承了接口,對接口進行了繼承,完成了雪糕的銷售
package cfeng.proxy;public class Merchant implements IcecreamSell {//聲明商家所代理的生產雪糕廠家private IcecreamSell factory = new CfengFactory();@Overridepublic float sell(int amount) {//向廠家調用訂單,告訴廠家,讓其發貨float pri = factory.sell(amount);//商家作為中間商需要加價pri += 25; //這部分代碼就是屬于功能增強,因為原來只會有上面的代碼,這里都是額外的,易于擴展//返回給用戶價格return pri;} }商家也實現了接口,這里商家完成了兩件事情,第一件事情就是調用廠家的方法,第二件事就是進行加價;加價這部分【除了原來的方法的調用之外】,都是屬于功能的增強的部分
package cfeng.proxy;public class Client {//從商家購買雪糕;不能直接從public static void main(String[] args) {Merchant mercha = new Merchant();System.out.println(mercha.sell(1));} }最后就是客戶類,客戶通過調用商家【代理】的【增強之后的】方法,完成了業務;這里的代理可以有多個,因為可以有多個商家,它們所做的處理不同;所以不同的代理可以完成不同的擴展
因此: 代理的最主要的功能就是 : 目標類的方法的調用【代理是沒有直接的目標的功能的】, 功能的增強
靜態代理的缺點
-
靜態代理的目標固定,所以如果有很多目標,那就需要很多個代理,這樣代碼冗雜了
-
當接口中功能增加了,或者修改了,因為眾多的代理實現類,那么修改的工作量大,這樣就容易出現問題
dynamic proxy 動態代理
在程序的執行過程中,創建代理對象,動態指定代理目標類;動態代理就是一種創建對象能力,不用創建類,就可以獲得對象 ------ 就是反射機制,獲得類的字節碼,再invoke即可; 動態代理是指代理類對象在程序運行時有JVM根據根舍機制動態生成的,動態代理不需要定義代理類的java源文件,動態獲取類,(比如通過對象,通過完整的類名) 動態創建class字節碼加載到JVM
動態代理的方式:
- JDK 動態代理: 使用java反射包中的類和接口實現動態代理的功能 反射包java.lang.reflect : 其中的三個類Method,InvocationHandler、Proxy
- cglib動態代理 : cglib時第三方工具庫,創建代理對象,cglib的原理就是繼承,通過繼承目標類,創建其子類,在子類中重寫超類的方法,實現功能的修改 — 所以父類不能是final的;cglib在很多框架中使用【比如mybatis、springAOP就會使用
反射 – Method
之前分享反射的時候著重分享了幾個類,首先就是Class類,承載的是類的字節碼;之后還有Method類表示的是類中的方法【公私均可】,Field類表示的是類的屬性【公私均可】; 但是私有的一定要設置可訪問,setAccess;這是之前最經常使用的
這里可以用反射的方式實現之前的客戶類
public class Client {//從商家購買雪糕;不能直接從public static void main(String[] args) throws NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {/*Merchant mercha = new Merchant();System.out.println(mercha.sell(1));*///這里不通過創建對象的方式,如何得到上面的結果?反射即可 --- 使用Method//獲取類的字節碼 try {Class cls = Class.forName("cfeng.proxy.Merchant");//通過字節碼創建一個對象;通過構造方法創建Object obj = cls.getDeclaredConstructor().newInstance();//通過類的字節碼獲取到所有的方法Method method = cls.getDeclaredMethod("sell",int.class);//執行該方法通過method的invoke方法;第一個參數是對象,第二個是方法執行時的參數值 方法執行后的返回值Object o = method.invoke(obj, 1);//o就是函數的返回值System.out.println(o);} catch (ClassNotFoundException e) {e.printStackTrace();}} }- 這里通過類的字節碼創建對象,不能直接使用newInstance了,since version 9就過時了,需要先獲得構造器,之后再創建對象;
- 獲得類的方法可以通過Class的getDeclaredMethod方法,第一個參數時方法的名稱,第二個參數是方法的參數的字節碼;
- 執行方法通過Method的invoke方法,這個方法的第一個參數為該類的一個具體的對象,第二個參數是該方法傳入的具體的參數的值;返回值為方法的返回值【invoke 調用】
動態代理步驟
動態代理的目標不是固定的,根據代理的對象,動態創建代理類,這樣就避免了靜態代理中的類過多的問題,動態代理的實現方式---- 反射; 可以直接使用java.lang.reflect.Proxy; 動態代理的實現步驟如下
jdk動態代理要求目標對象必須要實現接口,沒有接口就實現不了動態代理
動態代理主要依托的reflect包中的三個類: invocationHandler、Method、Proxy
- invocationHandler【調用處理器】 接口 ------- 就是表明代理要做什么: 這是一個函數式接口,其中就一個方法invoke;invoke方法表示代理對象要執行的功能代碼;代理類要完成的功能就卸載invoke代碼中【目標方法的調用、功能增強】
-
Method類: 表示方法的,也就是通過Method可以執行某個目標類的方法,通過invoke方法執行目標的方法
-
Proxy類: 核心的對象,用于創建代理對象,之前創建對象都是new 類構造方法;現在可以直接使用Proxy類的方法,代替new的使用
這里可以看一下源碼
@CallerSensitivepublic static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h) {Objects.requireNonNull(h);final Class<?> caller = System.getSecurityManager() == null? null: Reflection.getCallerClass();Constructor<?> cons = getProxyConstructor(caller, loader, interfaces);return newProxyInstance(caller, cons, h);}ClassLoader loader : 類加載器,負責向內存中加載對象 --- 使用反射機制就可以獲得對象的classLoader ----比如對于類String ---》String.class.getClassLoader() 也就是獲得目標的類Class<?>[] interfaces 接口,目標對象實現的接口,也是通過反射獲取的InvocationHandler h :也就是之前實現的InvocationHandler接口,完成代理類的功能通過Proxy類的這個newProxyInstance方法就可以生成一個目標對象的代理對象
動態代理實例
這里就不另外編寫例子了,就以剛剛靜態代理的買雪糕的例子 ---- 用戶不能直接從從某一個具體的工廠買雪糕,那么就需要創建一個代理類商家來進行間接購買,同時商家增強了功能【抬價】 所以這里的核心應該是用戶和目標;商家是為了處理這個業務應運而生的類型
按照動態代理的固定步驟,首先要規范目標類【服務類】的行為,面向接口編程,定義一個接口 ----- 定義接口的目的就是因為,客戶類想使用的其實就是該接口定義的方法,所以真正要代理的是方法,而不是某一個具體實現類;多態的核心就是抽象,這樣就便于擴展而少修改
package cfeng.proxy;//表示功能,廠家和商家都要實現這個方法 public interface IcecreamSell {//定義方法,售賣雪糕param表示單價float sell(int amount);//可以定義其他的方法 }這個接口也就是為了完成客戶類想要實現的功能;之后定義實現類來實現這個功能接口
package cfeng.proxy;public class CfengFactory implements IcecreamSell { //廠家不支持用戶的單獨購買@Overridepublic float sell(int amount) {//簡單return即可System.out.println("執行服務接口的具體實現類的實現方法");return 85.0f;}}本來按照靜態代理,接下來就要手動去創建針對這個服務類的代理類;但是動態代理接下來就是創建InvocationHandler接口的實現類
這里的參數中,method就代表的是目標方法,args代表的就是目標方法的參數,所以為了執行方法,這里需要執行Method的invoke方法,需要傳入目標類型的對象,這里就使用帶有參數的構造方法實現傳入目標對象
package cfeng.proxy;import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method;/*** 完成代理類要執行的功能,這里需要注意的是后兩個對象就是目標參數,其實就是接口中的方法---代理方法而不是代理具體的實現類* 通過構造器獲得方法執行的具體的對象【多態】*/ public class SellHandler implements InvocationHandler {// sell功能的處理器【代理功能】private Object obj;public SellHandler(Object obj) {super();this.obj = obj;}// 完成代理類執行的功能@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {Object result = null; //目標方法的返回值// 執行目標方法result = method.invoke(obj, args);//進行功能增強if(result != null) {float pri = (float)result;pri += 25;result = pri;}System.out.println("你獲得2元優惠");return result;} }第四步就是利用Proxy類的靜態方法生成代理對象;代理對象的目的也是執行接口中的sell方法,所以類型是接口的類型
package cfeng.proxy;import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Proxy;public class Client {//從商家購買雪糕;不能直接從public static void main(String[] args) throws NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {//創建需要代理的目標對象IcecreamSell fact = new CfengFactory();//創建調用處理器對象InvocationHandler handler = new SellHandler(fact);//創建代理對象 ---- obj.getClass 類.class Class.forName()IcecreamSell proxy = (IcecreamSell)Proxy.newProxyInstance(fact.getClass().getClassLoader(), fact.getClass().getInterfaces() , handler);//通過代理對象執行方法Object o = proxy.sell(1);System.out.println(o);} }單從這里來看,這里的動態代理就是類似于一種別樣的重載,比如sell;給出的代理類還是有接口中的方法;JDK的動態代理必須有接口,Proxy的第二個參數就一定是接口執行服務接口的具體實現類的實現方法 執行目標接口實現類的功能方法 你獲得2元優惠 執行代理對象的功能增強代碼 110.0 執行代理對象的功能增強代碼只能說動態代理和JDBC一樣固化,非常明確的幾個步驟
首先就是編寫功能接口,【多態】實現接口;
之后就是實現invocationHandler接口來封裝代理的功能 — 調用原方法、功能增強 ---- 最后就是使用Proxy的靜態方法生成代理對象;這個代理對象就是最開始定義的接口類型的;它的目的還是有這個sell功能 ----- 代理可以在不改變原有的功能的前提下,增加新的功能; 代理類和接口實現類都是接口類型的🎄
總結
以上是生活随笔為你收集整理的动态代理 ---- 框架基础技术的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 2020年全球半导体清洗设备发展现状、竞
- 下一篇: 《自然语言处理:基于预训练模型的方法》读