靜態代理,JDK 動態代理,CGLIB 動態代理都是些什麼鬼?

代理模式(靜態代理)

代理類爲被代理類預處理消息、過濾消息並在此之後將消息轉發給被代理類,之後還能進行消息的後置處理。代理類和被代理類通常會存在關聯關係(代理類持有的被代理對象的引用 private RealImage realImage;),代理類本身不實現服務,而是通過調用被代理類中的方法來提供服務。代理對象和實際對象都繼承了同一個接口,在代理對象中指向的是實際對象的實例,這樣對外暴露的是代理對象而真正調用的是 Real Object.

  • 優點:可以很好的保護實際對象的業務邏輯對外暴露,從而提高安全性。
  • 缺點:不同的接口要有不同的代理類實現,會很冗餘
  1. 創建一個接口(Image),然後創建被代理的類(RealImage)實現該接口並且實現該接口中的抽象方法。
public interface Image {

    String findImage(String name);

    String display();
}

public class RealImage implements Image {

    private String fileName;

    public RealImage() {
    }

    public RealImage(String fileName){
        this.fileName = fileName;
    }

    @Override
    public String findImage(String name) {
        System.out.println("原有的findImage方法");
        return "find by " + name;
    }

    @Override
    public String display() {
        System.out.println("原有的display方法");
        return "Displaying " + fileName;
    }
}
  1. 創建一個代理類(ProxyImage),同時也實現這個接口。
  2. 在代理類中持有一個被代理對象的引用,而後在代理類方法中調用該對象的方法。
public class ProxyImage implements Image {

    private RealImage realImage;

    private String fileName;

    public ProxyImage(String fileName){
        this.fileName = fileName;
        realImage = new RealImage(fileName);
    }


    @Override
    public String findImage(String name) {

        String image = realImage.findImage(name);
        System.out.println("增強被代理的方法");
        return image;
    }

    @Override
    public String display() {

        String display = realImage.display();
        System.out.println("增強被代理的方法");
        return display;
    }
}

public class ProxyPatternDemo {
    public static void main(String[] args) {
        Image image = new ProxyImage("test_10mb.jpg");

        String display = image.findImage("6666.avi");
        System.out.println(display);

        System.out.println("");

        String display1 = image.display();
        System.out.println(display1);
    }
}

動態代理

Java動態代理類位於java.lang.reflect包下,一般主要涉及到以下兩個類:

  • InvocationHandler 該接口中僅定義了一個方法invoke(Object obj,Method method, Object[] args)
  • Proxy 動態代理類

動態代理步驟

  1. 創建被代理的類以及接口
  2. 創建一個實現接口InvocationHandler的類,它必須實現invoke方法
public class InvocationHandlerImpl implements InvocationHandler {

    // 要代理的對象
    private Object target;

    // 給要代理的真實對象賦初值
    public InvocationHandlerImpl(Object target) {
        this.target = target;
    }

	/* 	proxy:  - 指代我們所代理的那個真實對象
    	method: - 指代的是我們所要調用真實對象的某個方法的Method對象
    	args:  - 指代的是調用真實對象某個方法時接受的參數*/
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("before..............");
        System.out.println("要增強的方法:" + method.getName());

        //當代理對象調用真實對象的方法時,其會自動的跳轉到代理對象關聯的handler對象的invoke方法來進行調用
        Object value = method.invoke(target, args);
        System.out.println("after..............");
        return value;
    }
}
  1. 通過Proxy的靜態方法newProxyInstance(ClassLoaderloader, Class[] interfaces, InvocationHandler handler)創建一個代理
  2. 通過代理調用方法
