【Javaエッセンシャル】Javaプロキシモード(静的プロキシ、JDK / Cglib動的プロキシ)

前書き

Java開発者として、プロキシ設計パターンを使用または確認する必要があると思います。SpringのAOPと同様に、これは使用される動的プロキシモードです。MybatisのxxxMapperインターフェイスは、対応するMapperプロキシオブジェクトの生成に使用される動的プロキシでもあります。動的エージェントモデルの重要性。

プロキシモードは通常、静的プロキシと動的プロキシに分けられます。目標は、元のメソッド機能を拡張および拡張することです。

静的プロキシ

  • 抽象オブジェクトの役割(AbstractObject):通常、インターフェイスまたは抽象クラスを使用して、ターゲットオブジェクトとプロキシオブジェクト間の共通インターフェイスを宣言します。これにより、ターゲットオブジェクトを使用できる場所であればどこでもプロキシオブジェクトを使用できます。
  • ターゲットオブジェクトの役割(RealObject):プロキシされる実際の役割。
  • ProxyObjectロール(ProxyObject):ターゲットオブジェクトと同じインターフェイスを実装し、ターゲットオブジェクトへの参照を含み、ターゲットメソッドをカスタマイズおよび拡張できます。

注文インターフェースを定義し、注文メソッドを追加します

public interface IOrderService {
    // 提交订单
    void submitOrder();
}

注文インターフェース実装クラスの記述

public class OrderServiceImpl implements IOrderService{

    // 提交订单测试
    @Override
    public void submitOrder() {
        System.out.println("-------保存订单-------");
    }
}

次に、現在のsubmitOrder()メソッドがニーズを満たせなくなった場合、このメソッドの前後に特別なビジネス処理を行う必要がありますが、元の注文インターフェイス実装クラス(開始と終了の原則)を変更したくないため、オーダープロキシクラスを追加できます。このプロキシクラスは、ターゲットインターフェイスオブジェクトのメンバープロパティを定義します。コンストラクションインジェクションを通じて、ターゲットオブジェクトを操作できます。ターゲットオブジェクトメソッドの前後にカスタムロジックを追加できます。

public class OrderStaticProxy implements IOrderService {

    private IOrderService orderService;

    // 构造注入
    public OrderStaticProxy(IOrderService orderService){
        this.orderService=orderService;
    }

    @Override
    public void submitOrder() {
        System.out.println("--------提交订单前,自定义逻辑");
        orderService.submitOrder();
        System.out.println("--------提交订单前,自定义逻辑");
    }
}

テストカテゴリ:

public class Test {

    public static void main(String[] args) {
        IOrderService orderService = new OrderServiceImpl();
        OrderStaticProxy proxy = new OrderStaticProxy(orderService);
        proxy.submitOrder();
    }
}

出力は次のとおりです。

分析:元のOrderServiceImpl実装クラスを変更せずに、元の注文インターフェイスの機能が拡張および拡張されます

利点:

  • ターゲットオブジェクトを変更せずに、ターゲットオブジェクトの機能を拡張できます。
  • 公共サービスはエージェントによって処理されます。これはより便利です。
  • 開閉の原則と単一性の原則を遵守してください。

短所:

  • プロキシクラスはターゲットオブジェクトと同じインターフェイスを実装する必要があるため、各クラスがプロキシクラスを作成する必要がある場合、クラスが多すぎてメンテナンスが不便になります。
  • インターフェイスにメソッドを追加する場合は、ターゲット実装クラスとプロキシクラスの両方を変更する必要があります。

したがって、動的エージェントにつながります。

動的プロキシ

動的エージェントの役割は、静的エージェントの役割と同じです。

静的プロキシは、ビジネス拡張ごとにプロキシクラスを提供し、プロキシクラスはプロキシオブジェクトを作成します。動的プロキシにはプロキシクラスがありませんが、プロキシオブジェクトはプロキシ生成ツールによって直接動的に生成されます。

動的プロキシのプロキシオブジェクトは実行時に動的に生成されます。静的プロキシのプロキシクラスは事前に作成する必要があります。つまり、プロキシクラスはコンパイル時に決定されます。

動的エージェントは次のように分類されます。

  • インターフェイスベースの動的プロキシ(JDK動的プロキシ)
  • クラスベースの動的プロキシ(cglib)

JDK動的プロキシ

JDK動的プロキシでは、コアはInvocationHandlerインターフェイスProxyクラスです。詳細については、JDKヘルプドキュメントを参照してください。

