成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

代理模式深度解析:業務解耦與安全管控實踐

開發 前端
UserService作為抽象角色,UserServiceImpl作為一個真實角色,需要每次訪問該業務類中的方式的時候,都要做權限判斷,判斷權限是否滿足要求。
  • 代理模式:為另一個對象提供一個替身或占位符,以便于控制對這個對象的訪問

靜態代理:

就是將一些在方法中重復的功能提取出來,通過一個專門的類去封裝,在具體類中需要的時候就用那個專門的類的對象去調用

動態代理:

原理和靜態代理差不多,只是用了一個反射的接口,去調用一些方法方便進行動態調度

動態代理的步驟就是:

先把代理類實例化,再實例化被代理類,將被代理類的對象傳到代理類的方法中,因為是Object類型,所以需要轉型再輸出那個方法

圖片圖片

圖片圖片

靜態代理

抽象角色:一般會使用接口或抽象類

package com.carl.agent;

/**
 * @Version 1.0.0
 * @author carl蔡先生
 * @Date 2022/10/03
 * @Description 租房
 */
public interface RentHouse {

    /**
     * 租房流程
     */
    void rent();
}

真實角色:被代理的角色

package com.carl.agent;

/**
 * @Version 1.0.0
 *
 * @author carl蔡先生
 * @Date 2022/10/03
 * @Description 房東--要租房
 */
public class Landlord implements RentHouse {
    @Override
    public void rent() {
        System.out.println("三室一廳,獨廚獨衛,家具齊全!價格面議");
    }
}

代理角色:代理真實角色,一般會做一些附屬操作

package com.carl.agent;


/**
 * @Version 1.0.0
 *
 * @author carl蔡先生
 * @Date 2022/10/03
 * @Description 靜態代理
 */
public class StaticProxy {
    private RentHouse rentHouse;
    public StaticProxy(){
    }
    public StaticProxy(RentHouse rentHouse){
        this.rentHouse = rentHouse;
    }
    public void rent(){
        rentHouse.rent();
        System.out.println("中介費用為房價的一半!");
    }
}

使用者:調用代理角色

@Test
public void testStaticAgent(){
    //租房直接找房東
    RentHouse rentHouse=new Landlord();
    //辦理租房流程
    rentHouse.rent();
    //找中介租房子
    StaticProxy staticProxy=new StaticProxy(rentHouse);
    //中介給你辦理租房流程
    staticProxy.rent();

}

代理的好處

  • 可以使真實角色的操作更加純粹,不用關注一些公共的業務
  • 公共的業務交給代理角色,實現業務的分工
  • 公共業務發生擴展的時候,方便集中管理

缺點

  • 一個真實角色就會產生一個代理角色,代碼量增加,開發效率變低

例如:

UserService作為抽象角色,UserServiceImpl作為一個真實角色,需要每次訪問該業務類中的方式的時候,都要做權限判斷,判斷權限是否滿足要求

這個時候使用代理角色,調用方法時,都統一進行權限管理,這樣權限判斷的業務和查詢User表的業務就分離了,互不影響。當UserServiceImpl增加一些業務,也可以通過代理,增加權限管理的業務,統一集中管理

動態代理

動態代理和靜態代理的角色一致,只是通過反射機制,讓代理變成了動態

動態代理的兩大類【Spring的AOP同時支持JDK和CGLib動態代理,具體使用哪種代理取決于目標對象的實現與配置】

  • 基于接口的動態代理(JDK動態代理==SpringBoot 2.x默認)
  • 基于類的動態代理(CGlib動態代理==SpringBoot 3.x默認)

JDK動態代理

通過java.lang.reflect.Proxy類生成代理對象,要求被代理類必須實現至少一個接口。代理對象會實現與原類相同的接口。

// 接口
public interface PaymentService {
    void pay(int amount);
}

// 實現類
public class AlipayService implements PaymentService {
    @Override
    public void pay(int amount) {
        System.out.println("支付寶支付: " + amount + "元");
    }
}

//-----以上都是正常接口實現------

//-----以下則是實現動態代理的地方 ------

// InvocationHandler實現
public class SecurityHandler implements InvocationHandler {
    private final Object target;

    public SecurityHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("[安全校驗] 開始驗證身份...");
        Object result = method.invoke(target, args);
        System.out.println("[安全校驗] 交易完成記錄日志");
        return result;
    }
}

// 使用代理
public class JdkProxyDemo {
    public static void main(String[] args) {
        PaymentService realService = new AlipayService();
        PaymentService proxy = (PaymentService) Proxy.newProxyInstance(
                PaymentService.class.getClassLoader(),
                new Class[]{PaymentService.class},
                new SecurityHandler(realService)
        );
        proxy.pay(100); // 代理方法調用
    }
}

執行結果:

[安全校驗] 開始驗證身份...
支付寶支付: 100元
[安全校驗] 交易完成記錄日志

Proxy類

Proxy提供了創建動態代理類和實例的靜態方法(newProxyInstance())

