JDK動態代理筆記,學習框架必備知識,學習框架之前先學一下這個

學JAVA的好人發表於2020-10-19

目錄

原理

暫時的掌握程度

動態代理模式的作用

靜態代理

動態代理

動態代理的分類

Method類的複習(其實是預習)

三個類的介紹

實現動態代理的步驟


  1. 原理

    1. 基於反射機制

    2. a想要訪問c,但是a沒有訪問c的許可權;這是可在a、c之間建立一個可以被a訪問且能訪問c的b;a就可以通過訪問b,讓b去訪問c,間接訪問c;並且還可以在b上實現更多的業務

  2. 暫時的掌握程度

    1. 先做了解,之後學mybatis和spring再學習怎麼用

    2. 知道是什麼動態代理?

    3. 知道動態代理能做什麼?

  3. 動態代理模式的作用

    1. 功能增強:在原有基礎上增加新功能;除了介面的實現功能外,其他功能都是增強功能

    2. 控制訪問:代理類可不讓你訪問目標

  4. 靜態代理

    1. 需要手動實現,建立一個Java類來作為代理類

    2. 優缺點:

      1. 優點:實現簡單、容易理解

      2. 缺點:代理類過多、難修改(需要一個個修改)

    3. 例子

      1. 需求:模擬一個使用者購買某品牌U盤

        1. 使用者是客戶端類;商家是代理類,代理某品牌的U盤;廠家是目標類

        2. 商家和廠家都是賣U盤的,他們完成的功能是一致的,都是賣U盤

      2. 實現步驟:

        1. 建立一個介面,定義賣U盤的方法,表示你的廠家和商家做的事情

        2. 建立廠家類,實現步驟1的介面

        3. 建立商家類(代理類),同樣要實現步驟1的介面

        4. 建立客戶端類,呼叫商家的方法買U盤

  5. 動態代理

    1. 什麼是動態代理:

      1. 使用jdk的反射機制,建立物件的能力,建立的是代理類的物件。不需要我們建立類檔案

      2. 動態:在程式執行時,呼叫jdk提供的方法才能建立代理類的物件

    2. 優點:

      1. 避免了靜態代理的缺點,代理類數量可以很少,修改介面中的方法時不會影響代理類

    3. 實現:

      1. 在程式執行過程中,使用jdk的反射機制,建立代理類物件,並動態的指定要代理的目標類

      2. 簡而言之:不用建立代理類就能夠建立代理物件(介面--->物件)

      3. 注意:學框架再學實現

  6. 動態代理的分類

    1. jdk動態代理(理解)

      1. 使用Java反射包中的類和介面實現動態代理的功能

      2. Java反射包的三個類:InvocationHandler、Method、Proxy

    2. ogLib動態代理(瞭解)

      1. ogLib是第三方工具庫,使用該工具庫來建立代理物件

      2. ogLib的原理是繼承和重寫方法。通過繼承目標類建立它的子類,在子類中重寫父類中同名的方法,實現功能的修改

      3. 因為ogLib的機制是繼承和重寫方法,所以要求目標類和方法不能是final的(要求寬鬆,能繼承即可)

      4. ogLib在很多框架中使用

  7. Method類的複習(其實是預習)

    1. invoke()方法

      1. 是Method類的一個方法,表示執行方法的呼叫(用來呼叫方法)

      2. 引數:引數1:Object,表示這個物件要執行的方法; 引數2:Object...,方法執行時的引數值

      3. 返回值:Object,方法執行的返回值

    2. 程式碼展示

      1. 純淨版

public static void main(String[] args) throws Exception {
    HelloService target = new HelloServiceImpl();
    Method method = HelloService.class.getMethod("sayHello", String.class);
    Object ret = method.invoke(target, "李四");
}
  1. 有註釋筆記版

