又開始個人專題了,又停滯了一段時間了,加油繼續吧。都知道 SpringAOP 是用代理模式實現,究竟是怎麼實現的?咱們來一探究竟,而且本身仿真手寫還原部分細節。
在生活中,咱們常常見到這樣的場景,如:租房中介、售票黃牛、婚介、經紀人、快遞、 事務代理、非侵入式日誌監聽等,這些都是代理模式的實際體現。代理模式(Proxy Pattern)的定義也很是簡單,是指爲其餘對象提供一種代理,以控制對這個對象的訪問。 代理對象在客服端和目標對象之間起到中介做用,代理模式屬於結構型設計模式。使用 代理模式主要有兩個目的:一保護目標對象,二加強目標象。下面咱們來看一下代理 模式的類結構圖:
Subject 是頂層接口,RealSubject 是真實對象(被代理對象),Proxy 是代理對象,代 理對象持有被代理對象的引用,客戶端調用代理對象方法,同時也調用被代理對象的方 法,可是在代理對象先後增長一些處理。在代碼中,咱們想到代理,就會理解爲是代碼 加強,其實就是在本來邏輯先後增長一些邏輯,而調用者無感知。代理模式屬於結構型 模式,有靜態代理和動態代理。
舉個例子:人到了適婚年齡,父母老是火燒眉毛但願早點抱孫子。而如今社會的人在各 種壓力之下,都選擇晚婚晚育。因而着急的父母就開始處處爲本身的子女相親,比子女 本身還着急。這個相親的過程,就是一種咱們人人都有份的代理。來看代碼實現: /** * 人不少行爲,要談戀愛 */ public interface Person { void findLove(); } /** * 兒子須要找對象 */ public class Son implements Person { @Override public void findLove() { System.out.println("工做沒時間!"); } } /** * 父親代理兒子 先幫物色對象 */ public class Father{ private Son son; //代理對象持有 被代理對象的應用 但沒辦法擴展 private Father(Son son) { this.son = son; } private void findLove() { //before System.out.println("父母幫物色對象"); son.findLove(); //after System.out.println("雙方贊成交往!"); } //測試代碼 public static void main(String[] args) { Son son = new Son(); Father father = new Father(son); father.findLove(); } }
動態代理和靜態對比基本思路是一致的,只不過動態代理功能更增強大,隨着業務的擴 展適應性更強。若是還以找對象爲例,使用動態代理至關因而可以適應複雜的業務場景。 不只僅只是父親給兒子找對象,若是找對象這項業務發展成了一個產業,進而出現了媒 婆、婚介所等這樣的形式。那麼,此時用靜態代理成本就更大了,須要一個更加通用的 解決方案,要知足任何單身人士找對象的需求。咱們升級一下代碼,先來看 JDK 實現方式:
建立媒婆(婚介)JDKMeipo 類:java
package com.study; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class JdkMeipo implements InvocationHandler { //持有被代理對象的引用 Object target; public Object getInstance(Object target){ this.target =target; Class<?> aClass = target.getClass(); return Proxy.newProxyInstance(aClass.getClassLoader(),aClass.getInterfaces(),this); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { before(); Object object = method.invoke(this.target,args); after(); return object; } private void before() { System.out.println("我是婚介,幫你物色對象"); } private void after() { System.out.println("已找到,若是合適就開始"); } }
建立單身客戶spring
package com.study; public class Customer implements Person{ @Override public void findLove() { System.out.println("我要找白富美"); } }
測試代碼設計模式
package com.study; public class DemoTest { public static void main(String[] args) { Person person = (Person) new JdkMeipo().getInstance(new Customer()); person.findLove(); } }
執行效果
框架
不只知其然,還得知其因此然。既然 JDK Proxy 功能如此強大,那麼它是如何實現的呢? 咱們如今來探究一下原理。 咱們都知道 JDK Proxy 採用字節重組,從新生的對象來替代原始的對象以達到動態代理 的目的。JDK Proxy 生成對象的步驟以下:ide
1.拿到代理對象的應用,並獲取它的全部接口,反射獲取。測試
2.經過JDK proxy 類從新生成一個新的類,同時新的類要實現被代理類全部實現的接口。this
3.動態生成Java代碼,把新加的業務邏輯方法由必定的邏輯代碼去調用(在代碼中體現)。設計
4.編譯從新生成Java代碼.class代理
5.再從新加載的JVM中
以上這個過程就叫字節重組。日誌
簡單看一下 CGLib 代理的使用,仍是以媒婆爲例,建立 CglibMeipo 類:
package com.study; import org.springframework.cglib.proxy.Enhancer; import org.springframework.cglib.proxy.MethodInterceptor; import org.springframework.cglib.proxy.MethodProxy; import java.lang.reflect.Method; public class CGlibMeipo implements MethodInterceptor { Object target; public Object getInstance(Class<?> aClass){ Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(aClass); enhancer.setCallback(this); return enhancer.create(); } @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { before(); Object invokeSuper = methodProxy.invokeSuper(o, objects); after(); return invokeSuper; } private void after() { System.out.println("已找到,若是合適就開始"); } private void before() { System.out.println("我是婚介,幫你物色對象"); } }
測試調用
package com.study; public class DemoTest { public static void main(String[] args) { //Person person = (Person) new JdkMeipo().getInstance(new Customer()); Person person = (Person) new CGlibMeipo().getInstance(Customer.class); person.findLove(); } }
執行結果:(CGLib代理的對象是不須要實現任何接口的,他是經過動態繼承目標對象實現的動態代理。)
CGLib 動態代理執行代理方法效率之因此比 JDK 的高是由於 Cglib 採用了 FastClass 機 制,它的原理簡單來講就是:爲代理類和被代理類各生成一個 Class,這個 Class 會爲代 理類或被代理類的方法分配一個 index(int 類型)。這個 index 當作一個入參,FastClass 就能夠直接定位要調用的方法直接進行調用,這樣省去了反射調用,因此調用效率比 JDK 動態代理經過反射調用高。
1.JDK 動態代理是實現了被代理對象的接口,CGLib 是繼承了被代理對象。
2.JDK 和 CGLib 都是在運行期生成字節碼,JDK 是直接寫 Class 字節碼,CGLib 使用 ASM 框架寫 Class 字節碼,Cglib 代理實現更復雜,生成代理類比 JDK 效率低。
3.JDK 調用代理方法,是經過反射機制調用,CGLib 是經過 FastClass 機制直接調用方法, CGLib 執行效率更高。