代理(proxy):就是一個“中介”。
現在對象A可以直接調用對象B。
需求來了:在調用B的前後打印日誌。
靜態代理
創建一個新類(發生在編譯時),來作爲舊類的代理,從而增加功能。增加新功能的代碼無法複用。
public class Hello{
public void sayHello(){
System.out.println("Hello");
}
}
// 代理類
public class ProxyStatic extends Hello{
private Hello hello = new Hello();
public void sayHello(){
System.out.println("START log");
hello.sayHello();
System.out.println("END log");
}
}
public class ProxyStaticTest {
public static void main(String[] args){
// 調用原來的類
Hello hello = new Hello();
hello.sayHello();
// 調用代理類
ProxyStatic helloNew = new ProxyStatic();
helloNew.sayHello();
}
}
輸出:
Hello
START log
Hello
END log
動態代理 by JDK
需求增加:有一批被調用對象,給這些調用的前後都加上日誌。所以,增加新功能的代碼可複用,不可能給每一個類都寫一個靜態代理類;可在運行時修改,也就是說原來的類已經編譯加載到內存了。所以,不能修改原來的類,只能創建一個新類,攔截原來的方法。
// 接口
public interface IHello {
void sayHello();
}
// 實現了接口的類
public class Hello implements IHello{
public void sayHello(){
System.out.println("Hello");
}
}
import java.lang.reflect.*;
// 調用處理器
public class LoggerHandler implements InvocationHandler {
private Object target;
public LoggerHandler(Object target){
this.target = target;
}
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable{
System.out.println("START log"); // 增加新功能
Object result = method.invoke(target, args); // 類中的方法調用
System.out.println("END log"); // 增加新功能
return result;
}
}
import java.lang.reflect.Proxy;
public class ProxyJDKTest {
public static void main(String[] args){
IHello hello = new Hello();
// 直接調用,不使用代理
hello.sayHello();
// 使用代理 by JDK
LoggerHandler handler = new LoggerHandler(hello); // 創建調用處理器
IHello helloNew = (IHello) Proxy.newProxyInstance( // 創建代理類
Thread.currentThread().getContextClassLoader(),
hw.getClass().getInterfaces(),
handler
);
helloNew.sayHello(); // 使用代理類調用
}
}
查看代理類(helloNew.getClass()):
import sun.misc.ProxyGenerator;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.Proxy;
public class ProxyJDKTest {
public static void main(String[] args) throws IOException {
IHello hello = new Hello();
// use proxy by JDK
LoggerHandler handler = new LoggerHandler(hello);
IHello helloNew = (IHello) Proxy.newProxyInstance(
Thread.currentThread().getContextClassLoader(),
hello.getClass().getInterfaces(),
handler
);
helloNew.sayHello();
ProxyJDKTest.saveClass(helloNew.getClass()); // 保存代理類的class文件
}
public static void saveClass(Class cls) throws IOException {
byte[] classFile = ProxyGenerator.generateProxyClass(cls.getName(), cls.getInterfaces());
File file =new File(cls.getName() + ".class");
FileOutputStream fos =new FileOutputStream(file);
fos.write(classFile);
fos.flush();
fos.close();
}
}
IntelliJ IDEA下查看生成的 $Proxy0.class,自動翻譯爲源碼(去掉try/catch):
public final class $Proxy0 extends Proxy implements IHello {
private static Method m1;
private static Method m3;
private static Method m2;
private static Method m0;
public $Proxy0(InvocationHandler var1) throws {
super(var1);
}
public final boolean equals(Object var1) throws {
……
return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
……
}
public final void sayHello() throws {
……
super.h.invoke(this, m3, (Object[])null);
……
}
public final String toString() throws {
……
return (String)super.h.invoke(this, m2, (Object[])null);
……
}
public final int hashCode() throws {
……
return (Integer)super.h.invoke(this, m0, (Object[])null);
……
}
static {
……
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m3 = Class.forName("proxy.proxyJDK.IHello").getMethod("sayHello");
m2 = Class.forName("java.lang.Object").getMethod("toString");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
……
}
}
super.h 是 Proxy類的 protected InvocationHandler h;
可見,代理類
無關於:被代理類、handler(InvocationHandler的具體實現)
有關於:接口數組(動態地實現了接口,實現就是轉發給 handler,即調動super.h)
對被代理類調用,由 handler 來管理。
JDK動態代理 總結:
實現機制:代理類主要是實現了接口,在接口的方法實現中,將調用轉發給 handler;handler調用被代理類,且在調用前後增加新功能。
侷限:只能爲接口創建代理,返回的代理對象只能轉換爲某個接口類型。
不適用的情況:如果一個類沒有接口,或希望代理代理的方法不是接口中定義的方法,那就不能使用 JDK動態代理了。
動態代理 by CGLib
通過 JDK 實現動態代理有個限制:原來的類的方法是實現的接口中的方法。也就是需要一個接口。
如果類沒有實現接口呢?使用 CGLib。
先導入 com.springsource.net.sf.cglib-2.2.0.jar 命名爲 net.sf.cglib。
public class Hello {
public void sayHello(){
System.out.println("Hello");
}
}
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class LogInterceptor implements MethodInterceptor {
public Object intercept(Object obj,
Method method,
Object[] args,
MethodProxy proxy) throws Throwable{
System.out.println("START log");
Object result = proxy.invokeSuper(obj, args);
System.out.println("END log");
return result;
}
}
import net.sf.cglib.proxy.Enhancer;
public class proxyCGLibTest {
public static void main(String[] args){
// 不使用代理
Hello hello = new Hello();
hello.sayHello();
// 使用代理 by CGLib
Enhancer enhancer = new Enhancer(); // 創建一個增強器,用來在運行時生成類
enhancer.setSuperclass(Hello.class); // 設置要繼承的目標類
enhancer.setCallback(new LogInterceptor()); // 設置 logInterceptor
Hello helloNew = (Hello) enhancer.create(); // 生成新的類
helloNew.sayHello(); // 調用代理類的方法
}
}
CGLib動態代理 總結:
實現機制:代理類是被代理類的一個子類,重寫了父類的所有 public非final 方法,改爲調用 Callback中的相關方法(示例中的 intercept)
JDK動態代理與CGLib動態代理對比
JDK動態代理
面向的是一組接口。
動態地創建一個新類,實現這些接口。接口的具體實現是通過 自定義的InvocationHandler 實現的。
代理的是對象。
先有一個被代理的對象;自定義的InvocationHandler引用該對象,然後創建一個代理類和代理對象;客戶端訪問的是代理對象,代理對象再調用實際對象的方法。
CGLib動態代理
面向的是一個具體的類。
動態地創建一個新類,繼承了被代理類。重寫方法。
代理的是類。創建的對象只有一個。
另
在Python這種動態語言中,實現“動態代理”太容易了。
使用裝飾器:
def proxy(func):
def warp():
print('START log')
result = func()
print('END log')
return result
return warp
@proxy
def say_hello():
print('hello')
if __name__ == '__main__':
say_hello()
在函數 say_hello上@proxy,相當於是 say_hello = proxy(say_hello)
等號右邊:原來的函數say_hello作爲參數傳遞給函數proxy,proxy的返回值是函數wrap。
等號以及左邊:最後讓say_hello變量引用wrap。也就是說函數say_hello名稱不變,但函數體是wrap