代理設計模式Proxy
存在意義
代理模式的一個好處就是對外部提供統一的接口方法,而代理類在接口中實現對真實類的附加操作行爲,從而可以在不影響外部調用情況下,進行系統擴展。也就是說,我要修改真實角色的操作的時候,儘量不要修改他,而是在外部在“包”一層進行附加行爲,即代理類.
代理模式(Proxy)也可以被用來區別一個對象實例的請求和實際的訪問
靜態代理
- 說明: 靜態代理需要自己生成代理類
/**
* 買房接口
*
* @title
* @description
* @since JDK1.8
*/
public interface House {
public void maiFang();
}
/**
* 小明買房
*
* @title
* @description
* @since JDK1.8
*/
public class Xiaoming implements House {
/*
* (非 Javadoc)
*
* @see com.example.demo.proxy.House#maiFang()
*/
@Override
public void maiFang() {
System.out.println("我是小明,我要買房了!!!!");
}
}
/**
* 靜態代理需要自己生成代理類
*
* @title
* @description
* @since JDK1.8
*/
public class StaticProxy implements House {
private Xiaoming xiaoming;
public StaticProxy(Xiaoming xiaoming) {
this.xiaoming = xiaoming;
}
/*
* (非 Javadoc)
*
* @see com.example.demo.proxy.House#maiFang()
*/
@Override
public void maiFang() {
System.out.println("我是中介,我要開始監聽了!!!!");
xiaoming.maiFang();
System.out.println("我是中介,我要結束監聽了!!!!");
}
public static void main(String[] args) {
StaticProxy staticProxy = new StaticProxy(new Xiaoming());
staticProxy.maiFang();
}
}
jdk自帶動態代理
- 說明: 實現InvocationHandler接口即可,代理類通過反射機制生成
/**
* jdk自帶動態代理類:代理類通過反射機制生成
*
* @title
* @description
* @since JDK1.8
*/
public class Jdkproxy implements InvocationHandler {
/**
* 這個就是我們要代理的真實對象
*/
private Object target;
/**
* 構造方法,給我們要代理的真實對象賦初值
*
* @param subject
*/
public Jdkproxy(Object target) {
this.target = target;
}
/**
* 該方法負責集中處理動態代理類上的所有方法調用。 調用處理器根據這三個參數進行預處理或分派到委託類實例上反射執行
*
* @param proxy 代理類實例
* @param method 被調用的方法對象
* @param args 調用參數
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 在代理真實對象前我們可以添加一些自己的操作
System.out.println("我是房產中介,我jdk動態代理開始監聽了!!!!!!!!!!");
// 當代理對象調用真實對象的方法時,其會自動的跳轉到代理對象關聯的handler對象的invoke方法來進行調用
Object in = method.invoke(target, args);
// 在代理真實對象後我們也可以添加一些自己的操作
System.out.println("我是房產中介,我jdk動態代理結束監聽了!!!!!!!!!!");
return in;
}
public static void main(String[] args) {
Xiaoming xm = new Xiaoming();
Jdkproxy pro = new Jdkproxy(xm);
/**
* 該方法用於爲指定類裝載器、一組接口及調用處理器生成動態代理類實例
*/
House house = (House) Proxy.newProxyInstance(xm.getClass().getClassLoader(), xm.getClass().getInterfaces(), pro);
house.maiFang();
}
}
- 補充:通過分析代碼可以看出Java 動態代理,具體有如下四步驟:
通過實現 InvocationHandler 接口創建自己的調用處理器;
通過爲 Proxy 類指定 ClassLoader 對象和一組 interface 來創建動態代理類;
通過反射機制獲得動態代理類的構造函數,其唯一參數類型是調用處理器接口類型;
通過構造函數創建動態代理類實例,構造時調用處理器對象作爲參數被傳入。
jdk動態代理擴展
參數說明:
Object proxy:指被代理的對象。
Method method:要調用的方法
Object[] args:方法調用時所需要的參數
可以將InvocationHandler接口的子類想象成一個代理的最終操作類,替換掉ProxySubject。
Proxy類:
Proxy類是專門完成代理的操作類,可以通過此類爲一個或多個接口動態地生成實現類,此類提供瞭如下的操作方法:
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
參數說明:
ClassLoader loader:類加載器
Class<?>[] interfaces:得到全部的接口
InvocationHandler h:得到InvocationHandler接口的子類實例
Ps:類加載器
在Proxy類中的newProxyInstance()方法中需要一個ClassLoader類的實例,ClassLoader實際上對應的是類加載器,在Java中主要有一下三種類加載器;
Booststrap ClassLoader:此加載器採用C++編寫,一般開發中是看不到的;
Extendsion ClassLoader:用來進行擴展類的加載,一般對應的是jre\lib\ext目錄中的類;
AppClassLoader:(默認)加載classpath指定的類,是最常使用的是一種加載器。
動態代理
與靜態代理類對照的是動態代理類,動態代理類的字節碼在程序運行時由Java反射機制動態生成,無需程序員手工編寫它的源代碼。動態代理類不僅簡化了編程工作,而且提高了軟件系統的可擴展性,因爲Java 反射機制可以生成任意類型的動態代理類。java.lang.reflect 包中的Proxy類和InvocationHandler 接口提供了生成動態代理類的能力。
Cglib動態代理
- 說明: JDK的動態代理機制只能代理實現了接口的類,而不能實現接口的類就不能實現JDK的動態代理,cglib是針對類來實現代理的,他的原理是對指定的目標類生成一個子類,並覆蓋其中方法實現增強,但因爲採用的是繼承,所以不能對final修飾的類進行代理。
/**
* cglib動態代理:spring aop 用的就是這個方式
*
* @title
* @description
* @since JDK1.8
*/
public class CglibProxy implements MethodInterceptor {
/*
* 回調方法 (非 Javadoc)
*
* @see org.springframework.cglib.proxy.MethodInterceptor#intercept(java.lang.Object, java.lang.reflect.Method, java.lang.Object[],
* org.springframework.cglib.proxy.MethodProxy)
*/
@Override
public Object intercept(Object obj, Method method, Object[] arg2, MethodProxy proxy) throws Throwable {
System.out.println("我是房產中介,我使用的是cglib動態代理監聽開始!!!!!!!!!!!");
Object invoke = proxy.invokeSuper(obj, arg2);
System.out.println("我是房產中介,我使用的是cglib動態代理監聽結束!!!!!!!!!!!");
return invoke;
}
public static void main(String[] args) {
CglibProxy pro = new CglibProxy();
Enhancer enhancer = new Enhancer();
// 創建子類
enhancer.setSuperclass(Xiaoming.class);
// 回調方法
enhancer.setCallback(pro);
// 創建代理對象
House house = (House) enhancer.create();
house.maiFang();
}
}
jdk動態代理和cglib動態代理區別:
- dk動態代理是由Java內部的反射機制來實現的,cglib動態代理底層則是藉助asm來實現的.總的來說,反射機制在生成類的過程中比較高效,而asm在生成類之後的相關執行過程中比較高效(可以通過將asm生成的類進行緩存,這樣解決asm生成類過程低效問題).還有一點必須注意:jdk動態代理的應用前提,必須是目標基於統一的接口.如果沒有上述前提,jdk動態代理不能應用.
注意:asm其實就是java字節碼控制.