自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

代理模式深度解析:業(yè)務(wù)解耦與安全管控實(shí)踐

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

靜態(tài)代理:

就是將一些在方法中重復(fù)的功能提取出來,通過一個(gè)專門的類去封裝,在具體類中需要的時(shí)候就用那個(gè)專門的類的對(duì)象去調(diào)用

動(dòng)態(tài)代理:

原理和靜態(tài)代理差不多,只是用了一個(gè)反射的接口,去調(diào)用一些方法方便進(jìn)行動(dòng)態(tài)調(diào)度

動(dòng)態(tài)代理的步驟就是:

先把代理類實(shí)例化,再實(shí)例化被代理類,將被代理類的對(duì)象傳到代理類的方法中,因?yàn)槭荗bject類型,所以需要轉(zhuǎn)型再輸出那個(gè)方法

圖片圖片

圖片圖片

靜態(tài)代理

抽象角色:一般會(huì)使用接口或抽象類

package com.carl.agent;

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

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

真實(shí)角色:被代理的角色

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("三室一廳,獨(dú)廚獨(dú)衛(wèi),家具齊全!價(jià)格面議");
    }
}

代理角色:代理真實(shí)角色,一般會(huì)做一些附屬操作

package com.carl.agent;


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

使用者:調(diào)用代理角色

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

}

代理的好處

  • 可以使真實(shí)角色的操作更加純粹,不用關(guān)注一些公共的業(yè)務(wù)
  • 公共的業(yè)務(wù)交給代理角色,實(shí)現(xiàn)業(yè)務(wù)的分工
  • 公共業(yè)務(wù)發(fā)生擴(kuò)展的時(shí)候,方便集中管理

缺點(diǎn)

  • 一個(gè)真實(shí)角色就會(huì)產(chǎn)生一個(gè)代理角色,代碼量增加,開發(fā)效率變低

例如:

UserService作為抽象角色,UserServiceImpl作為一個(gè)真實(shí)角色,需要每次訪問該業(yè)務(wù)類中的方式的時(shí)候,都要做權(quán)限判斷,判斷權(quán)限是否滿足要求

這個(gè)時(shí)候使用代理角色,調(diào)用方法時(shí),都統(tǒng)一進(jìn)行權(quán)限管理,這樣權(quán)限判斷的業(yè)務(wù)和查詢User表的業(yè)務(wù)就分離了,互不影響。當(dāng)UserServiceImpl增加一些業(yè)務(wù),也可以通過代理,增加權(quán)限管理的業(yè)務(wù),統(tǒng)一集中管理

動(dòng)態(tài)代理

動(dòng)態(tài)代理和靜態(tài)代理的角色一致,只是通過反射機(jī)制,讓代理變成了動(dòng)態(tài)

動(dòng)態(tài)代理的兩大類【Spring的AOP同時(shí)支持JDK和CGLib動(dòng)態(tài)代理,具體使用哪種代理取決于目標(biāo)對(duì)象的實(shí)現(xiàn)與配置】

  • 基于接口的動(dòng)態(tài)代理(JDK動(dòng)態(tài)代理==SpringBoot 2.x默認(rèn))
  • 基于類的動(dòng)態(tài)代理(CGlib動(dòng)態(tài)代理==SpringBoot 3.x默認(rèn))

JDK動(dòng)態(tài)代理

通過java.lang.reflect.Proxy類生成代理對(duì)象,要求被代理類必須實(shí)現(xiàn)至少一個(gè)接口。代理對(duì)象會(huì)實(shí)現(xiàn)與原類相同的接口。

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

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

//-----以上都是正常接口實(shí)現(xiàn)------

//-----以下則是實(shí)現(xiàn)動(dòng)態(tài)代理的地方 ------

// InvocationHandler實(shí)現(xiàn)
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("[安全校驗(yàn)] 開始驗(yàn)證身份...");
        Object result = method.invoke(target, args);
        System.out.println("[安全校驗(yàn)] 交易完成記錄日志");
        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); // 代理方法調(diào)用
    }
}

執(zhí)行結(jié)果:

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

Proxy類

Proxy提供了創(chuàng)建動(dòng)態(tài)代理類和實(shí)例的靜態(tài)方法(newProxyInstance())

常用方法

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

JDK提供了兩種方式獲取動(dòng)態(tài)代理類的實(shí)例:

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 {
    /*
     * 第一個(gè)參數(shù):this.getClass().getClassLoader(),使用handler對(duì)象的classLoader加載代理對(duì)象
     * 第二個(gè)參數(shù):handler.getClass().getInterfaces(),為代理類提供真實(shí)對(duì)象的接口,便于調(diào)用接口中的所有方法
     * 第三個(gè)參數(shù):this,InvocationHandler對(duì)象
     */
    return Proxy.newProxyInstance(this.getClass().getClassLoader(), handler.getClass().getInterfaces(),this);
}

