代理模式(靜態代理)
代理類爲被代理類預處理消息、過濾消息並在此之後將消息轉發給被代理類,之後還能進行消息的後置處理。代理類和被代理類通常會存在關聯關係(代理類持有的被代理對象的引用 private RealImage realImage;),代理類本身不實現服務,而是通過調用被代理類中的方法來提供服務。代理對象和實際對象都繼承了同一個接口,在代理對象中指向的是實際對象的實例,這樣對外暴露的是代理對象而真正調用的是 Real Object.
- 優點:可以很好的保護實際對象的業務邏輯對外暴露,從而提高安全性。
- 缺點:不同的接口要有不同的代理類實現,會很冗餘
- 創建一個接口(Image),然後創建被代理的類(RealImage)實現該接口並且實現該接口中的抽象方法。
public interface Image {
String findImage(String name);
String display();
}
public class RealImage implements Image {
private String fileName;
public RealImage() {
}
public RealImage(String fileName){
this.fileName = fileName;
}
@Override
public String findImage(String name) {
System.out.println("原有的findImage方法");
return "find by " + name;
}
@Override
public String display() {
System.out.println("原有的display方法");
return "Displaying " + fileName;
}
}
- 創建一個代理類(ProxyImage),同時也實現這個接口。
- 在代理類中持有一個被代理對象的引用,而後在代理類方法中調用該對象的方法。
public class ProxyImage implements Image {
private RealImage realImage;
private String fileName;
public ProxyImage(String fileName){
this.fileName = fileName;
realImage = new RealImage(fileName);
}
@Override
public String findImage(String name) {
String image = realImage.findImage(name);
System.out.println("增強被代理的方法");
return image;
}
@Override
public String display() {
String display = realImage.display();
System.out.println("增強被代理的方法");
return display;
}
}
public class ProxyPatternDemo {
public static void main(String[] args) {
Image image = new ProxyImage("test_10mb.jpg");
String display = image.findImage("6666.avi");
System.out.println(display);
System.out.println("");
String display1 = image.display();
System.out.println(display1);
}
}
動態代理
Java動態代理類位於java.lang.reflect包下,一般主要涉及到以下兩個類:
- InvocationHandler 該接口中僅定義了一個方法invoke(Object obj,Method method, Object[] args)
- Proxy 動態代理類
動態代理步驟:
- 創建被代理的類以及接口
- 創建一個實現接口InvocationHandler的類,它必須實現invoke方法
public class InvocationHandlerImpl implements InvocationHandler {
// 要代理的對象
private Object target;
// 給要代理的真實對象賦初值
public InvocationHandlerImpl(Object target) {
this.target = target;
}
/* proxy: - 指代我們所代理的那個真實對象
method: - 指代的是我們所要調用真實對象的某個方法的Method對象
args: - 指代的是調用真實對象某個方法時接受的參數*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("before..............");
System.out.println("要增強的方法:" + method.getName());
//當代理對象調用真實對象的方法時,其會自動的跳轉到代理對象關聯的handler對象的invoke方法來進行調用
Object value = method.invoke(target, args);
System.out.println("after..............");
return value;
}
}
- 通過Proxy的靜態方法newProxyInstance(ClassLoaderloader, Class[] interfaces, InvocationHandler handler)創建一個代理
- 通過代理調用方法
public class DynamicProxyDemonstration {
public static void main(String[] args) {
Image realImage = new RealImage("66666.img");
/**
* InvocationHandlerImpl 實現了 InvocationHandler 接口,並能實現方法調用從代理類到委託類的分派轉發
* 其內部通常包含指向委託類實例的引用,用於真正執行分派轉發過來的方法調用.
* 即:要代理哪個真實對象,就將該對象傳進去,最後是通過該真實對象來調用其方法
*/
InvocationHandler handler = new InvocationHandlerImpl(realImage);
ClassLoader loader = realImage.getClass().getClassLoader();
Class[] interfaces = realImage.getClass().getInterfaces();
/**
* 該方法用於爲指定類裝載器、一組接口及調用處理器生成動態代理類實例
*
* 通過Proxy的newProxyInstance方法來創建我們的代理對象,我們來看看其三個參數
* 第一個參數 handler.getClass().getClassLoader() ,我們這裏使用handler這個類的ClassLoader對象來加載我們的代理對象
* 第二個參數realSubject.getClass().getInterfaces(),我們這裏爲代理對象提供的接口是真實對象所實行的接口,表示我要代理的是該真
* 實對象,這樣我就能調用這組接口中的方法了
* 第三個參數handler, 我們這裏將這個代理對象關聯到了上方的 InvocationHandler 這個對象上
*/
Image imageProxy = (Image) Proxy.newProxyInstance(loader, interfaces, handler);
String display = imageProxy.display();
System.out.println(display);
String name = imageProxy.findImage("789.avi");
System.out.println(name);
}
}
CGLIB 動態代理
CGLIB 創建動態代理類的模式是:
- 查找目標類上的所有非 final 的 public 類型的方法 (final 的不能被重寫)
- 將這些方法的定義轉成字節碼
- 將組成的字節碼轉換成相應的代理的 Class 對象然後通過反射獲得代理類的實例對象
- 實現 MethodInterceptor 接口, 用來處理對代理類上所有方法的請求
public class CglibInterceptor implements MethodInterceptor {
/**
* CGLIB 增強類對象,代理類對象是由 Enhancer 類創建的,
* Enhancer 是 CGLIB 的字節碼增強器,可以很方便的對類進行拓展
*/
private Enhancer enhancer = new Enhancer();
/**
*
* @param object 被代理的對象
* @param method 代理的方法
* @param objects 方法的參數
* @param methodProxy CGLIB方法代理對象
* @return cglib生成用來代替Method對象的一個對象,使用MethodProxy比調用JDK自身的Method直接執行方法效率會有提升
* @throws Throwable
*/
@Override
public Object intercept(Object object, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("方法增強前........");
Object o = methodProxy.invokeSuper(object, objects);
System.out.println("方法增強後........");
return o;
}
/**
* 創建一個代理對象
*/
public Object newProxyInstance(Class<?> c) {
/**
* 設置產生的代理對象的父類,增強類型
*/
enhancer.setSuperclass(c);
/**
* 定義代理邏輯對象爲當前對象,要求當前對象實現 MethodInterceptor 接口
*/
enhancer.setCallback(this);
/**
* 使用默認無參數的構造函數創建目標對象,被代理的類要提供無參構造方法
* 有參構造 create(Class[] argumentTypes, Object[] arguments)
*/
//return enhancer.create();
return enhancer.create(new Class[]{String.class}, new Object[]{"daqiao.avi"});
}
}
// 測試類
public class CglibProxyDemo {
public static void main(String[] args) {
CglibInterceptor cglibInterceptor = new CglibInterceptor();
Image image = (Image )cglibInterceptor.newProxyInstance(RealImage.class);
String display = image.display();
System.out.println(display);
String image1 = image.findImage("123.avi");
System.out.println(image1);
}
}
JDK 動態代理和 CGLIB 動態代理的區別
- JDK 動態代理基於 Java 反射機制實現, 必須要實現了接口的業務類才能用這種方法生成代理對象,因爲動態生成的類默認繼承Proxy類。而 CGLIB 動態代理基於 ASM 框架通過生成業務類的子類來實現。
- JDK 動態代理的優勢是最小化依賴關係,代碼實現簡單。因爲是基於接口設計實現的,如果沒有接口,會拋異常。而基於 CGLIB 框架的優勢是無須實現接口。