常用方法

  • getInvocationHandler(Object proxy):返回指定代理實例的調用處理程序
  • getProxyClass(ClassLoader loader,類<?>...interfaces):給出類加載器和接口的代理類
  • isProxyClass():如果當且僅當使用getProxyClass方法或newProxyInstance方法將指定的類動態生成代理類時,返回true
  • newProxyInstance(ClassLoader loader,類<?>[] interfaces,InvocationHandler h):返回指定接口的代理類的實例,該接口將方法調用分派給指定的調用處理程序

JDK提供了兩種方式獲取動態代理類的實例:

InvocationHandler handler = new MyInvocationHandler(...);
Class<?> proxyClass = Proxy.getProxyClass(Foo.class.getClassLoader(), Foo.class);
Foo f = (Foo) proxyClass.getConstructor(InvocationHandler.class).newInstance(handler);
/**
 * 獲取代理
 * @return {@link Object }
 */
public Object getProxy() throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
    /*
     * 第一個參數:this.getClass().getClassLoader(),使用handler對象的classLoader加載代理對象
     * 第二個參數:handler.getClass().getInterfaces(),為代理類提供真實對象的接口,便于調用接口中的所有方法
     * 第三個參數:this,InvocationHandler對象
     */
    return Proxy.newProxyInstance(this.getClass().getClassLoader(), handler.getClass().getInterfaces(),this);
}

InvocationHandler類

InvocationHandler類是被代理實例調用處理程序的接口【代理邏輯處理器,實現invoke()方法增強邏輯】

Object invoke(Object proxy, Method method, Object[] args):通過反射獲取被代理類的示例

  • proxy:代理類代理的真實對象
  • method:通過反射獲取到的需要調用某個對象的方法
  • args:指代代理對象方法的傳遞參數

在invoke對象中,調用被代理對象的處理程序,并可以進行增強,具體實現如下:

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    Object result = method.invoke(handler, args);
    return result;
}

實現步驟

抽象被代理角色:

package com.carl.proxy.dynamic;

/**
 * @Version 1.0.0
 *
 * @author carl蔡先生
 * @Date 2022/10/03
 * @Description 租房
 */
public interface RentHouse {

    /**
     * 租金
     */
    void rent();
}

真實被代理角色:

package com.carl.proxy.dynamic;

/**
 * @Version 1.0.0
 *
 * @author carl蔡先生
 * @Date 2022/10/03
 * @Description 房東--要租房
 */
public class Landlord implements RentHouse {
    @Override
    public void rent() {
        System.out.println("三室一廳,獨廚獨衛,家具齊全!價格面議");
    }
}

代理角色:

package com.carl.proxy.dynamic;

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

/**
 * @Version 1.0.0
 * @see InvocationHandler
 * @author carl蔡先生
 * @Date 2022/10/03
 * @Description 動態代理--必須實現InvocationHandler接口
 */
public class DynamicProxy implements InvocationHandler {
        /**
         * 被代理對象
         */
        private RentHouse rentHouse;

        public void setRentHouse(RentHouse rentHouse) {
                this.rentHouse = rentHouse;
        }
        /**
         * 被代理對象的處理程序
         * @param proxy 代理對象
         * @param method 方法
         * @param args 參數
         * @throws Throwable 拋出最高異常
         * @return {@link Object }
         */
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                //前后增加操作
                lookHouse();
                Object result = method.invoke(rentHouse, args);
                fare();
                return result;
        }

        /**
         * 獲取代理類
         * @return {@link Object }
         */
        public Object getProxy(){
                return Proxy.newProxyInstance(this.getClass().getClassLoader(), rentHouse.getClass().getInterfaces(),this);
        }

        public void lookHouse(){
                System.out.println("中介帶看房源!");
        }
        public void fare(){
                System.out.println("中介收中介費!");
        }
}
package com.carl.proxy.util;

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

/**
 * @Version 1.0.0
 * @see InvocationHandler
 * @author carl蔡先生
 * @Date 2022/10/04
 * @Description 通用代理調用處理程序
 */
public class ProxyInvocationHandler implements InvocationHandler {
    /**
     * @see Object
     * 被代理對象
     */
    private Object handler;

