JDK動態(tài)代理(AOP)

代理模式

代理模式是指冀膝,為其他對象提供一種代理以控制對這個對象的訪問唁奢。在某些情況下,一個對象不適合或者不能直接引用另一個對象窝剖,而代理對象可以在客戶類和目標對象之間起到中介的作用麻掸。

換句話說,使用代理對象赐纱,是為了在不修改目標對象的基礎上脊奋,增強主業(yè)務邏輯。

客戶類真正的想要訪問的對象是目標對象疙描,但客戶類真正可以訪問的對象是代理對象诚隙。客戶類對目標對象的訪問是通過訪問代理對象來實現的久又。當然地消,代理類與目標類要實現同一個接口脉执。

例如: 有 A,B迅细,C 三個類嗦随, A 原來可以調用 C 類的方法, 現在因為某種原因 C 類不允許A 類調用其方法砂吞,但 B 類可以調用 C 類的方法蜻直。A 類通過 B 類調用 C 類的方法呼巷。這里 B 是 C的代理王悍。 A 通過代理 B 訪問 C.

原來的訪問關系:

image.png

通過代理的訪問關系:

image.png

Window 系統(tǒng)的快捷方式也是一種代理模式〖铮快捷方式代理的是真實的程序,雙擊快捷方式是啟動它代表的程序养渴。

代理模式作用

A厚脉、控制訪問
B霞溪、 增強功能

代理模式分類

可以將代理分為兩類:靜態(tài)代理與動態(tài)代理

代理的實現方式

靜態(tài)代理和動態(tài)代理

需求

需求:用戶需要購買 u 盤鸯匹,u 盤廠家不單獨接待零散購買殴蓬,廠家規(guī)定一次最少購買 1000個以上染厅,用戶可以通過淘寶的代理商肖粮,或者微商哪里進行購買行施。

淘寶上的商品蛾号,微商都是 u 盤工廠的代理商须教, 他們代理對 u 盤的銷售業(yè)務轻腺。

用戶購買-------代理商(淘寶贬养,微商)----- u 廠家(金士頓,閃迪等不同的廠家)

設計這個業(yè)務需要的類:

  1. 商家和廠家都是提供 sell 購買 u 盤的方法儿礼。定義購買 u 盤的接口 UsbSell
  2. 金士頓(King)對購買 1 千以上的價格是 85, 3 千以上是 80, 5 千以上是 75蚊夫。 單個 120元知纷。定義 UsbKingFactory 類琅轧,實現 UsbSell
  3. 閃迪(San)對購買 1 千以上的價格是 82, 3 千以上是 78, 5 千以上是 72。 單個 120 元睹酌。定義 UsbSanFactory 類忍疾,實現 UsbSell
  4. 定義淘寶的代理商 TaoBao 卤妒,實現 UsbSell
  5. 定義微商的代理商 WeiShang, 實現 UsbSell
  6. 定義測試類则披,測試通過淘寶士复, 微商購買 u 盤

靜態(tài)代理

靜態(tài)代理是指阱洪,代理類在程序運行前就已經定義好.java 源文件冗荸,其與目標類的關系在程序運行前就已經確立蚌本。在程序運行前代理類已經編譯為.class 文件。

靜態(tài)代理

在 idea 中創(chuàng)建 java 工程嵌莉,
工程名稱:ch01-staticproxy

(1) 定義業(yè)務接口
定義業(yè)務接口 UsbSell(目標接口)烦秩,其中含有抽象方法 sell(int amount), sell 是目標方法抛寝。

image.png

(2) 定義接口實現類
目標類 UsbKingFactory(金士頓 u 盤),該類實現了業(yè)務接口钻趋。

image.png

(3) 代理商 TaoBao
TaoBao 就是一個代理類较沪, 代理廠家銷售 u 盤

image.png

(4) 代理商 WeiShang
WeiShang 就是一個代理類, 代理廠家銷售 u 盤

image.png

(5) 客戶端調用者,購買商品類

image.png

