1、代理模式的理解:不修改A對象的代碼的基礎上,對A代碼塊進行拓展。通過創建ProxyA代理對象,拓展A對象并調用A對象的核心功能;
即:不修改對象的源碼基礎上,創建代理對象,進行功能的附加和增強;
2、代理的分類:1)靜態代理;2)動態代理(jdk,cglib);
3、靜態代理:在編譯的過程中就已經將代理對象、被代理對象、接口確定下來了,class文件已經生成了;
實現步驟:
1、定義一個接口及其實現類;
2、創建一個代理類同樣實現這個接口
3、將目標對象注注入進代理類,然后在代理類的對應方法調用目標類中的對應方法。
3.1)定義一個接口及方法ParentPrint:
package com.example.demo.proxy;public interface ParentPrint {void print(String content);
}
3.2)定義一個被代理的類,并實現接口ParentPrint,其中print方法就是我們需要代理的核心方法;
package com.example.demo.proxy;public class Printer implements ParentPrint {@Overridepublic void print(String content) {System.out.println(content);}
}
3.3)定義代理類,構造一個有參數的構造器,并重寫print方法(其中通過傳入的參數調用被代理對象的print方法)。
package com.example.demo.proxy;/*
1、首先需要代理類ProxyStaticPrinter和被代理類Printer同時實現一個共同的接口 ;
2、代理類ProxyStaticPrinter需要在構造方法中注入接口定義對象,并重寫代理的方法print(前置后置處理),在其中加入注入對象的print方法;
3、實例化代理對象ProxyStaticPrinter,實例化被代理對象Printer;
4、將被代理對象通過參數注入到代理對象(ProxyStaticPrinter)有參構造器中;
5、代理對象調用處理過的方法;
*/
public class ProxyStaticPrinter implements ParentPrint {private ParentPrint parentPrint;public ProxyStaticPrinter(ParentPrint parentPrint) {this.parentPrint = parentPrint;}@Overridepublic void print(String content) {System.out.println("前置操作");parentPrint.print(content);System.out.println("后置操作");}}
3.4)測試,定義一個代理對象,將代理對象通過參數傳入被代理的對象中;
//測試public static void main(String[] args) {ParentPrint parentPrint = new Printer();ParentPrint ProxyParentPrint = new ProxyStaticPrinter(parentPrint);ProxyParentPrint.print("測試");}
運行
3.5)總結:
將目標對象注注入進代理類,然后在代理類的對應方法調用目標類中的對應方法。這樣的話,我們就可以通過代理類屏蔽對目標對象的訪問;
優點:被代理類無需實現接口
缺點:只能代理這個類,要想代理其他類,要想代理其他類需要寫新的代理方法。
4、動態代理:動態代理包含了jdk動態代理和cglib動態代理;
4.1)jkd動態代理:
實現步驟:
1、定義一個接口及其實現類;
2、自定義 InvocationHandler 并重寫invoke方法,在 invoke 方法中我們會調用原生方法(被代理類的方法)并自定義一些處理邏輯;
3、通過 Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h) 方法創建代理對象;
4.1.1)定義一個接口
package com.example.demo.proxy;public interface ParentPrint {void print(String content);
}
4.1.2)定義一個接口及方法ParentPrint:
package com.example.demo.proxy;public interface ParentPrint {void print(String content);
}
4.1.3)定義一個被代理的類,并實現接口ParentPrint,其中print方法就是我們需要代理的核心方法;
package com.example.demo.proxy;public class Printer implements ParentPrint {@Overridepublic void print(String content) {System.out.println(content);}
}
4.1.4)定義一個代理類ProxyJdkPrinter并實現InvocationHandler,實現invoke方法,最終調用method.invoke(parentPrint,args);
注意該Proxy類中是靜態方法,且接收的三個參數依次為:
ClassLoader loader:指定當前目標對象使用類加載器,獲取加載器的方法是固定的。
Class<?>[] interfaces:目標對象實現的接口的類型,使用泛型方式確認類型。
InvocationHandler:事件處理,執行目標對象的方法時,會觸發事件處理器的方法,會把當前執行目標對象的方法作為參數傳入。
package com.example.demo.proxy;import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;/*jdk動態代理:前提目標類必須有父接口(接口:ParentPrint,目標類:Printer)
1、創建ProxyJdkPrinter類,繼承接口InvocationHandler創建代理類的調用處理程序;
2、同樣引入代理類的參數對象private ParentPrint parentPrint;
3、實現invoke方法,最終調用method.invoke(parentPrint,args);
4、測試:
實例化代理類p1;
將p1放入代理類ProxyJdkPrinter;
通過Proxy類的WuDaInvocationHandler方法創建代理對象;
代理對象調用被代理的方法;
* */
public class ProxyJdkPrinter implements InvocationHandler {private ParentPrint parentPrint;public ProxyJdkPrinter(ParentPrint parentPrint) {this.parentPrint = parentPrint;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {String name = method.getName();if(name.equals("print")){System.out.println("jdk增加操作");}return method.invoke(parentPrint,args);}}
4.1.5)測試,invoke() 方法: 當我們的動態代理對象調用原生方法的時候,最終實際上調用到的是 invoke() 方法,然后 invoke() 方法代替我們去調用了被代理對象的原生方法。
public static void main(String[] args) {ParentPrint p1 = new Printer();ProxyJdkPrinter proxyJdkPrinter = new ProxyJdkPrinter(p1);//newProxyInstance:創建代理實例對象//三個參數://1、 目標類的類加載器 2、目標類的父接口 3、handlerParentPrint proxyPrint = (ParentPrint)Proxy.newProxyInstance(p1.getClass().getClassLoader(), p1.getClass().getInterfaces(), proxyJdkPrinter);proxyPrint.print("測試");}
運行
4.1.6)總結:
通過java提供的Proxy類幫我們創建代理對象,基于接口的動態代理需要利用JDK中的API,在JVM內存中動態的構建Proxy對象;
優點:可以生成所有實現接口的代理對象
缺點:JDK反射生成代理必須面向接口, 這是由Proxy的內部實現決定的。生成代理的方法中你必須指定實現類的接口,它根據這個接口來實現代理類生成的所實現的接口。
4.2)cglib動態代理:當目標沒有實現類的時候,可以使用;
實現步驟:
1、定義一個類;
2、自定義 MethodInterceptor 并重寫 intercept 方法,intercept 用于攔截增強被代理類的方法,和 JDK 動態代理中的 invoke 方法類似;
3、通過 Enhancer 類的 create()創建代理類;
4.2.1)定義一個被代理類Printer
package com.example.demo.proxy;public class Printer1 {public void print(String content) {System.out.println(content);}
}
4.2.2)定義一個代理類ProxyCglibPrinter,并實現MethodInterceptor
package com.example.demo.proxy;import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;/*cglib動態代理:(目標對象不需要實現接口)
實現MethodInterceptor 接口,在調用目標對象的方法時,就可以實現在調用方法之前、調用方法過程中、調用方法之后對其進行控制。
1.創建目標對象target;
2.創建interceptor對象
3.創建Enhancer對象,它以目標類和interceptor作為原料,生產出代理對象
4、enhancer設置參數setSuperclass,setCallback
5、創建代理類proxy,調用需要代理的方法;
* */
public class ProxyCglibPrinter implements MethodInterceptor {@Overridepublic Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {String name = method.getName();if(name.equals("print")){System.out.println("cglib增加操作");}methodProxy.invokeSuper(o,objects);return null;}}
4.2.3)測試
public static void main(String[] args) {Printer1 target = new Printer1();ProxyCglibPrinter interceptor = new ProxyCglibPrinter();Enhancer enhancer = new Enhancer();enhancer.setSuperclass(target.getClass());enhancer.setCallback(interceptor);Printer1 proxy = (Printer1)enhancer.create();proxy.print("測試");}
運行
4.2.4)總結:
無需代理類實現接口,使用Cblib中的Enhancer來生成代理對象子類,并實現MethodInterceptor中的intercept方法,在此方法中可以實現增強功能。
如果目標對象需要實現接口,則使用JDK代理。
如果目標對象不需要實現接口,則使用Cglib代理。
5、綜上所述,再次總結:
靜態代理:
jdk動態代理:
cglib動態代理: