【Spring】(7)JDK 動態代理

一、動態代理

  • 動態代理和靜態代理角色都一樣(抽象角色、真實角色、代理角色)。

  • 動態代理的代理類是動態生成的,不是我們直接寫好的。(靜態代理是我們自己寫了代理類,但是動態代理就不需要我們自己的寫代理類了。)

  • 動態代理分爲:

    • 基於接口的:JDK 動態代理
    • 基於類:cglib
    • 基於Java 字節碼:Javassist

我們主要學習JDK 的動態代理(Spring AOP默認使用JDK動態代理)。

需要在JDK的API文檔中去看看 InvocationHandler(接口)、Proxy(類) 。(在反射包reflect下)

二、租房案例

還是使用上一篇靜態代理中的案例,不過這次使用的是動態代理!

對象:房東(Renter )、中介(Proxy)

出租事件:Rent接口的rent()方法,用於出租。

關係:中介(代理對象)可以代理房東(真實對象)租房子。

在這裏插入圖片描述

1.抽象角色

抽象角色:代理角色(代理人)和真實角色可以實現)

這裏抽象角色爲一個接口:出租方法,用於出租房屋。

/** 房東Host和代理人中介Proxy都可以實現出租Rent這件事情(方法) */
public interface Rent {
    /** 出租房屋 */
    void rent();
}

2.真實角色

真實角色:真實的角色,可以實現業務方法.

真實角色房東,可以實現出租房屋。房東僅僅只用關注出租房屋。

/** 房東:可以出租房屋的人 */
public class Renter implements Rent {
    @Override
    public void rent() {
        System.out.println("房東出租房子。");
    }
}

3.創建調用處理器

創建一個類並且實現InvocationHandler接口。

用這個類來生成代理實例。

/** 調用處理類實例:(每個代理實例都有一個關聯的調用處理程序) */
public class MyInvocationHandler implements InvocationHandler {
    /** 被代理的接口 */
    private Rent rent;
    public void setRent(Rent rent) {
        this.rent = rent;
    }

    /** 真正生成代理類的方法:返回rent接口的代理類實例 */
    public Object getProxy() {
        // 參數解釋:loader:類加載器來定義代理類。interfaces :代理類實現的接口列表。h:調度方法調用的調用處理函數(因爲實現了InvocationHandler所以直接填this)
        return Proxy.newProxyInstance(this.getClass().getClassLoader(), rent.getClass().getInterfaces(), this);
    }

    /**
     * 處理代理實例,並返回結果。(處理代理實例上的方法調用並返回結果。)
     * proxy:調用該方法的代理實例(代理類代理的真實代理對象com.sun.proxy.$Proxy0(生成的代理對象)
     * method:我們所要調用某個對象真實的方法的Method對象
     * args:指代代理對象方法傳遞的參數
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        before();
        // 方法調用
        Object result = method.invoke(rent, args);
        after();
        return result;
    }

    void before() {
        System.out.println("----- 中介帶你看房 -----");
    }
    void after() {
        System.out.println("----- 中介帶你籤合同 -----");
    }
}

4.測試

創建調用處理器,set置入需要代理的對象(房東),獲取代理對象(中介)。最後代理對象(中介)執行出租方法。

/** 租客:需要租房子的人 */
public class Tenant {
    /** 顧客去租房 */
    public static void main(String[] args) {
        // 創建調用處理器
        MyInvocationHandler ih = new MyInvocationHandler();
        // set 置入需要代理的對象(房東)
        ih.setRent(new Renter());
        // 獲取代理對象(中介)
        Rent proxy = (Rent) ih.getProxy();
        // 代理對象,執行出租方法。
        proxy.rent();
    }
}

輸出:

----- 中介帶你看房 -----
房東出租房子。
----- 中介帶你籤合同 -----

三、真實業務案例

還是使用上一篇靜態代理中的案例,不過這次使用的是動態代理!

需求:

增加業務日誌輸出功能:業務調用前需要輸出調用的方法名,調用後需要輸出返回值。

1.抽象角色

用戶增刪改查接口。

/** 用戶數據訪問層接口 */
public interface UserDao {
    void ins();
    void del();
    void upd();
    void sel();
}

2.真實對象

用戶接口的實現類

public class UserDaoImpl implements UserDao {
    @Override
    public void ins() {
        System.out.println("insert命令");
    }

    @Override
    public void del() {
        System.out.println("delete命令");
    }

    @Override
    public void upd() {
        System.out.println("update命令");
    }

    @Override
    public void sel() {
        System.out.println("select命令");
    }
}

3.創建調用處理類

創建調用處理類

/** 調用處理類實例:(每個代理實例都有一個關聯的調用處理程序) */
public class MyInvocationHandler implements InvocationHandler {
    private UserDao userDao;
    public void setUserDaoImpl(UserDao userDao) {
        this.userDao = userDao;
    }

    /** 獲取代理對象 */
    public Object getProxy() {
        return Proxy.newProxyInstance(this.getClass().getClassLoader(), userDao.getClass().getInterfaces(), this);
    }

    /** 處理代理實例,並返回結果。 */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        log(method.getName());
        Object result = method.invoke(userDao, args);
        logReturnType(method.getReturnType().getName());
        return result;
    }
    /** 日誌 */
    private void log(String name) {
        System.out.println("[info]: ----- "+name + "()方法 -----");
    }
    /** 日誌 */
    private void logReturnType(String name) {
        System.out.println("[info]: ----- 返回類型:"+name + " -----");
    }
}

4.測試

創建調用處理類實例,set注入需要代理的對象(UserDaoImpl),獲取代理對象,最後執行代理對象的插入方法

public class Test {
    public static void main(String[] args) {
        // 創建調用處理類實例
        MyInvocationHandler ih = new MyInvocationHandler();
        // set 置入需要代理的對象(UserDaoImpl)
        ih.setUserDaoImpl(new UserDaoImpl());
        // 獲取代理對象(UserDaoImpl)
        UserDao proxy = (UserDao) ih.getProxy();
        // 代理對象,執行插入方法。
        proxy.ins();
    }
}

輸出:

[info]: ----- ins()方法 -----
insert命令
[info]: ----- 返回類型:void -----

四、封裝成通用類

將剛剛使用的兩個MyInvocationHandler給封裝成通用類。至此i,JDK的動態代理已結束,開始學習Spring 的AOP。

/** 調用處理類實例:(每個代理實例都有一個關聯的調用處理程序) */
public class MyInvocationHandler implements InvocationHandler {
    /** 目標:被代理的接口 */
    private Object target;
    public void setTarget(Object target) {
        this.target = target;
    }

    /** 返回代理類實例 */
    public Object getProxy() {
        // 參數解釋:loader:類加載器來定義代理類。interfaces :代理類實現的接口列表。h:調度方法調用的調用處理函數(因爲實現了InvocationHandler所以直接填this)
        return Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
    }

    /** 處理代理實例,並返回結果。(處理代理實例上的方法調用並返回結果。) */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 方法調用
        Object result = method.invoke(target, args);
        return result;
    }
}

相關

我的該分類的其他相關文章,請點擊:【Spring + Spring MVC + MyBatis】文章目錄

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