使用代理的訪問關系圖:

image.png

靜態(tài)代理的缺點

(1) 代碼復雜茬射,難于管理
代理類和目標類實現了相同的接口躲株,每個代理都需要實現目標類的方法,這樣就出現了大量的代碼重復望浩。如果接口增加一個方法磨德,除了所有目標類需要實現這個方法外,所有代理類也需要實現此方法啦吧。增加了代碼維護的復雜度琳水。

(2) 代理類依賴目標類,代理類過多
代理類只服務于一種類型的目標類私沮,如果要服務多個類型造垛。勢必要為每一種目標類都進行代理筋搏,靜態(tài)代理在程序規(guī)模稍大時就無法勝任了,代理類數量過多。

動態(tài)代理

動態(tài)代理是指代理類對象在程序運行時由 JVM 根據反射機制動態(tài)生成的建丧。動態(tài)代理不需要定義代理類的.java 源文件翎朱。

動態(tài)代理其實就是 jdk 運行期間,動態(tài)創(chuàng)建 class 字節(jié)碼并加載到 JVM凛忿。

動態(tài)代理的實現方式常用的有兩種:使用 JDK 動態(tài)代理澈灼,與通過 CGLIB 動態(tài)代理。

jdk 的動態(tài)代理

jdk 動態(tài)代理是基于 Java 的反射機制實現的店溢。使用 jdk 中接口和類實現代理對象的動態(tài)創(chuàng)建叁熔。

Jdk 的動態(tài)要求目標對象必須實現接口,這是 java 設計上的要求床牧。

從 jdk1.3 以來荣回,java 語言通過 java.lang.reflect 包提供三個類支持代理模式 Proxy, Method和 InovcationHandler。

(1) InvocationHandler 接口
InvocationHandler 接口叫做調用處理器戈咳,負責完調用目標方法糯累,并增強功能胖秒。

通 過 代 理 對 象 執(zhí) 行 目 標 接 口 中 的 方 法 判导, 會 把 方 法 的 調 用 分 派 給 調 用 處 理 器(InvocationHandler)的實現類,執(zhí)行實現類中的 invoke()方法,我們需要把功能代理寫在 invoke()方法中 疹娶。

image.png

接口中只有一個方法:

image.png

在 invoke 方法中可以截取對目標方法的調用移斩。在這里進行功能增強你稚。Java 的動態(tài)代理是建立在反射機制之上的鸡典。

實現了 InvocationHandler 接口的類用于加強目標類的主業(yè)務邏輯症汹。這個接口中有一個方法 invoke()破婆,具體加強的代碼邏輯就是定義在該方法中的谤职。通過代理對象執(zhí)行接口中的方法時,會自動調用 invoke()方法。

invoke()方法的介紹如下:

public Object invoke ( Object proxy, Method method, Object[] args)
proxy:代表生成的代理對象
method:代表目標方法
args:代表目標方法的參數

第一個參數 proxy 是 jdk 在運行時賦值的撬码,在方法中直接使用驼鹅,第二個參數后面介紹钓辆,
第三個參數是方法執(zhí)行的參數壳咕, 這三個參數都是 jdk 運行時賦值的聂宾,無需程序員給出薪寓。

(2) Method 類
invoke()方法的第二個參數為 Method 類對象存崖,該類有一個方法也叫 invoke()供搀,可以調用目標方法。這兩個 invoke()方法万栅,雖然同名,但無關。

public Object invoke ( Object obj, Object... args)
obj:表示目標對象
args:表示目標方法參數个初,就是其上一層 invoke 方法的第三個參數

該方法的作用是:調用執(zhí)行 obj 對象所屬類的方法,這個方法由其調用者 Method 對象確定弄息。

在代碼中痊班,一般的寫法為
method.invoke(target, args);

其中,method 為上一層 invoke 方法的第二個參數摹量。這樣涤伐,即可調用了目標類的目標方法馒胆。

