代理(Proxy)是一種設計模式,提供了對目標對象另外的訪問方式;即通過代理對象訪問目標對象.這樣做的好處是:可以在目標對象實現的基礎上,增強額外的功能操作,即擴展目標對象的功能.
這裏使用到編程中的一個思想:不要隨意去修改別人已經寫好的代碼或者方法,如果需改修改,可以通過代理的方式來擴展該方法,
JDK動態代理模式
需要用到的類:
- 一個實現invocationHandle接口的代理類
- 需要代理的接口
- 接口的實現類
代理的接口:
//媒婆
public class Meipo implements InvocationHandler {
private Person target; //被代理對象的引用作爲一個成員變量保存下來了
//獲取被代理人的個人資料
public Object getInstance(Person target) throws Exception{
this.target = target;
Class clazz = target.getClass();
System.out.println("被代理對象的class是:"+clazz);
return Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("我是媒婆:" + "得給你找個異性才行");
System.out.println("開始進行海選...");
System.out.println("------------");
//調用的時候
method.invoke(this.target, args);
System.out.println("------------");
System.out.println("如果合適的話,就準備辦事");
return null;
}
}
接口類:
public interface Person {
//尋找真愛、相親
void findLove();
}
具體實現類 -> 被代理類
//小星星、單身
public class XiaoXingxing implements Person {
@Override
public void findLove() {
System.out.println("高富帥");
System.out.println("有房有車的");
System.out.println("身高要求180cm以上,體重70kg");
}
//回顧一下,滿足代理模式應用場景的三個必要條件,窮取法
//1、兩個角色:執行者、被代理對象
//2、注重過程,必須要做,被代理對象沒時間做或者不想做(怕羞羞),不專業
//3、執行者必須拿到被代理對象的個人資料(執行者持有被代理對象的引用)
}
具體測試:
// 這裏獲取拿到具體的實現類,通過Meipo這個類進行代理拿到具體的接口對象進行嗲用
Person obj = (Person)new Meipo().getInstance(new XiaoXingxing());
System.out.println(obj.getClass());
// 當
obj.findLove();
總結
代理對象不需要實現接口,但是目標對象一定要實現接口,否則不能用動態代理
Cglib代理
- 實現MethodInterceptor接口
ublic class GPMeipo implements MethodInterceptor{
//疑問?
//好像並沒有持有被代理對象的引用
public Object getInstance(Class clazz) throws Exception{
Enhancer enhancer = new Enhancer();
//把父類設置爲誰?
//這一步就是告訴cglib,生成的子類需要繼承哪個類
enhancer.setSuperclass(clazz);
//設置回調
enhancer.setCallback(this);
//第一步、生成源代碼
//第二步、編譯成class文件
//第三步、加載到JVM中,並返回被代理對象
return enhancer.create();
}
//同樣是做了字節碼重組這樣一件事情
//對於使用API的用戶來說,是無感知
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy)
throws Throwable {
System.out.println("我是GP媒婆:" + "得給你找個異性才行");
System.out.println("開始進行海選...");
System.out.println("------------");
//這個obj的引用是由CGLib給我們new出來的
//cglib new出來以後的對象,是被代理對象的子類(繼承了我們自己寫的那個類)
//OOP, 在new子類之前,實際上默認先調用了我們super()方法的,
//new了子類的同時,必須先new出來父類,這就相當於是間接的持有了我們父類的引用
//子類重寫了父類的所有的方法
//我們改變子類對象的某些屬性,是可以間接的操作父類的屬性的
proxy.invokeSuper(obj, args);
System.out.println("------------");
System.out.println("如果合適的話,就準備辦事");
return null;
}
}
具體被代理對象:
public class Moumoumou{
public void findLove(){
System.out.println("膚白貌美大長腿");
}
}
測試類:
try {
Moumoumou obj = (Moumoumou)new GPMeipo().getInstance(Moumoumou.class);
obj.findLove();
} catch (Exception e) {
e.printStackTrace();
}
總結
Cglib代理,也叫作子類代理,它是在內存中構建一個子類對象從而實現對目標對象功能的擴展.
JDK的動態代理有一個限制,就是使用動態代理的對象必須實現一個或多個接口,如果想代理沒有實現接口的類,就可以使用Cglib實現.
Cglib是一個強大的高性能的代碼生成包,它可以在運行期擴展java類與實現java接口.它廣泛的被許多AOP的框架使用,例如Spring AOP和synaop,爲他們提供方法的interception(攔截)
Cglib包的底層是通過使用一個小而塊的字節碼處理框架ASM來轉換字節碼並生成新的類.不鼓勵直接使用ASM,因爲它要求你必須對JVM內部結構包括class文件的格式和指令集都很熟悉.
Cglib子類代理實現方法:
- 需要引入cglib的jar文件,但是Spring的核心包中已經包括了Cglib功能,所以直接引入pring-core-3.2.5.jar即可.
- 引入功能包後,就可以在內存中動態構建子類
- 代理的類不能爲final,否則報錯
- 目標對象的方法如果爲final/static,那麼就不會被攔截,即不會執行目標對象額外的業務方法.
另外一個小的知識點:
Spring中使用AOP代理的時候會分爲JDK和Cglib兩種代理方式取決的方式就和上面的相關:
- 當目標類對象有實現接口的採用JDK代理
- 沒有實現接口,就是普通類的時候採用Cglib