public class DynamicProxyDemonstration {
    public static void main(String[] args) {
        Image realImage =  new RealImage("66666.img");
        /**
         * InvocationHandlerImpl 實現了 InvocationHandler 接口,並能實現方法調用從代理類到委託類的分派轉發
         * 其內部通常包含指向委託類實例的引用,用於真正執行分派轉發過來的方法調用.
         * 即:要代理哪個真實對象,就將該對象傳進去,最後是通過該真實對象來調用其方法
         */
        InvocationHandler handler = new InvocationHandlerImpl(realImage);

        ClassLoader loader = realImage.getClass().getClassLoader();
        
        Class[] interfaces = realImage.getClass().getInterfaces();

        /**
         * 該方法用於爲指定類裝載器、一組接口及調用處理器生成動態代理類實例
         *
         * 通過Proxy的newProxyInstance方法來創建我們的代理對象,我們來看看其三個參數
         * 第一個參數 handler.getClass().getClassLoader() ,我們這裏使用handler這個類的ClassLoader對象來加載我們的代理對象
         * 第二個參數realSubject.getClass().getInterfaces(),我們這裏爲代理對象提供的接口是真實對象所實行的接口,表示我要代理的是該真		
         * 實對象,這樣我就能調用這組接口中的方法了
         * 第三個參數handler, 我們這裏將這個代理對象關聯到了上方的 InvocationHandler 這個對象上
         */
        Image imageProxy = (Image) Proxy.newProxyInstance(loader, interfaces, handler);


        String display = imageProxy.display();
        System.out.println(display);

        String name = imageProxy.findImage("789.avi");
        System.out.println(name);
    }
}

CGLIB 動態代理

CGLIB 創建動態代理類的模式是:

  1. 查找目標類上的所有非 final 的 public 類型的方法 (final 的不能被重寫)
  2. 將這些方法的定義轉成字節碼
  3. 將組成的字節碼轉換成相應的代理的 Class 對象然後通過反射獲得代理類的實例對象
  4. 實現 MethodInterceptor 接口, 用來處理對代理類上所有方法的請求
public class CglibInterceptor implements MethodInterceptor {

    /**
     * CGLIB 增強類對象,代理類對象是由 Enhancer 類創建的,
     * Enhancer 是 CGLIB 的字節碼增強器,可以很方便的對類進行拓展
     */
    private Enhancer enhancer = new Enhancer();

    /**
     *
     * @param object  被代理的對象
     * @param method 代理的方法
     * @param objects 方法的參數
     * @param methodProxy CGLIB方法代理對象
     * @return  cglib生成用來代替Method對象的一個對象,使用MethodProxy比調用JDK自身的Method直接執行方法效率會有提升
     * @throws Throwable
     */
    @Override
    public Object intercept(Object object, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("方法增強前........");
        Object o = methodProxy.invokeSuper(object, objects);
        System.out.println("方法增強後........");
        return o;
    }

    /**
     * 創建一個代理對象
     */
    public Object newProxyInstance(Class<?> c) {
        /**
         * 設置產生的代理對象的父類,增強類型
         */
        enhancer.setSuperclass(c);
        /**
         * 定義代理邏輯對象爲當前對象,要求當前對象實現 MethodInterceptor 接口
         */
        enhancer.setCallback(this);
        /**
         * 使用默認無參數的構造函數創建目標對象,被代理的類要提供無參構造方法
         *  有參構造 create(Class[] argumentTypes, Object[] arguments)
         */
        //return enhancer.create();
        return enhancer.create(new Class[]{String.class}, new Object[]{"daqiao.avi"});
    }
}


// 測試類
public class CglibProxyDemo {
    public static void main(String[] args) {
        CglibInterceptor cglibInterceptor = new CglibInterceptor();

        Image image = (Image )cglibInterceptor.newProxyInstance(RealImage.class);

        String display = image.display();
        System.out.println(display);

        String image1 = image.findImage("123.avi");
        System.out.println(image1);
    }
}

JDK 動態代理和 CGLIB 動態代理的區別

  1. JDK 動態代理基於 Java 反射機制實現, 必須要實現了接口的業務類才能用這種方法生成代理對象,因爲動態生成的類默認繼承Proxy類。而 CGLIB 動態代理基於 ASM 框架通過生成業務類的子類來實現。
  2. JDK 動態代理的優勢是最小化依賴關係,代碼實現簡單。因爲是基於接口設計實現的,如果沒有接口,會拋異常。而基於 CGLIB 框架的優勢是無須實現接口。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章