(3) Proxy 類
通 過 JDK 的 java.lang.reflect.Proxy 類 實 現 動 態(tài) 代 理 , 會 使 用 其 靜 態(tài) 方 法newProxyInstance()凝果,依據目標對象祝迂、業(yè)務接口及調用處理器三者,自動生成一個動態(tài)代理對象器净。

public static newProxyInstance ( ClassLoader loader, Class<?>[] interfaces, InvocationHandler handler)

loader:目標類的類加載器型雳,通過目標對象的反射可獲取
interfaces:目標類實現的接口數組,通過目標對象的反射可獲取
handler:調用處理器山害。

jdk 動態(tài)代理實現

jdk 動態(tài)代理是代理模式的一種實現方式纠俭,其只能代理接口。

實現步驟
1浪慌、新建一個接口冤荆,作為目標接口
2、為接口創(chuàng)建一個實現類权纤,是目標類
3钓简、創(chuàng)建類實現 java.lang.reflect.InvocationHandler 接口,調用目標方法并增加其他功能代碼
4汹想、創(chuàng)建動態(tài)代理對象外邓,使用 Proxy.newProxyInstance()方法,并把返回值強制轉為接口類型古掏。

idea 創(chuàng)建 java project
工程名稱:ch02-dynamicproxy

(1) 定義目標接口

image.png

(2) 定義目標接口實現類

image.png

(3) 定義調用處理程序
調用處理程序是實現了 InvocationHandler 的類损话,在 invoke 方法中增加業(yè)務功能。還需要創(chuàng)
建有參構造冗茸,參數是目標對象席镀。為的是完成對目標對象的方法調用。

image.png

(4) 創(chuàng)建動態(tài)代理對象

image.png

執(zhí)行流程:

image.png

類圖:

image.png

cgLib 代理

CGLIB(Code Generation Library)是一個開源項目夏漱。是一個強大的,高性能顶捷,高質量的 Code 生成類庫挂绰,它可以在運行期擴展 Java 類與實現 Java 接口。它廣泛的被許多 AOP 的框架使用服赎,例如 Spring AOP葵蒂。

使用 JDK 的 Proxy 實現代理,要求目標類與代理類實現相同的接口重虑。若目標類不存在接口践付,則無法使用該方式實現。

但對于無接口的類缺厉,要為其創(chuàng)建動態(tài)代理永高,就要使用 CGLIB 來實現隧土。CGLIB 代理的生成原理是生成目標類的子類,而子類是增強過的命爬,這個子類對象就是代理對象曹傀。所以,使用CGLIB 生成動態(tài)代理饲宛,要求目標類必須能夠被繼承皆愉,即不能是 final 的類。

cglib 經常被應用在框架中艇抠,例如 Spring 幕庐,Hibernate 等。Cglib 的代理效率高于 Jdk家淤。對于 cglib 一般的開發(fā)中并不使用翔脱。做了一個了解就可以。

總結

動態(tài)代理(理解): 基于反射機制媒鼓。

  1. 什么是動態(tài)代理 届吁?
    使用jdk的反射機制,創(chuàng)建對象的能力绿鸣, 創(chuàng)建的是代理類的對象疚沐。 而不用你創(chuàng)建類文件。不用寫java文件潮模。

動態(tài):在程序執(zhí)行時亮蛔,調用jdk提供的方法才能創(chuàng)建代理類的對象。

jdk動態(tài)代理擎厢,必須有接口究流,目標類必須實現接口, 沒有接口時动遭,需要使用cglib動態(tài)代理

  1. 知道動態(tài)代理能做什么 芬探?
    可以在不改變原來目標方法功能的前提下, 可以在代理中增強自己的功能代碼厘惦。

程序開發(fā)中的意思偷仿。
比如:你所在的項目中,有一個功能是其他人(公司的其它部門宵蕉,其它小組的人)寫好的酝静,你可以使用。
GoNong.class , GoNong gn = new GoNong(), gn.print();

你發(fā)現這個功能羡玛,現在還缺點别智, 不能完全滿足我項目的需要。 我需要在gn.print()執(zhí)行后稼稿,需要自己在增加代碼薄榛。

