以前面試的過程當中,有被面試官問到Spring中的AOP相關問題。當時只回答了具體的一個概念和相關應用,還補充了下底層是運用了代理模式;但以前一直並無對相關的代理模式進行一個深刻的理解,特此寫下了這篇博客,記錄下。。。java
廢話很少說,首先咱們知道代理模式是設計模式中的一種,書中給到的定義是:代理模式給某一個對象提供一個代理對象,並由代理對象控制對原對象的引用。網上通俗說是像咱們生活中的中介。 舉個例子:我要買房子,但我這我的沒什麼生活經驗,我只懂得付錢面試
public class people{ public void buy(){ System.out.println("付錢"); } }
但我又不想向下面代碼那樣去主動學怎麼找房子、怎麼辦理相關手續。設計模式
public void buy(){ System.out.println("找房子"); System.out.println("付錢"); System.out.println("辦理手續"); }
那這個時候我該怎麼辦呢?簡單,有問題找中介,我只要會付錢,其餘的東西中介幫我搞定。這個中介就是一個代理對象,在不改變「我」這個目標對象的功能的狀況下,用代理對象的方式實現功能擴展。ide
想要實現以上的需求有三種方式,也就是java的三種代理模式:靜態代理、動態代理(JDK代理)、Cglib代理。函數
public interface IPeople{ void buy(); }
/** * 目標對象實現了某一接口 */ public class People implements IPeople{ public void buy(){ System.out.println("付錢"); } }
/** * 代理對象(中介)和目標對象(我)實現相同的接口 */ public class PeopleProxy implements IPeople{ // 接收目標對象,以便調用buy方法 private IPeople target; public PeopleProx(IPeople target){ this.target = target; } // 對目標對象的buy方法進行功能擴展 public void buy() { System.out.println("找房子"); target.buy(); System.out.println("辦理手續"); } }
/** * 測試類 */ public class Test { public static void main(String[] args) { //目標對象 IPeople target = new People(); //代理對象 IPeople proxy = new PeopleProxy(target); //執行的是代理的方法 proxy.buy(); } }
總結:靜態代理業務類只須要關注業務邏輯自己,保證了業務類的重用性。代理對象的一個接口只服務於一種類型的對象,若是要代理的方法不少,須要爲每一種方法都進行代理,靜態代理在程序規模稍大時就沒法勝任。若是接口增長一個方法,除了全部實現類須要實現這個方法外,全部代理類也須要實現此方法,增長了代碼維護的複雜度工具
public interface IPeople{ void buy(); }
/** * 目標對象實現了某一接口 */ public class People implements IPeople { public void buy(){ System.out.println("付錢"); } }
/** JDK動態代理的實現原理大概流程 一、爲接口建立代理類的字節碼文件 二、使用ClassLoader將字節碼文件加載到JVM 三、建立代理類實例對象,執行對象的目標方法 **/ public class JdkProxyTest { public static void main(String[] args) { People target = new People(); IPeople proxy = (IPeople) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() { @Override public Object invoke(Object o, Method method, Object[] objects) throws Throwable { System.out.println("找房子"); //執行目標對象方法 Object returnValue = method.invoke(target, objects); System.out.println("辦理手續"); return returnValue; } } ); proxy.buy(); } }
總結:動態代理與靜態代理相比較,最大的好處是接口中聲明的全部方法都被轉移到調用處理器一個集中的方法中處理(InvocationHandler invoke)。這樣,在接口方法數量比較多的時候,能夠進行靈活處理,而不須要像靜態代理那樣每個方法進行中轉。並且動態代理的應用使類職責更加單一,複用性更強。測試
/** * 目標對象不用實現接口 */ public class People { public void buy(){ System.out.println("付錢"); } }
/** * Cglib子類代理工廠 **/ public class ProxyFactory implements MethodInterceptor { //維護目標對象 private Object target; public ProxyFactory(Object target){ this.target = target; } // 給目標對象建立一個代理對象 public Object getProxyInstance(){ //1.工具類 Enhancer en = new Enhancer(); //2.設置父類 en.setSuperclass(target.getClass()); //3.設置回調函數 en.setCallback(this); //4.建立子類(代理對象) return en.create(); } @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { System.out.println("找房子"); Object returnValue = method.invoke(target,objects); System.out.println("辦理手續"); return returnValue; } }
public class Test { public static void main(String[] args) { //目標對象 People target= new People(); //代理對象 People targer =(People) new ProxyFactory(target).getProxyInstance(); //執行代理對象的方法 targer.buy(); } }
總結:靜態代理和動態代理都須要目標對象實現接口,而Cglib代理則不須要這麼麻煩。最後,順便提下關於Cglib的問題,若是目標對象有不一樣方法,須要實現不一樣代理,那麼代碼須要怎麼實現呢?this
特此補充:在sping中JDK代理和cglib代理混合使用,若是被代理對象實現了接口,就優先使用JDK代理,若是沒有實現接口,就用用cglib代理spa