    public ProxyInvocationHandler() {
    }
    public ProxyInvocationHandler(Object handler) {
        this.handler = handler;
    }
    /**
     * 獲取代理
     * @return {@link Object }
     */
    public Object getProxy() throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        /*
         * 第一個參數:this.getClass().getClassLoader(),使用handler對象的classLoader加載代理對象
         * 第二個參數:handler.getClass().getInterfaces(),為代理類提供真實對象的接口,便于調用接口中的所有方法
         * 第三個參數:this,InvocationHandler對象
         */
        return Proxy.newProxyInstance(this.getClass().getClassLoader(), handler.getClass().getInterfaces(),this);
    }

    /**
     * @param proxy 代理類
     * @param method 方法
     * @param args 參數
     * @throws Throwable 拋出異常
     * @return {@link Object }
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object result = method.invoke(handler, args);
        return result;
    }
}

CGLib動態代理

通過操作字節碼生成被代理類的子類,不需要實現接口。需要引入第三方庫CGLib

  • 引入maven依賴
<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.3.0</version>
</dependency>
  • 實現被代理類
// 被代理類(無需實現接口)
public class WechatPayService {
    public void pay(int amount) {
        System.out.println("微信支付: " + amount + "元");
    }
}
  • 實現MethodInterceptor接口
// MethodInterceptor實現
public class LogInterceptor implements MethodInterceptor {
    @Override
    public Object intercept(Object obj, Method method, Object[] args, 
                           MethodProxy proxy) throws Throwable {
        System.out.println("[日志] 方法調用: " + method.getName());
        Object result = proxy.invokeSuper(obj, args); // 調用父類方法
        System.out.println("[日志] 方法執行完成");
        return result;
    }
}

測試

// 使用代理
public class CglibProxyDemo {
    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(WechatPayService.class); // 設置父類
        enhancer.setCallback(new LogInterceptor());     // 設置回調

        WechatPayService proxy = (WechatPayService) enhancer.create();
        proxy.pay(200); // 代理方法調用
    }
}

執行結果:

[日志] 方法調用: pay
微信支付: 200元
[日志] 方法執行完成

注意事項:

  • CGLib無法代理final修飾的最終類
  • CGlib需要默認構造函數【空參構造器】
  • 匿名類隱式依賴外部類實例,導致CGLib無法生成代理
new ByteBuddy()
  .subclass(type)
  .method(any()).intercept(MethodDelegation.to(new Object() {
     @RuntimeType
     public Object intercept(@SuperCall Callable<?> c, 
                             @Origin Method m, 
                             @AllArguments Object[] a) throws Exception {
       // implement your interception logic
     }
   }).make();

指定構造器參數。enhancer.create(new Class<?>[] {type.getEnclosingClass()}, new Object[] {null})傳遞 null 作為外部類實例(需確保邏輯安全)

該用其他字節碼庫【如:ByteBuddy】

  • CGLib存在首次生成代理類時加載過慢問題【需要操作字節碼,可以在啟動階段提前加載代理類】
  • MethodProxy.invokeSuper()代替Method.invoke()。可以減少反射開銷。

總結

針對一些開發場景,如何選擇使用JDK動態代理還是CGLib動態代理?

  1. 優先考慮JDK動態代理,確保符合面向接口編程原則
  2. 實在是無需使用接口的使用CGLib
  3. 高頻調用的方法建議使用CGLib【性能要求高的場景】
  4. 注意CGLib的類初始化問題
責任編輯:武曉燕 來源: 愛編程的杰尼龜
相關推薦

2022-07-13 08:36:57

MQ架構設計模式

2022-07-05 09:48:25

DevSecOps工商銀行安全管控

2025-02-11 07:55:45

2020-04-07 11:23:54

IT治理IT管控業務管控

2023-03-03 08:12:07

設計模式語言

2019-12-20 07:28:45

Docker容器安全云計算

2025-03-27 04:10:00

2020-10-16 18:41:43

command設計模式代碼

2025-04-15 08:10:56

2022-09-02 08:23:12

軟件開發解耦架構

2024-09-19 08:49:13

2025-02-11 08:28:52

2021-01-07 10:30:23

設計模式

2024-08-30 09:53:17

Java 8編程集成

2022-03-21 14:20:15

安全警報過載網絡安全

2018-06-14 21:47:46

WOT沈劍58速運

2011-11-21 12:56:10

Java代理模式設計模式

2023-02-28 09:10:28

設計模式解耦數據

2013-11-01 10:34:35

SAP

2023-12-04 16:18:30

點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 99在线精品视频 | 一级片在线播放 | 综合精品久久久 | 一级片在线观看 | 自拍 亚洲 欧美 老师 丝袜 | 青青激情网 | 欧美日韩一本 | 久久99精品久久久久婷婷 | 伊人春色在线 | 国产成人精品免费 | 国产精品久久久久久久久久久免费看 | 日本成人综合 | 看a级黄色毛片 | 久久99深爱久久99精品 | 一区二区三区在线免费观看 | 久久久久久综合 | 精品成人一区二区 | 欧美在线视频一区二区 | 午夜国产在线 | 一区二区免费看 | 久久久国产一区二区三区四区小说 | 国产日韩精品在线 | 日韩久久久久久 | 欧美日韩综合一区 | 一区二区三区在线电影 | 欧美天堂 | 国产精品久久久久久婷婷天堂 | 一区二区国产精品 | 欧美久久一区二区三区 | 午夜免费网站 | 亚洲三级av | 日韩在线精品视频 | 亚洲专区在线 | 中国一级特黄真人毛片免费观看 | 日韩另类视频 | www.天天操.com | 国产区在线看 | 久久久久久久久国产成人免费 | 99精品欧美一区二区三区综合在线 | 久久精品中文字幕 | 国产一区久久久 |