用代理實現 gn.print()調用時讳窟, 增加自己代碼, 而不用去改原來的 GoNong文件蛇数。


1.代理
代購挪钓, 中介,換ip耳舅,商家等等

比如有一家美國的大學碌上, 可以對全世界招生。 留學中介(代理)

留學中介(代理): 幫助這家美國的學校招生浦徊, 中介是學校的代理馏予, 中介是代替學校完成招生功能。

代理特點:
1) 中介和代理他們要做的事情是一致的: 招生盔性。
2)中介是學校代理霞丧, 學校是目標。
3)家長---中介(學校介紹冕香,辦入學手續(xù))----美國學校蛹尝。
4) 中介是代理,不能白干活悉尾,需要收取費用突那。
5) 代理不讓你訪問到目標。

為什么要找中介 构眯?
1) 中介是專業(yè)的愕难, 方便
2) 家長現在不能自己去找學校。 家長沒有能力訪問學校惫霸。 或者美國學校不接收個人來訪猫缭。

買東西都是商家賣, 商家是某個商品的代理壹店, 你個人買東西猜丹, 肯定不會讓你接觸到廠家的。

  1. 在開發(fā)中也會有這樣的情況茫打, 你有a類居触, 本來是調用c類的方法, 完成某個功能老赤。 但是c不讓a調用。

a -----不能調用 c的方法制市。
在a 和 c 直接 創(chuàng)建一個 b 代理抬旺, c讓b訪問。
a --訪問b---訪問c

實際的例子: 登錄祥楣,注冊有驗證碼开财, 驗證碼是手機短信汉柒。

中國移動, 聯通能發(fā)短信责鳍。

中國移動碾褂, 聯通能有子公司,或者關聯公司历葛,他們面向社會提供短信的發(fā)送功能

張三項目發(fā)送短信----子公司正塌,或者關聯公司-----中國移動, 聯通

3.使用代理模式的作用
1).功能增強: 在你原有的功能上恤溶,增加了額外的功能乓诽。 新增加的功能,叫做功能增強咒程。
2)控制訪問: 代理類不讓你訪問目標鸠天,例如商家不讓用戶訪問廠家。

4.實現代理的方式
1)靜態(tài)代理 :
a) 代理類是自己手工實現的帐姻,自己創(chuàng)建一個java類稠集,表示代理類。
b)同時你所要代理的目標類是確定的饥瓷。

特點:
1)實現簡單
2)容易理解剥纷。

缺點:
當你的項目中,目標類和代理類很多時候扛伍,有以下的缺點:
1)當目標類增加了筷畦, 代理類可能也需要成倍的增加。 代理類數量過多刺洒。
2) 當你的接口中功能增加了鳖宾, 或者修改了,會影響眾多的實現類逆航,廠家類鼎文,代理類都需要修改。影響比較多。

模擬一個用戶購買u盤的行為厨相。
用戶是客戶端類
商家:代理潜慎,代理某個品牌的u盤。
廠家:目標類撑帖。

三者的關系: 用戶(客戶端)---商家(代理)---廠家(目標)

商家和廠家都是賣u盤的,他們完成的功能是一致的澳眷,都是賣u盤胡嘿。

實現步驟:
1). 創(chuàng)建一個接口,定義賣u盤的方法钳踊, 表示你的廠家和商家做的事情衷敌。
2). 創(chuàng)建廠家類勿侯,實現1步驟的接口
3). 創(chuàng)建商家,就是代理缴罗,也需要實現1步驟中的接口助琐。
4). 創(chuàng)建客戶端類,調用商家的方法買一個u盤面氓。

代理類完成的功能:
1). 目標類中方法的調用
2). 功能增強

  1. 動態(tài)代理
    在靜態(tài)代理中目標類很多時候兵钮,可以使用動態(tài)代理,避免靜態(tài)代理的缺點侧但。
    動態(tài)代理中目標類即使很多矢空,
    1)代理類數量可以很少,
    2)當你修改了接口中的方法時禀横,不會影響代理類屁药。