InvocationHandler類

InvocationHandler類是被代理實(shí)例調(diào)用處理程序的接口【代理邏輯處理器,實(shí)現(xiàn)invoke()方法增強(qiáng)邏輯】

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

  • proxy:代理類代理的真實(shí)對(duì)象
  • method:通過反射獲取到的需要調(diào)用某個(gè)對(duì)象的方法
  • args:指代代理對(duì)象方法的傳遞參數(shù)

在invoke對(duì)象中,調(diào)用被代理對(duì)象的處理程序,并可以進(jìn)行增強(qiáng),具體實(shí)現(xiàn)如下:

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

實(shí)現(xiàn)步驟

抽象被代理角色:

package com.carl.proxy.dynamic;

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

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

真實(shí)被代理角色:

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("三室一廳,獨(dú)廚獨(dú)衛(wèi),家具齊全!價(jià)格面議");
    }
}

代理角色:

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 動(dòng)態(tài)代理--必須實(shí)現(xiàn)InvocationHandler接口
 */
public class DynamicProxy implements InvocationHandler {
        /**
         * 被代理對(duì)象
         */
        private RentHouse rentHouse;

        public void setRentHouse(RentHouse rentHouse) {
                this.rentHouse = rentHouse;
        }
        /**
         * 被代理對(duì)象的處理程序
         * @param proxy 代理對(duì)象
         * @param method 方法
         * @param args 參數(shù)
         * @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("中介收中介費(fèi)!");
        }
}
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 通用代理調(diào)用處理程序
 */
public class ProxyInvocationHandler implements InvocationHandler {
    /**
     * @see Object
     * 被代理對(duì)象
     */
    private Object handler;

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

    /**
     * @param proxy 代理類
     * @param method 方法
     * @param args 參數(shù)
     * @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動(dòng)態(tài)代理

通過操作字節(jié)碼生成被代理類的子類,不需要實(shí)現(xiàn)接口。需要引入第三方庫CGLib

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

測(cè)試

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

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

執(zhí)行結(jié)果:

[日志] 方法調(diào)用: pay
微信支付: 200元
[日志] 方法執(zhí)行完成

注意事項(xiàng):

  • CGLib無法代理final修飾的最終類
  • CGlib需要默認(rèn)構(gòu)造函數(shù)【空參構(gòu)造器】
  • 匿名類隱式依賴外部類實(shí)例,導(dǎo)致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();

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

該用其他字節(jié)碼庫【如:ByteBuddy】

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

總結(jié)

針對(duì)一些開發(fā)場(chǎng)景,如何選擇使用JDK動(dòng)態(tài)代理還是CGLib動(dòng)態(tài)代理?

  1. 優(yōu)先考慮JDK動(dòng)態(tài)代理,確保符合面向接口編程原則
  2. 實(shí)在是無需使用接口的使用CGLib
  3. 高頻調(diào)用的方法建議使用CGLib【性能要求高的場(chǎng)景】
  4. 注意CGLib的類初始化問題
責(zé)任編輯:武曉燕 來源: 愛編程的杰尼龜
相關(guān)推薦

2022-07-13 08:36:57

MQ架構(gòu)設(shè)計(jì)模式

2020-04-07 11:23:54

IT治理IT管控業(yè)務(wù)管控

2022-07-05 09:48:25

DevSecOps工商銀行安全管控

2025-02-11 07:55:45

2019-12-20 07:28:45

Docker容器安全云計(jì)算

2023-03-03 08:12:07

設(shè)計(jì)模式語言

2025-03-27 04:10:00

2020-10-16 18:41:43

command設(shè)計(jì)模式代碼

2022-09-02 08:23:12

軟件開發(fā)解耦架構(gòu)

2025-04-15 08:10:56

2025-02-11 08:28:52

2021-01-07 10:30:23

設(shè)計(jì)模式

2024-09-19 08:49:13

2024-08-30 09:53:17

Java 8編程集成

2022-03-21 14:20:15

安全警報(bào)過載網(wǎng)絡(luò)安全

2018-06-14 21:47:46

WOT沈劍58速運(yùn)

2013-11-01 10:34:35

SAP

2013-09-16 10:19:08

htmlcssJavaScript

2016-11-30 15:30:42

架構(gòu)工具和方案

2023-02-28 09:10:28

設(shè)計(jì)模式解耦數(shù)據(jù)
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)