// 使用反射機制執行sayHello方法。核心Method類中的方法
public static void main(String[] args) throws Exception {
    // 建立一個需要執行方法的物件
    HelloService target = new HelloServiceImpl();
    // 獲取sayHello名稱對應的Method類物件
        // 引數1:需要獲取的物件的名稱(方法名稱);   引數2:該方法引數的資料型別
    Method method = HelloService.class.getMethod("sayHello", String.class);
    // 通過Method物件執行target的sayHello方法
        /*
        invoke是Method類的一個方法,表示執行方法的呼叫(用來呼叫方法)
        引數:引數1:Object,表示要執行的方法的物件; 引數2:Object...,方法執行時的引數值
        返回值:Object,方法執行的返回值
         */
        // 需要執行sayHello方法的物件是target,方法的引數是“李四”(執行target的sayHello方法,引數是“李四”)
    Object ret = method.invoke(target, "李四");
}
  1. 三個類的介紹

    1. Java反射包(java.lang.reflect)的三個類:InvocationHandler、Method、Proxy

    2. InvocationHandler介面(呼叫處理器)

      1. InvocationHandler介面用來表示代理要乾的事情

      2. invoke()方法(該介面只有一個方法)

        1. invoke()方法表示代理物件要執行的功能程式碼。代理類要完成的功能寫在invoke()方法裡面。

        2. 代理類完成的功能:

          1. 呼叫目標方法,執行目標方法的功能

          2. 功能增強,在目標方法呼叫時,增加功能

        3. 方法原型:public Object invoke(Object proxt, Method method, Object[] args){...}

          1. 引數分析:(第一個引數不需要賦值,傳遞後兩個引數即可)

            1. Object proxt:jdk建立的代理物件,無需賦值(這個引數不需要我們賦值)

            2. Method method:目標類中的方法,jdk提供method物件

            3. Object[] args:目標類中方法的引數

        4. 使用方式:

          1. 建立InvocationHandler介面的實現類

          2. 重寫invoke()方法,把要實現的功能寫在該方法裡

    3. Method類

      1. Method類用來表示目標類中的方法,可用於呼叫invoke方法來執行方法

      2. 作用:通過Method類可以執行某個目標類的方法Method.invoke(目標物件, 方法引數)

    4. Proxy類

      1. Proxy類用來建立代理物件,Proxy類物件是核心的物件。常規建立物件是“new 類的構造方法”,現使用Proxy類的方法來代替new

      2. newProxyInstance()靜態方法

        1. 作用:建立代理物件

        2. 方法原型:public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)

          1. ClassLoader loader:類載入器,負責向記憶體中載入物件,使用反射獲取物件的ClassLoader

          2. Class<?>[] interfaces:通過反射獲取的目標物件的類所實現的介面

          3. InvocationHandler h:代理類要完成的功能,我們自己寫(就是InvocationHandler介面的實現類,重寫的invoke方法包含了要實現的功能)

          4. 返回值:代理物件

  2. 實現動態代理的步驟

    1. 建立介面,定義目標類要完成的功能

    2. 建立目標類實現介面

    3. 建立InvocationHandler介面的實現類,在invoke()方法中完成代理類的功能

      1. 呼叫目標方法

      2. 增強功能

    4. 使用Proxy類的靜態方法建立代理物件,並把代理物件轉為介面型別

    5. 程式碼展示

      1. 建立介面

package com.bjpowenode.service;

// 目標介面
public interface UsbSell {
    //
    float sell(int amount);
}
  1. 建立目標類實現介面

package com.bjpowenode.factory;

import com.bjpowenode.service.UsbSell;

public class UsbKingFactory implements UsbSell {
    @Override
    public float sell(int amount) {
        System.out.println("目標類中,執行sell目標方法");
        return 85.0f;
    }
}
  1. 建立InvocationHandler介面的實現類

package com.bjpowenode.handler;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

// 實現InvocationHandler介面,完成代理類要做的功能(呼叫目標方法、功能增強)
public class MySellHandler implements InvocationHandler {

    private Object target = null;

    // 動態代理:目標物件是活動的,不是固定的,需要傳入進來。傳入誰,就給誰建立代理
    public MySellHandler(Object target){
        // 給目標物件賦值
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 定義一個變數,等會接受執行方法時的返回值
        Object res = null;

        // 執行目標方法,獲取返回值
        res = method.invoke(target, args);  // 執行目標方法

        // 加價
        if (res != null) {
            Float price = (Float)res;
            price = price + 25;
            res = price;
        }

        // 在目標類的方法呼叫後,實現其他功能,即功能增強
        System.out.println("您獲得一個返利紅包");

        // 返回加價後的價格
        return res;
    }
}
  1. 建立代理物件,並執行功能

package com.bjpowenode;

import com.bjpowenode.factory.UsbKingFactory;
import com.bjpowenode.handler.MySellHandler;
import com.bjpowenode.service.UsbSell;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

public class MsinShop {

    public static void main(String[] args) {
        // 使用Proxy類的靜態方法建立代理物件,並把代理物件轉為介面型別

        // 1、建立目標物件
        UsbSell factory = new UsbKingFactory();
        // 2、建立InvocationHandler物件
        InvocationHandler handler = new MySellHandler(factory);
        // 3、建立代理物件。
        // getClass()方法是獲取物件的類,     getInterfaces()方法是獲取類所實現的介面
        // getClassLoader()方法是獲取這個類的載入器(類載入器負責讀取 Java 位元組程式碼,並轉換成 java.lang.Class類的一個例項。)
        UsbSell proxy = (UsbSell) Proxy.newProxyInstance(factory.getClass().getClassLoader(),
                factory.getClass().getInterfaces(),
                handler);
        // 4、通過代理執行方法
        float price = proxy.sell(1);

        System.out.println("通過動態代理物件呼叫方法:" + price);
    }
}
  1. 注意

    1. jdk動態代理,必須有介面,目標型別實現介面,沒有介面時,需要使用cglib動態代理

相關文章