プロキシモード (15)

自分を信じてください、自分を信じてください

前の章でフライウェイト モード (14) について簡単に紹介しました。まだ読んでいない場合は、前の章をご覧ください。

1.プロキシモード

ルーキー チュートリアルのプロキシ パターンの紹介を参照してください: https://www.runoob.com/design-pattern/proxy-pattern.html

プロキシ パターンでは、あるクラスが別のクラスの機能を表します。このタイプのデザインパターンは構造パターンです。

プロキシ パターンでは、外部への機能インターフェイスを提供するために、既存のオブジェクトを使用してオブジェクトを作成します。

1.1 はじめに

目的:このオブジェクトへのアクセスを制御するために、他のオブジェクトにプロキシを提供すること。

主に解決するのは、オブジェクトへの直接アクセスによって引き起こされる問題です。たとえば、アクセスされるオブジェクトがリモート マシン上にある場合などです。オブジェクト指向システムでは、何らかの理由(オブジェクトの作成に多額のコストがかかる、特定の操作にセキュリティ制御が必要、プロセス外のアクセスが必要など)により、直接アクセスするとユーザーやシステム構造に多大な問題が発生します。このオブジェクトにアクセスするときに、このオブジェクトにアクセス レイヤを追加できます。

使用する場合:クラスへのアクセスを制御したい場合。

解決方法:中間層を増やす。

キーコード:実装と委任クラスの組み合わせ。

応用例: 1. Windows のショートカット。2. 朱八傑は高翠嵐を探しに行きましたが、孫悟空がそれを変更しました。抽象的な高翠嵐の外見、高翠蘭自身と孫悟空がこのインターフェイスを実現した、というように理解できます。プロキシ クラス。3. 鉄道の切符を購入するには、駅で購入する必要はなく、営業所に行っても購入できます。4. 小切手または銀行預金証明書は、口座内の資金の代理となります。小切手は市場取引で現金の代わりに使用され、発行者の口座内の資金を管理します。5.スプリングオープ。

利点: 1. 責任が明確です。2. 高い拡張性。3. インテリジェント。

欠点: 1. クライアントと実際のサブジェクトの間にプロキシ オブジェクトが追加されるため、一部の種類のプロキシ モードではリクエストの処理速度が遅くなる可能性があります。2. プロキシ モードの実装には追加の作業が必要で、一部のプロキシ モードの実装は非常に複雑です。

使用シナリオ:責任ごとに分けると、通常、次の使用シナリオがあります。 1. リモート エージェント。2. 仮想エージェント。3. コピーオンライトエージェント。4. エージェントを保護またはアクセスします。5. キャッシュエージェント。6. ファイアウォールエージェント。7. 同期エージェント。8. スマートリファレンスエージェント。

注: 1. アダプター モードとの違い: アダプター モードは主に対象のオブジェクトのインターフェイスを変更しますが、プロキシ モードはプロキシされたクラスのインターフェイスを変更できません。2. デコレータモードとの違い: デコレータモードは機能拡張、プロキシモードは制御です。

ここに画像の説明を挿入

2.プロキシモード

静的プロキシ、動的プロキシ (JDK プロキシ)、Cglib プロキシ (メモリ内にオブジェクトを動的に作成できる) があります。

2. 静的プロキシ

2.1.1 教師の講義インターフェース

public interface ITeacher {
    
    

    public void talk();
}

2.1.2 教師の講義のインターフェース実現

@Slf4j
public class RealTeacher implements ITeacher{
    
    

    @Override
    public void talk() {
    
    
        log.info("老师正在讲课");
    }
}

2.1.3 プロキシを使用しない

 @Test
    public void noProxyTest() {
    
    
        ITeacher iTeacher = new RealTeacher();
        iTeacher.talk();
    }

情報 [メイン] (RealTeacher.java:16) - 教師が講義を行っています

2.1.4 プロキシの使用

@Slf4j
public class TeacherProxy implements ITeacher{
    
    
    private ITeacher iTeacher ;

    public TeacherProxy (ITeacher iTeacher) {
    
    
        this.iTeacher = iTeacher;
    }

    @Override
    public void talk() {
    
    
        log.info("老师提前备课--> 用户 prepare 操作");
        iTeacher.talk();
        log.info("老师课后总结--> 用户 after 操作");
    }

}

2.1.5 代理テスト方法

@Test
    public void oneTest() {
    
    
        RealTeacher realTeacher = new RealTeacher();
        TeacherProxy teacherProxy = new TeacherProxy(realTeacher);
        teacherProxy.talk();
    }

画像-20230615145810997

2.2 動的プロキシ

2.2.1 インターフェース

public interface ITeacher2 {
    
    

