保護代理

保護代理的實現(xiàn)方式有兩種:靜態(tài)代理和動態(tài)代理。首先代理模式的作用是什么呢?控制訪問。

代理模式

  • 場景
    為其他對象提供一種代理,控制對這個對象的訪問——控制訪問

  • 條件(特點)
    1、代理對象和被代理對象繼承同一個父類
    2、代理對象持有被代理對象的引用

  • 為什么要滿足這個條件
    當然是做控制訪問,代理類和被代理類繼承或?qū)崿F(xiàn)同一個接口,實際操作的還是被代理類,代理類只是做訪問控制

虛代理

  • 根據(jù)需要創(chuàng)建開銷很大的對象,該對象只有在需要的時候才會被真正創(chuàng)建
  • 懶加載 LazyLoad,ORM(對象映射關(guān)系框架)

靜態(tài)代理

首先引用一個案例:

代理模式條件首先是代理對象和被代理對象要繼承或?qū)崿F(xiàn)同一個接口,所以先定義一個接口 OrderApi

  public interface OrderApi {

    public String getUserName();

    public void setUserName(String userName,String updateUser);

    public String getOrderName();

    public void setOrderName(String orderName,String updateUser);

    public String getOrderId();

    public void setOrderId(String orderId,String updateUser);

    public String getOrderTime();

    public void setOrderTime(String orderTime,String updateUser);

}

然后定義一個被代理類Order(實際做業(yè)務(wù)操作的類,以下以JavaBean做示范,并非說被代理類是JavaBean),然后實現(xiàn)OrderAPI接口

public class Order implements OrderApi {

    private String userName;
    private String orderName;
    private String orderId;
    private String orderTime;
    

    public Order(String userName, String orderName, String orderId, String orderTime) {
        super();
        this.userName = userName;
        this.orderName = orderName;
        this.orderId = orderId;
        this.orderTime = orderTime;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName,String updateUser) {
        this.userName = userName;
    }

    public String getOrderName() {
        return orderName;
    }

    public void setOrderName(String orderName,String updateUser) {
        this.orderName = orderName;
    }

    public String getOrderId() {
        return orderId;
    }

    public void setOrderId(String orderId,String updateUser) {
        this.orderId = orderId;
    }

    public String getOrderTime() {
        return orderTime;
    }

    public void setOrderTime(String orderTime,String updateUser) {
        this.orderTime = orderTime;
    }

}

然后定義一個代理類OrderProxy,實現(xiàn)OrderApi接口并持有Order被代理類引用,當進行set操作的時候做權(quán)限判斷,只有本人可以修改這個訂單:

public class OrderProxy implements OrderApi {

    private Order order;

    public OrderProxy(Order order) {
        this.order = order;
    }

    @Override
    public String getUserName() {
        return order.getUserName();
    }

    @Override
    public void setUserName(String userName, String updateUser) {
        if (updateUser != null && updateUser.equals(order.getUserName())) {
            order.setUserName(userName, updateUser);
        } else {
            throw new IllegalAccessError(updateUser + "同學(xué),你沒有權(quán)限操作!");
        }
    }

    @Override
    public String getOrderName() {
        return order.getOrderName();
    }

    @Override
    public void setOrderName(String orderName, String updateUser) {
        if (updateUser != null && updateUser.equals(order.getUserName())) {
            order.setOrderName(orderName, updateUser);
        } else {
            throw new IllegalAccessError(updateUser + "同學(xué),你沒有權(quán)限操作!");
        }
    }

    @Override
    public String getOrderId() {
        return order.getOrderId();
    }

    @Override
    public void setOrderId(String orderId, String updateUser) {
        if (updateUser != null && updateUser.equals(order.getUserName())) {
            order.setOrderId(orderId, updateUser);
        } else {
            throw new IllegalAccessError(updateUser + "同學(xué),你沒有權(quán)限操作!");
        }
    }

    @Override
    public String getOrderTime() {
        return order.getOrderTime();
    }

    @Override
    public void setOrderTime(String orderTime, String updateUser) {
        if (updateUser != null && updateUser.equals(order.getUserName())) {
            order.setOrderTime(orderTime, updateUser);
        } else {
            throw new IllegalAccessError(updateUser + "同學(xué),你沒有權(quán)限操作!");
        }
    }

}

最后寫一個測試類:

public class Client {
    
    public static void main(String[] args) {
        
        Order order = new Order("張三","Mac Pro 訂單","999999","2021/03/12");

        OrderProxy orderProxy = new OrderProxy(order);
        
        orderProxy.setOrderName("iPhone 12 Pro Max 訂單", "張三");
        
        System.out.println("現(xiàn)在的訂單名稱為:"+orderProxy.getOrderName());
        
    }
    
}

通過以上的一個案例會發(fā)現(xiàn)靜態(tài)代理有一個很大的確定,那就是做權(quán)限 保護判斷等邏輯的時候,代理類里的每個函數(shù)都需要手動判斷一次,特別繁瑣導(dǎo)致一堆垃圾代碼,而且如果目標接口發(fā)生改變,被代理類對應(yīng)的函數(shù)也需要改一遍很麻煩,這時候就引入了一個動態(tài)代理:

動態(tài)代理

  • 不需要手動創(chuàng)建代理對象,而是由JVM自動幫我們創(chuàng)建,而且代理對象自動實現(xiàn)了目標接口
  • 他是通過動態(tài)生成class字節(jié)碼做到的


    image.png

具體是怎么做的呢?


image.png

自動生成的代理類對應(yīng)的每個函數(shù)會做這樣一個操作,將當前函數(shù)傳入到InvocationHandler實現(xiàn)類的invoke函數(shù)中,再在InvocationHandler 的 invoke函數(shù)中做統(tǒng)一權(quán)限等判斷操作最后調(diào)用被代理類對應(yīng)的函數(shù)

public class DynamicInvocationHandler implements InvocationHandler {

    private Order order;

    public void setTarget(Order order) {
        this.order = order;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 所有被代理對象的方法被執(zhí)行,都會傳入到invoke方法中
        // 方法名稱只要是set開頭,進行權(quán)限檢查
        if (method.getName().startsWith("set")) {
            if (order != null && order.getUserName() != null && order.getUserName().equals(args[1])) {
                return method.invoke(order, args);
            } else {
                throw new IllegalAccessError("沒有操作權(quán)限!");
            }
        }
        return method.invoke(order, args);
    }

}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容