Java JDK動態代理 CGLib動態代理

代理(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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章