package java.lang.reflect;

public interface InvocationHandler {

    public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable;
}

その中で、InvocationHandlerinvoke()メソッドを実装する必要があります。パラメーターは次のとおりです。

  • オブジェクトプロキシ:プロキシオブジェクト(基本的に役に立たない)
  • メソッドメソッド:プロキシオブジェクトによって呼び出されるメソッド
  • Object [] args:呼び出されたメソッドのパラメーター

Proxy#newProxyInstance()を介してプロキシクラスオブジェクト生成します

    @CallerSensitive
    public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {
        //。。。。
    }

その中で、メソッドProxyはクラス内の静的メソッドであり、パラメーターは次のとおりです。

  • ClassLoader loader:クラスローダーを使用する現在のターゲットオブジェクトを指定し、ローダーの取得方法を修正しました。
  • Class<?>[] interfaces:ターゲットオブジェクトによって実装されるインターフェイスのタイプ。タイプは、汎用メソッドを使用して確認されます。
  • InvocationHandler h:イベントハンドラー。ターゲットオブジェクトのメソッドが実行されると、イベントハンドラーのinvoke()メソッドがトリガーされ、現在実行されているターゲットオブジェクトのメソッドがパラメーターとして渡されます。

コード

抽象オブジェクトの役割とターゲットオブジェクトの役割は、静的プロキシ(IOrderService-> OrderServiceImpl)と同じです。

実装カスタマイズのInvocationHandler呼び出し()を次のように

public class DynamicProxy implements InvocationHandler {


    private Object target;

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

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        System.out.println("------动态代理,前置增强-----");
        Object invoke = method.invoke(target, args);
        System.out.println("------动态代理,后置增强-----");
        return invoke;
    }
    // 生成动态代理对象
    public static Object getProxy(Object target){
        DynamicProxy proxy = new DynamicProxy(target);
        return Proxy.newProxyInstance(proxy.getClass().getClassLoader(), target.getClass().getInterfaces(),proxy);
    }

}

テストカテゴリ:

/**
 * @description: 测试
 * @author: stwen_gan
 * @date: 
 **/
public class Test {

    public static void main(String[] args) {

        // 为了在项目下生成代理类
        System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");
        
        // 静态代理
//        IOrderService orderService = new OrderServiceImpl();
//        OrderStaticProxy proxy = new OrderStaticProxy(orderService);
//        proxy.submitOrder();

        // 动态代理
        IOrderService orderService = new OrderServiceImpl();
        IOrderService dynamicProxy = (IOrderService) DynamicProxy.getProxy(orderService);
        dynamicProxy.submitOrder();


    }
}

出力:

その中に、次の構成ステートメントを追加します

//この設定は、cg1ib動的プロキシによって生成されたクラスを出力するために使用されます

// System.setProperty(Debuggingclasswriter.DEBUG_LOCATION_PROPERTY、 "D:\\ class");

//この設定は、jdk動的プロキシによって生成されたクラスを出力するために使用されます

System.getProperties()。put( "sun.misc.ProxyGenerator.saveGeneratedFiles"、 "true");

これは現在のディレクトリにあり、JDKはデフォルトでプロキシクラスを生成します。名前はデフォルトで$ Proxy0になり、プロキシクラスを継承します。

コア:一般に特定のタイプのビジネスに対応する動的エージェントは、複数のクラスに対応し、インターフェイスに対応できます。

上記のコードからわかるように、動的プロキシオブジェクトはターゲットオブジェクトインターフェイスを実装する必要はありませんが、ターゲットオブジェクトはインターフェイスを実装する必要があります。そうしないと、JDK動的プロキシを使用できません。

改善:戻りオブジェクトタイプは汎用であり、抽象インターフェイスに実装クラスを含めることはできません。MybatisのxxxMapperインターフェイスと同様に、最下層も動的プロキシを介してマッパープロキシオブジェクトを生成し、SQLをスプライスして実行します。マッパー実装クラスを作成する必要はありません。

Cglibプロキシ

上記の静的プロキシモードとJDK動的プロキシモードはどちらも、インターフェイスを実装するためにターゲットオブジェクトを必要としますが、場合によっては、ターゲットオブジェクトは、インターフェイスを実装せずに単なる独立したクラスである可能性があります。現時点では、ターゲットオブジェクトサブクラスを使用できます。プロキシを実現する方法であるこのプロキシ方式は次のとおりです。Cglibプロキシ、比較的使用頻度が低い。引用

