動態代理的常用實作方式是反射,反射機制是 Java 語言提供的一種基礎功能,賦予程式在運行時自省(introspect,官方用語)的能力,通過反射我們可以直接操作類或者物件,比如獲取某個物件的類定義,獲取類宣告的屬性和方法,呼叫方法或者構造物件,甚至可以運行時修改類定義,
動態代理是一種方便運行時動態構建代理、動態處理代理方法呼叫的機制,很多場景都是利用類似機制做到的,比如用來包裝 RPC 呼叫、面向切面的編程(AOP),
JDK 自身提供的動態代理,就是主要利用了上面提到的反射機制,但動態代理不止有反射一種實作方式,還有其他的實作方式,比如利用傳說中更高性能的位元組碼操作機制,類似 ASM、cglib(基于 ASM,一個 Java 位元組碼操作框架)、Javassist 等,簡單來說,動態代理是一種行為方式,而反射或 ASM 只是它的一種實作手段而已,
JDK Proxy 和 CGLib 的區別主要體現在以下幾個方面:
JDK Proxy 是 Java 語言自帶的功能,無需通過加載第三方類實作;
Java 對 JDK Proxy 提供了穩定的支持,并且會持續的升級和更新 JDK Proxy,例如 Java 8 版本中的 JDK Proxy 性能相比于之前版本提升了很多;
JDK Proxy 是通過攔截器加反射的方式實作的;
JDK Proxy 只能代理繼承介面的類;
JDK Proxy 實作和呼叫起來比較簡單;
CGLib 是第三方提供的工具,基于 ASM 實作的,性能比較高;
CGLib 無需通過介面來實作,它是通過實作子類的方式來完成呼叫的,
什么是靜態代理
靜態代理是代理類在編譯期間就創建好了,不是編譯器生成的代理類,而是手動創建的類,在編譯時就已經將介面,被代理類,代理類等確定下來,,軟體設計中所指的代理一般是指靜態代理,也就是在代碼中顯式指定的代理,
下面我們通過一個簡單的案例,來了解下靜態代理,
Cat.java
123456789
COPY
/** * 靜態代理類介面, 委托類和代理類都需要實作的介面規范, * 定義了一個貓科動物的兩個行為介面,吃東西,奔跑, * 作為代理類 和委托類之間的約束介面 */public interface Cat { public String eatFood(String foodName); public boolean running();}
Lion.java
123456789101112131415161718192021222324252627282930
COPY
/** * 獅子 實作了貓科動物介面Cat, 并實作了具體的行為,作為委托類實作 */public class Lion implements Cat { private String name; private int runningSpeed; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getRunningSpeed() { return runningSpeed; } public void setRunningSpeed(int runningSpeed) { this.runningSpeed = runningSpeed; } public Lion() { } @Override public String eatFood(String foodName) { String eat = this.name + " Lion eat food. foodName = " + foodName; System.out.println(eat); return eat; } @Override public boolean running() { System.out.println(this.name + " Lion is running . Speed :" + this.runningSpeed); return false; }}
代理類角色(FeederProxy)
FeederProxy.java
12345678910111213141516171819202122232425262728
COPY
/** * 飼養員 實作Cat介面,作為靜態代理類實作,代理獅子的行為, * 代理類中可以新增一些其他行為,在實踐中主要做的是引數校驗的功能, */public class FeederProxy implements Cat { private Cat cat; public FeederProxy(){} public FeederProxy(Cat cat) { if (cat instanceof Cat) { this.cat = cat; } } public void setCat(Cat cat) { if (cat instanceof Cat) { this.cat = cat; } } @Override public String eatFood(String foodName) { System.out.println("proxy Lion exec eatFood "); return cat.eatFood(foodName); } @Override public boolean running() { System.out.println("proxy Lion exec running."); return cat.running(); }}
靜態代理類測驗
staticProxyTest.java
12345678910111213141516
COPY
/** * 靜態代理類測驗 */public class staticProxyTest { public static void main(String[] args) { Lion lion = new Lion(); lion.setName("獅子 小王"); lion.setRunningSpeed(100); /** * new 靜態代理類,靜態代理類在編譯前已經創建好了,和動態代理的最大區別點 */ Cat proxy = new FeederProxy(lion); System.out.println(Thread.currentThread().getName()+" -- " + proxy.eatFood("水牛")); proxy.running(); }}
靜態代理很好的詮釋了代理設計模式,代理模式最主要的就是有一個公共介面(Cat),一個委托類(Lion),一個代理類(FeederProxy),代理類持有委托類的實體,代為執行具體類實體方法,
代理模式就是在訪問實際物件時引入一定程度的間接性,因為這種間接性,可以附加多種用途,這里的間接性就是指客戶端不直接呼叫實際物件的方法,客戶端依賴公共介面并使用代理類, 那么我們在代理程序中就可以加上一些其他用途,
就這個例子來說在 eatFood 方法呼叫中,代理類在呼叫具體實作類之前添加System.out.println(“proxy Lion exec eatFood “);陳述句 就是添加間接性帶來的收益,代理類存在的意義是為了增加一些公共的邏輯代碼,
靜態代理的缺陷
代理類和委托類實作了相同的介面,代理類通過委托類實作了相同的方法,這樣就出現了大量的代碼重復,如果介面增加一個方法,除了所有實作類需要實作這個方法外,所有代理類也需要實作此方法,增加了代碼維護的復雜度,
代理物件只服務于一種型別的物件,如果要服務多型別的物件,勢必要為每一種物件都進行代理,靜態代理在程式規模稍大時就無法勝任了,
靜態代理一個代理只能代理一種型別,而且是在編譯器就已經確定被代理的物件,
JDK Proxy 和 CGLib 的使用樣例
JDK Proxy 動態代理實作
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/227043.html
標籤:Java
上一篇:10 個牛逼的后臺開源專案,接私活賺錢必備!
下一篇:spring注入map,spring注入一個介面的多個實作類在map里