代理的作用
在實(shí)際的項(xiàng)目中,往往有一些通用的功能需要穿插在項(xiàng)目功能代碼的各個(gè)角落,比如很常見的log的記錄,還有接口訪問的權(quán)限檢查。總結(jié)下來,這類的代碼耦合,一種是預(yù)處理(信息過濾、權(quán)限校驗(yàn)),用來控制訪問;另一種是其他通用功能的織入用于功能擴(kuò)展。
使用代理的目的,就是解耦。把瑣碎的事情交給代理類去做,讓核心業(yè)務(wù)代碼專注于業(yè)務(wù),而無須關(guān)注那些通用功能的實(shí)現(xiàn),且那些通用功能的代碼也不會(huì)污染了核心的業(yè)務(wù)代碼。經(jīng)典的Spring Aop實(shí)際上就是代理的巧妙運(yùn)用。
另外EasyMock這類方便unit test的工具,也是基于代理。也正是對(duì)EasyMock的好奇,才來了解一下java的代理。
java代理的實(shí)現(xiàn)方法
靜態(tài)代理
-
動(dòng)態(tài)代理
-
靜態(tài)代理的實(shí)現(xiàn)思路
一個(gè)目標(biāo)類(被代理類),一個(gè)代理類,這兩個(gè)是核心,還有一個(gè)抽象接口,其實(shí)如果只是代理這個(gè)功能來說,抽象接口的意義是不明顯的。使用了抽象接口,主要在于在面向接口編程時(shí),代理類可以很優(yōu)雅地取代目標(biāo)類。
首先說不使用抽象接口的。實(shí)現(xiàn)機(jī)制就是代理類持有目標(biāo)類的對(duì)象,因?yàn)楝F(xiàn)在代理類持有了這個(gè)對(duì)象,就可以不直接使用目標(biāo)類對(duì)象來invoke方法,而是調(diào)用代理類的某個(gè)方法,然后代理類方法間接地去invoke目標(biāo)方法,這樣的間接過程,就可以在目標(biāo)方法的執(zhí)行前后追加其他業(yè)務(wù)邏輯。 原本的
Target.invoke()也變成了Proxy.invoke();代碼:
-
//目標(biāo)類
public class Real implements IService{
@Override
public void execute(){
//do something
}
}
//代理類
public class Proxy implements IService{
private Real realObj;
public Proxy(Real realObj){
this.realObj = realObj;
}
@Override
public void execute(){
//do pre
realObj.execute();
//do after
}
}
//抽象類
public Interface IService{
public void execute();
}
實(shí)際使用是將實(shí)體類傳給代理的構(gòu)造器,調(diào)用代理類的方法,就起到代理的作用。
public static void main(String []agrs){
IService proxy = new Proxy(new Real());
proxy.execute();
}
- 動(dòng)態(tài)代理
JDK提供了動(dòng)態(tài)代理的方法。動(dòng)態(tài)代理的所謂動(dòng)態(tài),動(dòng)的是代理類。動(dòng)態(tài)代理和靜態(tài)代理的區(qū)別,就是動(dòng)態(tài)生成了代理類,免去了手工編寫代理類的無趣。與靜態(tài)相比,可以少寫代碼偷點(diǎn)懶。
動(dòng)態(tài)代理的實(shí)現(xiàn)思路和靜態(tài)是一致的。簡(jiǎn)要記錄一下api的使用。
public static void main(String[] args) {
IService real = new RealService();
InvocationHandler handler = new MyInvocationHandler(real);
IService proxy = (IService)Proxy.newProxyInstance(real.getClass().getClassLoader(),real.getClass().getInterfaces(),handler);
proxy.execute();
}
InvocationHandler是JDK定義的接口,是一種Callback接口,通過它傳人的回調(diào)函數(shù),代理類對(duì)象上的的所有方法調(diào)用都回到這個(gè)Callback中的可執(zhí)行代碼。使用InvocationHandler方法既可以創(chuàng)建類實(shí)現(xiàn)InvocationHandler接口,也可以使用匿名內(nèi)部類。
動(dòng)態(tài)代理的主要優(yōu)點(diǎn)是,減少代碼量,并且實(shí)現(xiàn)的Callback類可重用,但是此方法的局限是只能代理實(shí)現(xiàn)了接口的類,因?yàn)樵赑roxy.newProxyInstance()方法中,使用類加載器和目標(biāo)類的接口在運(yùn)行時(shí)生成了代理類。在編譯后實(shí)際上可以發(fā)現(xiàn)一個(gè)包含*$*的class文件。
- cglib——JDK動(dòng)態(tài)代理的補(bǔ)全方案
cglib可以代理未實(shí)現(xiàn)接口的類。
CGLIB原理是動(dòng)態(tài)生成一個(gè)目標(biāo)類的子類,子類重寫目標(biāo)類的所有不是final的方法(因?yàn)閒inal方法無法重寫),在方法體中織入了回調(diào)函數(shù)的調(diào)用。
其使用方法是基于Enhancer類,Enhancer設(shè)置繼承的父類(即為要代理的類),設(shè)置回調(diào)函數(shù)。現(xiàn)有的回調(diào)函數(shù)有:
- net.sf.cglib.proxy.FixedValue, all method calls return a fixed value which is generated by the anonymous
FixedValueimplementation.
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(MyClass.class);
// FixedValue is a intercepter
enhancer.setCallback(new FixedValue() {
@Override
public Object loadObject() throws Exception {
System.out.println("FixedValue");
return "hello,cglib";
}
});
MyClass proxy = (MyClass) enhancer.create();
proxy.dosome();
proxy.execute();
System.out.println(proxy.getClass()); //final method
//author said:
/*The anonymous subclass of FixedValue would become hardly referenced from the enhanced SampleClass such that neither the anonymous FixedValue instance or the class holding the @Test method would ever be garbage collected. This can introduce nasty memory leaks in your applications. Therefore, do not use non-static inner classes with cglib. (I only use them in this overview for keeping the examples short.)
*/
- net.sf.cglib.proxy.InvocationHandler,和jdk中同款,它攔截所有方法到
invoke(),和FixedValue類似,但是比前者要更靈活一點(diǎn)。在其內(nèi)部處理要注意無限遞歸調(diào)用。
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(MyClass.class);
// net.sf.cglib.proxy.InvocationHandler is a intercepter
enhancer.setCallback(new net.sf.cglib.proxy.InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("InvocationHandler");
return null;
}
});
MyClass proxy = (MyClass) enhancer.create();
proxy.dosome();
proxy.execute();
- net.sf.cglib.proxy.MethodInterceptor,The
MethodInterceptorallows full control over the intercepted method and offers some utilities for calling the method of the enhanced class in their original state.它的回調(diào)方法里proxy.invokeSuper(obj, args);可以通過父類調(diào)用其它方法,而不必全部都指向回調(diào)函數(shù)。
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(MyClass.class);
// MethodInterceptor
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
if(method.getDeclaringClass() != Object.class && method.getReturnType() == String.class) {
return "Hello cglib!";
} else {
return proxy.invokeSuper(method, args);
}
}
});
MyClass proxy = (MyClass) enhancer.create();
proxy.dosome();
proxy.execute();
proxy.hashCode();
System.out.println(proxy.getClass());
這些都是既有的回調(diào)函數(shù),需要更加的自定義,可以使用Enhancer#setCallbackFilter(CallbackHelper callbackHelper)方法自定義回調(diào)函數(shù),callbackHelper返回回調(diào)函數(shù)對(duì)象,至于返回那種,就是可以在callbackHelper中自定義了,如對(duì)不同的方法,指定不同的回調(diào)函數(shù)。
Enhancer enhancer = new Enhancer();
CallbackHelper callbackHelper = new CallbackHelper(SampleClass.class, new Class[0]) {
@Override
protected Object getCallback(Method method) {
if(method.getDeclaringClass() != Object.class && method.getReturnType() == String.class) {
return new FixedValue() {
@Override
public Object loadObject() throws Exception {
return "Hello cglib!";
};
}
} else {
return NoOp.INSTANCE; // A singleton provided by NoOp.
}
}
};
enhancer.setSuperclass(MyClass.class);
enhancer.setCallbackFilter(callbackHelper);
enhancer.setCallbacks(callbackHelper.getCallbacks());
//code snippet via https://github.com/cglib/cglib/wiki/Tutorial
summary
java代理的實(shí)現(xiàn)機(jī)制,大概就是這兩步:(1)創(chuàng)建目標(biāo)類的可替代類;(2)織入回調(diào)方法。
回頭去看靜態(tài)代理,實(shí)際上就是代理實(shí)現(xiàn)的基礎(chǔ)與核心。后面的動(dòng)態(tài)代理,無論是JDK自帶的還是CGLIB擴(kuò)展的,無非就是在尋找方法取代手工編寫可替代類的方法,并將整個(gè)過程“接口化”。