動態(tài)代理: 在程序執(zhí)行過程中,使用jdk的反射機制柏锄,創(chuàng)建代理類對象酿箭, 并動態(tài)的指定要代理目標類。

換句話說: 動態(tài)代理是一種創(chuàng)建java對象的能力趾娃,讓你不用創(chuàng)建TaoBao類缭嫡,就能創(chuàng)建代理類對象。

在java中抬闷,要想創(chuàng)建對象:
1.創(chuàng)建類文件妇蛀, java文件編譯為class
2.使用構造方法,創(chuàng)建類的對象笤成。

動態(tài)代理的實現:

  1. jdk動態(tài)代理(理解): 使用java反射包中的類和接口實現動態(tài)代理的功能评架。

反射包 java.lang.reflect , 里面有三個類 : InvocationHandler , Method, Proxy.

  1. cglib動態(tài)代理(了解): cglib是第三方的工具庫, 創(chuàng)建代理對象炕泳。

cglib的原理是繼承纵诞, cglib通過繼承目標類,創(chuàng)建它的子類培遵,在子類中 重寫父類中同名的方法浙芙, 實現功能的修改。

因為cglib是繼承籽腕,重寫方法嗡呼,所以要求目標類不能是final的, 方法也不能是final的皇耗。

cglib的要求目標類比較寬松晤锥, 只要能繼承就可以了。cglib在很多的框架中使用廊宪, 比如 mybatis 矾瘾,spring框架中都有使用。

jdk動態(tài)代理:

  1. 反射箭启, Method類壕翩,表示方法。類中的方法傅寡。 通過Method可以執(zhí)行某個方法放妈。

  2. jdk動態(tài)代理的實現
    反射包 java.lang.reflect , 里面有三個類 : InvocationHandler , Method, Proxy.

1)InvocationHandler 接口(調用處理器):就一個方法invoke()

invoke():表示代理對象要執(zhí)行的功能代碼。你的代理類要完成的功能就寫在invoke()方法中荐操。

代理類完成的功能:

  1. 調用目標方法芜抒,執(zhí)行目標方法的功能
  2. 功能增強,在目標方法調用時托启,增加功能宅倒。

方法原型:
public Object invoke(Object proxy, Method method, Object[] args)

參數:
Object proxy:jdk創(chuàng)建的代理對象,無需賦值屯耸。
Method method:目標類中的方法拐迁,jdk提供method對象的
Object[] args:目標類中方法的參數, jdk提供的疗绣。

InvocationHandler 接口:表示你的代理要干什么线召。
怎么用:
1.創(chuàng)建類實現接口InvocationHandler
2.重寫invoke()方法, 把原來靜態(tài)代理中代理類要完成的功能多矮,寫在這缓淹。

2)Method類:表示方法的, 確切的說就是目標類中的方法塔逃。
作用:通過Method可以執(zhí)行某個目標類的方法讯壶,Method.invoke();
method.invoke(目標對象,方法的參數)

Object ret = method.invoke(service2, "李四");

說明: method.invoke()就是用來執(zhí)行目標方法的患雏,等同于靜態(tài)代理中的:
//向廠家發(fā)送訂單鹏溯,告訴廠家,我買了u盤淹仑,廠家發(fā)貨
float price = factory.sell(amount); //廠家的價格丙挽。

3)Proxy類:核心的對象,創(chuàng)建代理對象匀借。之前創(chuàng)建對象都是 new 類的構造方法()現在我們是使用Proxy類的方法颜阐,代替new的使用。

方法: 靜態(tài)方法 newProxyInstance()
作用是: 創(chuàng)建代理對象吓肋, 等同于靜態(tài)代理中的TaoBao taoBao = new TaoBao();