Cglib動的プロキシの原理は、ターゲットクラスのサブクラスを生成することです(つまり、サブクラスオブジェクトはプロキシオブジェクトです)。これは、メモリ内にサブクラスオブジェクトを構築して、ターゲットオブジェクトの機能拡張を実現します

注:Cglib動的プロキシは、インターフェイスが実装されているかどうかに関係なく、インターフェイスがない場合だけでなく、使用できます。

  • JDKの動的プロキシには制限があります。つまり、動的プロキシを使用するターゲットオブジェクトは、少なくとも1つのインターフェイスを実装する必要があります。したがって、インターフェイスを実装しないがプロキシを使用するターゲットオブジェクトの場合は、Cglibプロキシを使用できます。
  • Cglibは、実行時にJavaクラスを拡張し、Javaインターフェイスを実装できる強力な高性能コード生成パッケージです。Spring AOPやsynaopなどの多くのAOPフレームワークで、メソッドinterception(インターセプト)を提供するために広く使用されています
  • Cglibパッケージの最下層は、小さくて高速なバイトコード処理フレームワークASMを使用して、バイトコードを変換し、新しいクラスを生成することです。クラスファイルの形式を含むJVMの内部構造を理解する必要があるため、ASMを直接使用することはお勧めしません。命令セットに精通している。

MethodInterceptor

public interface MethodInterceptor extends Callback {
    Object intercept(Object var1, Method var2, Object[] var3, MethodProxy var4) throws Throwable;
}

Cglibツールクラス。ターゲットメソッドを拡張するには、MethodInterceptor#intercept()実装するだけで済みます。

public class CglibProxy implements MethodInterceptor {

    //维护目标对象
    private Object target;
    public CglibProxy(Object target) {
        this.target = target;
    }

//    //获取目标对象的代理对象
//    public Object getProxy() {
//        //1. 实例化工具类
//        Enhancer en = new Enhancer();
//        //2. 设置父类对象
//        en.setSuperclass(this.target.getClass());
//        //3. 设置回调函数
//        en.setCallback(this);
//        //4. 创建子类,也就是代理对象
//        return en.create();
//    }

    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("-------动态代理,前置增强-----");
        //执行目标对象的方法
        Object object = method.invoke(target, objects);
        System.out.println("-------动态代理,后置增强-----");
        return object;
    }
}

その中で、コメントされたメソッドは、ターゲットクラスのプロキシオブジェクトを取得することです。これを個別に抽出して、cglibプロキシファクトリを追加できます。

/**
 * @description: cglib代理工厂
 * @author: stwen_gan
 * @date: 
 **/
public class CglibProxyFactory {
    //获取目标类的代理对象
    public static <T> T createProxy(final Class<?> targetClass, final MethodInterceptor methodInterceptor) {
        return (T) Enhancer.create(targetClass,methodInterceptor);
    }
}

新しいOrderDaoクラスを追加します。インターフェースを実装する必要はありません。

public class OrderDao {
    
    void submitOrder(){
        System.out.println("--------保存订单-------");
    }
}

テストカテゴリ:

出力:

総括する

静的エージェントと比較すると、JDK動的エージェントはすべてインターフェイス指向のプログラミングであるという点で類似しており、元のコードを変更せずにターゲットメソッドの機能を強化し、動的エージェントは静的エージェントのようにターゲットオブジェクトに1対1で対応するエージェントクラスを記述する必要がありません。エージェントモデルの使用は「開閉原理」に準拠しており、機能責任の分担が明確であり、本来のメソッド機能を拡張・拡張し、後の拡張・保守を容易にすることを目的としています。

史上最強のTomcat8パフォーマンス最適化

なぜアリババは90秒で100億に抵抗できるのですか?-サーバー側の高同時分散アーキテクチャの進化

B2Beコマースプラットフォーム--ChinaPayUnionPay電子決済機能

Zookeeperの分散ロックを学び、インタビュアーに感心してあなたを見てもらいましょう

SpringCloudeコマーススパイクマイクロサービス-Redisson分散ロックソリューション

もっと良い記事をチェックして、公式アカウントを入力してください-私にお願いします-過去に素晴らしい

深くソウルフルなパブリックアカウント0.0

おすすめ

転載: blog.csdn.net/a1036645146/article/details/111881599