前言
代理模式是提供了一種目標(biāo)對象另外的訪問方式,通過各種方式構(gòu)建出代理對象讓其去訪問目標(biāo)對象,這樣我們就可以在訪問屬性或者方法的開始之前或者結(jié)束之后做一些事情。在逆向的過程中沒有類的定義還可以更加隱匿自己的一些信息。
在java中代理模式主要有三種實現(xiàn)方式
- 靜態(tài)代理
- JDK動態(tài)代理
- cglib動態(tài)代理
因為很多源碼中都用到了代理模式所以我這里專門寫下記錄一下
靜態(tài)代理
靜態(tài)代理要求代理對象和被代理對象實現(xiàn)相同接口或繼承相同類
其實就是在代理類中定義一個被代理類屬性,在構(gòu)造方法中初始化。再在需要代理的方法中調(diào)用原方法。
下面我們看看具體應(yīng)用
//IUser.java
package com.shark.bean;
public interface IUser {
public void say();
}
//User.java
/**
被代理對象
**/
package com.shark.bean;
public class User implements IUser {
public void say() {
// TODO Auto-generated method stub
System.out.println("I say!");
}
}
/**
代理對象
UserProxy.java
**/
package com.shark.staticproxy;
import com.shark.bean.IUser;
public class UserProxy implements IUser {
private IUser user;
public UserProxy(IUser user) {
// TODO Auto-generated constructor stub
this.user = user;
}
public void say() {
// TODO Auto-generated method stub
System.out.println("Static Before invoke " );
user.say();
System.out.println("Static Before invoke " );
}
}
/**
調(diào)用
**/
public static void testStatic() {
User user = new User();
IUser proxyInstance = new UserProxy(user);
System.out.println(proxyInstance.getClass());
proxyInstance.say();
}
結(jié)果如下

缺點(diǎn):其實就是這個代理類與被代理類都需要實現(xiàn)同一個接口,有的時候沒有這種條件。并且接口方法一添加兩者都需要修改非常難維護(hù)。
JDK動態(tài)代理
JDK動態(tài)代理調(diào)用jdk的API,在代碼執(zhí)行時構(gòu)建代理類。關(guān)鍵api如下
//
/**
ClassLoader loader,:指定當(dāng)前目標(biāo)對象使用類加載器,獲取加載器的方法是固定的
Class<?>[] interfaces,:目標(biāo)對象實現(xiàn)的接口的類型,使用泛型方式確認(rèn)類型
InvocationHandler h:事件處理,執(zhí)行目標(biāo)對象的方法時,會觸發(fā)事件處理器的方法,會把當(dāng)前執(zhí)行目標(biāo)對象的方法作為參數(shù)傳入
**/
static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h )
第三個參數(shù)在執(zhí)行目標(biāo)方法的時候就會觸發(fā)這個處理器的invoke方法,這里面我們可以判斷是哪個方法,并獲得其參數(shù)和返回值。
/**
Object proxy,:代理對象
Method method,:調(diào)用的方法
Object[] args:方法傳入的參數(shù)
**/
public Object invoke(Object proxy, Method method,Object[] args)
它的原理就是運(yùn)行時解析到了需要加載的類的名稱,然后通過字節(jié)碼技術(shù)動態(tài)生成對應(yīng)的class字節(jié)碼,加載到內(nèi)存中得到class對象,進(jìn)一步得到實例對象。有興趣的同學(xué)可以看這段源碼
這里我使用了匿名類來當(dāng)參數(shù),因為要保存被代理對象,所以我這里定義了一個代理工廠
/**
ProxyFactory.java
**/
package com.shark.dynamicproxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class ProxyFactory {
// 維護(hù)一個目標(biāo)對象
private Object target;
public ProxyFactory(Object target) {
this.target = target;
}
public Object getProxyInstance() {
return Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(), new InvocationHandler() {
public Object invoke(Object proxy, Method method,
Object[] args) throws Throwable {
System.out.println("Before invoke " + method.getName());
method.invoke(target, args);
System.out.println("After invoke " + method.getName());
return null;
}
});
}
}
調(diào)用如下
public static void testDynamic() {
User user = new User();
IUser proxyInstance = (IUser) new ProxyFactory(user).getProxyInstance();
System.out.println(proxyInstance.getClass());
proxyInstance.say();
}
運(yùn)行

缺點(diǎn):目標(biāo)對象一定要實現(xiàn)接口,否則不能用動態(tài)代理
Cglib代理
Cglib代理,也叫作子類代理,它是在內(nèi)存中構(gòu)建一個子類對象從而實現(xiàn)對目標(biāo)對象功能的擴(kuò)展.所以被代理類不能為final
在被代理類沒有實現(xiàn)接口的時候上面jdk動態(tài)代理就不太好使了,我們可以使用Cglib代理
Cglib包的底層是通過使用一個小而塊的字節(jié)碼處理框架ASM來轉(zhuǎn)換字節(jié)碼并生成新的類.不鼓勵直接使用ASM,因為它要求你必須對JVM內(nèi)部結(jié)構(gòu)包括class文件的格式和指令集都很熟悉.
ASM這個東西我們以后可以好好討論一下,這里先留個印象。
這里需要引入jar包,spring的就不用了因為里面自帶了。
asm-7.1.jar
cglib-3.3.0.jar
代碼示例
//CglibProxyFactory.java
package com.shark.cglibproxy;
import java.lang.reflect.Method;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
public class CglibProxyFactory implements MethodInterceptor {
// 維護(hù)目標(biāo)對象
private Object target;
public CglibProxyFactory(Object target) {
this.target = target;
}
// 給目標(biāo)對象創(chuàng)建一個代理對象
public Object getProxyInstance() {
// 1.工具類
Enhancer en = new Enhancer();
// 2.設(shè)置父類
en.setSuperclass(target.getClass());
// 3.設(shè)置回調(diào)函數(shù)
en.setCallback(this);
// 4.創(chuàng)建子類(代理對象)
return en.create();
}
public Object intercept(Object obj, Method method, Object[] args,
MethodProxy proxy) throws Throwable {
System.out.println("Cglib Before invoke " + method.getName());
// 執(zhí)行目標(biāo)對象的方法
Object returnValue = method.invoke(target, args);
System.out.println("Cglib After invoke " + method.getName());
return returnValue;
}
}
實現(xiàn)MethodInterceptor 接口的intercept攔截方法
和jdk類似在調(diào)用代理對象的方法的時候,就和來到這個intercept方法
/**
Object obj:代理對象
Method method:被代理對象調(diào)用的方法
Object[] args:傳入的參數(shù)
MethodProxy proxy:代理方法
**/
public Object intercept(Object obj, Method method, Object[] args,MethodProxy proxy);
調(diào)用如下
public static void testCglib() {
User user = new User();
IUser proxyInstance = (IUser) new CglibProxyFactory(user).getProxyInstance();
System.out.println(proxyInstance.getClass());
proxyInstance.say();
}
運(yùn)行