參數:

  1. ClassLoader loader 類加載器凳怨,負責向內存中加載對象的。 使用反射獲取對象的ClassLoader類a , a.getCalss().getClassLoader(), 目標對象的類加載器
  2. Class<?>[] interfaces: 接口, 目標對象實現的接口肤舞,也是反射獲取的紫新。
  3. InvocationHandler h : 我們自己寫的,代理類要完成的功能李剖。

返回值:就是代理對象

public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)

  1. 實現動態(tài)代理的步驟:
    1). 創(chuàng)建接口芒率,定義目標類要完成的功能
    2). 創(chuàng)建目標類實現接口
    3). 創(chuàng)建InvocationHandler接口的實現類,在invoke方法中完成代理類的功能:
    1.調用目標方法
    2.增強功能

4.使用Proxy類的靜態(tài)方法篙顺,創(chuàng)建代理對象偶芍。 并把返回值轉為接口類型。

動態(tài)代理.png
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末德玫,一起剝皮案震驚了整個濱河市匪蟀,隨后出現的幾起案子,更是在濱河造成了極大的恐慌宰僧,老刑警劉巖材彪,帶你破解...
    沈念sama閱讀 212,884評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現場離奇詭異撒桨,居然都是意外死亡查刻,警方通過查閱死者的電腦和手機,發(fā)現死者居然都...
    沈念sama閱讀 90,755評論 3 385
  • 文/潘曉璐 我一進店門凤类,熙熙樓的掌柜王于貴愁眉苦臉地迎上來穗泵,“玉大人,你說我怎么就攤上這事谜疤〉柩樱” “怎么了?”我有些...
    開封第一講書人閱讀 158,369評論 0 348
  • 文/不壞的土叔 我叫張陵夷磕,是天一觀的道長履肃。 經常有香客問我,道長坐桩,這世上最難降的妖魔是什么尺棋? 我笑而不...
    開封第一講書人閱讀 56,799評論 1 285
  • 正文 為了忘掉前任,我火速辦了婚禮绵跷,結果婚禮上膘螟,老公的妹妹穿的比我還像新娘。我一直安慰自己碾局,他們只是感情好荆残,可當我...
    茶點故事閱讀 65,910評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著净当,像睡著了一般内斯。 火紅的嫁衣襯著肌膚如雪蕴潦。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 50,096評論 1 291
  • 那天俘闯,我揣著相機與錄音潭苞,去河邊找鬼。 笑死备徐,一個胖子當著我的面吹牛萄传,可吹牛的內容都是我干的。 我是一名探鬼主播蜜猾,決...
    沈念sama閱讀 39,159評論 3 411
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼振诬!你這毒婦竟也來了蹭睡?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 37,917評論 0 268
  • 序言:老撾萬榮一對情侶失蹤赶么,失蹤者是張志新(化名)和其女友劉穎肩豁,沒想到半個月后,有當地人在樹林里發(fā)現了一具尸體辫呻,經...
    沈念sama閱讀 44,360評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡清钥,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,673評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現自己被綠了放闺。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片祟昭。...
    茶點故事閱讀 38,814評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖怖侦,靈堂內的尸體忽然破棺而出篡悟,到底是詐尸還是另有隱情,我是刑警寧澤匾寝,帶...
    沈念sama閱讀 34,509評論 4 334
  • 正文 年R本政府宣布搬葬,位于F島的核電站,受9級特大地震影響艳悔,放射性物質發(fā)生泄漏急凰。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 40,156評論 3 317
  • 文/蒙蒙 一猜年、第九天 我趴在偏房一處隱蔽的房頂上張望抡锈。 院中可真熱鬧,春花似錦码倦、人聲如沸企孩。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,882評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽勿璃。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間补疑,已是汗流浹背歧沪。 一陣腳步聲響...
    開封第一講書人閱讀 32,123評論 1 267
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留莲组,地道東北人诊胞。 一個月前我還...
    沈念sama閱讀 46,641評論 2 362
  • 正文 我出身青樓,卻偏偏與公主長得像锹杈,于是被迫代替她去往敵國和親撵孤。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,728評論 2 351

推薦閱讀更多精彩內容