    public void talk();

    public void hello (String name);
}

2.2.2 インターフェースの実装

@Slf4j
public class RealTeacher2 implements ITeacher2{
    
    

    @Override
    public void talk() {
    
    
        log.info("老师正在讲课");
    }

    @Override
    public void hello(String name) {
    
    
        log.info("你好啊 {}", name);
    }
}

2.2.3 JDKプロキシの実装

@Slf4j
public class TeacherProxy2 {
    
    

    private Object target ;

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

    /**
     获取动态代理
     */
    public Object getProxyInstance() {
    
    
        return Proxy.newProxyInstance(
                target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                new InvocationHandler() {
    
    
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    
    
                        log.info("JDK代理开始");

                        Object returnVal = method.invoke(target, args);

                        log.info("JDK代理结束");
                        return returnVal;
                    }
                }
        );
    }

}

2.2.4 プロキシテスト

   @Test
    public void twoTest() {
    
    
        ITeacher2 teacher2 = new RealTeacher2();
        // 处理转换代理信息
        ITeacher2 target = (ITeacher2)(new TeacherProxy2(teacher2).getProxyInstance());
        // 进行处理
        target.hello("张三");
    }

画像-20230615150102593

2.3 Cglib プロキシ

2. 3. 1 つの共通クラス

@Slf4j
public class RealTeacher3 {
    
    

    public void talk() {
    
    
        log.info("老师正在讲课");
    }

    public void hello(String name) {
    
    
        log.info("你好啊 {}", name);
    }
}

2.3.2 このクラスのプロキシ TeacherProxy3

@Slf4j
public class TeacherProxy3 implements MethodInterceptor {
    
    
    private Object target;
    public TeacherProxy3(Object target) {
    
    
        this.target = target;
    }
    
    public Object getProxyInstance () {
    
    
        // 创建工具类 Enhancer
        Enhancer enhancer = new Enhancer();
        // 设置父类
        enhancer.setSuperclass(target.getClass());
        // 设置回调函数, 主要是这个。
        enhancer.setCallback(this);
        // 创建子类对象,即代理对象
        return enhancer.create();
    }
    
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
    
    
        log.info("CGLIB 代理开始");

        Object returnVal = methodProxy.invoke(target, objects);

        log.info("CGLIB 代理结束");
        return returnVal;
    }

}

2.3.3 プロキシテスト

@Test
    public void threeTest() {
    
    
        RealTeacher3 realTeacher3 = new RealTeacher3();
        // 处理转换代理信息
        RealTeacher3 target = (RealTeacher3)new TeacherProxy3(realTeacher3).getProxyInstance();
        // 进行处理
        target.hello("张三");
    }

画像-20230615150340055

3. HutoolのJDKエージェント

プロキシにはhutoolのProxyUtil ProxyFactoryを使用できます。

@Test
    public void fourTest() {
    
    
        ITeacher2 teacher2 = new RealTeacher2();

        ITeacher2 proxy = ProxyUtil.proxy(teacher2, new SimpleAspect());

        proxy.hello("hutool 工具类的代理");

        ITeacher2 proxy2 = ProxyFactory.createProxy(teacher2, new Aspect() {
    
    
            @Override
            public boolean before(Object target, Method method, Object[] args) {
    
    
                log.info(">>>> 代理之前执行的操作");
                return true;
            }

            @Override
            public boolean after(Object target, Method method, Object[] args, Object returnVal) {
    
    
                log.info(">>>> 代理之后执行的操作");
                return true;
            }

            @Override
            public boolean afterException(Object target, Method method, Object[] args, Throwable e) {
    
    
                log.info(">>>> 异常时执行的操作");
                return true;
            }
        });
        proxy2.hello("hutool 工厂的代理");
    }

画像-20230615150904736

CGLIB で作成された動的プロキシ オブジェクトは、JDK で作成された動的プロキシ オブジェクトよりもパフォーマンスが高くなりますが、CGLIB はプロキシ オブジェクトの作成に JDK よりもはるかに時間がかかります。

したがって、シングルトン オブジェクトの場合は、頻繁にオブジェクトを作成する必要がないため、CGLIB を使用するのが適切であり、それ以外の場合は、JDK メソッドを使用するのが適切です。

同時に、CGLib は動的にサブクラスを作成する方法を使用するため、最終的に変更されたメソッドをプロキシすることはできません。


この章のコードは github にあります。


https://github.com/yuejianli/DesignPattern/tree/develop/Proxy


ご覧いただきありがとうございます、気に入ったらフォローしてください、またよろしくお願いします!

おすすめ

転載: blog.csdn.net/yjltx1234csdn/article/details/131228558