Java23デザインパターンのエージェントモードの学習

ソースリンク(Giteeコードクラウド):https
//gitee.com/oldou/javadesignpatternsこれは私がコンパイルしたJava23デザインパターンとブログチュートリアルのソースコードです。ブログチュートリアルでは、Java23デザインパターンのさまざまな実装を紹介しています。そして、アプリケーションのシナリオは、私たちのデザイン思考を学び、改善するのに非常に適しています。それがすべての人に役立つ場合は、スターを付けて、作者に精神的なサポートを与えることを忘れないでください。あなたのスターは、私がより良いブログを書く動機です。ありがとうございます。

プロキシパターン

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

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

コアロール:

  • プロキシを介して、オブジェクトへのアクセスを制御します。
  • 特定のオブジェクト(特定のタイプ)にアクセスするメソッドを詳細に制御し、このメソッドを呼び出す前に前処理を実行し、このメソッドを呼び出した後に後処理を実行できます。(つまり、AOPのマイクロ実現です!)
  • エージェントクラスで処理される統合プロセスコードを実装します。

AOP(Aspect Oriented Programming)アスペクト指向プログラミング)コア実装メカニズム!

コアの役割

  • 抽象的役割
    -エージェントの役割と実際の役割を定義するためのパブリック外部メソッド

  • 実際の役割
    -抽象的な役割を実装するには、エージェントの役割が呼び出すために、実際の役割によって実装されるビジネスロジックを定義します。
    –実際のビジネスロジックに注意してください。

  • エージェントの役割
    -抽象的な役割を実現するために、それは実際の役割のエージェントであり、抽象的な方法は実際の役割のビジネスロジックメソッドによって実現され、独自の操作をアタッチできます。
    –統合されたプロセス制御をエージェントの役割に置きます

アプリケーションシナリオ

  • セキュリティプロキシ:実際の役割への直接アクセスを保護します。
  • リモートエージェント:エージェントクラスを介してリモートメソッド呼び出し(RMI)を処理します
  • レイジーロード:最初に軽量プロキシオブジェクトをロードしてから、実際のオブジェクトをロードします。
  • たとえば、大きなドキュメント表示ソフトウェアを開発する場合、大きなドキュメントに大きな画像が含まれます。画像が100MBになる可能性があります。ファイルを開くときにすべての画像を表示することはできないため、画像を表示する必要があるときにプロキシモードを使用できます。の場合、プロキシを使用して全体像を開きます。

分類

(1)静的プロキシ(プロキシクラスを静的に定義)

(2)動的プロキシ(動的に生成されたプロキシクラス)

  • JDKに付属の動的プロキシ
  • Javaassistバイトコード操作ライブラリは動的プロキシを実装します
  • CGLIB動的エージェント
  • ASM(基本的な指示、不十分な保守性)

この記事では、静的プロキシモードとJDK動的プロキシモードの使用についてのみ紹介します。

場合

たとえば、私たちは生活の中で賃貸を例にとります。私たちは通常、家を借りるときに仲介者を見つけ、仲介者は代理人です。彼は家主を代表して家を借りるのを手伝います。家主は借りた家を提供するだけでよく、他にはありません。家主と仲介業者は最終的に家を貸し出しているので、共通のインターフェースを持っています。コードを使用して実装しましょう。
ここに写真の説明を挿入

静的プロキシモード

レンタルインターフェースを提供する:

/**
 * 租房
 */
public interface Rent {
    
    
    public void rent();//租房这个事情
}

本当の役割:地主

/**
 * 真实的角色:房东
 * 需要将房子租出去
 */
public class FangD implements Rent {
    
    
    @Override
    public void rent() {
    
    
        System.out.println("出租XXX房,两室一厅....");
    }
}

代理店の役割:仲介

/**
 * 代理角色:中介
 * 需要代理房东去租房,因此也要实现租房的方法
 */
public class Proxy implements Rent {
    
    
    private FangD fd;
    //提供一个房东的有参构造
    public Proxy(FangD fd) {
    
    
        this.fd = fd;
    }
    public Proxy(){
    
    }

    @Override
    public void rent() {
    
    
        seeHouse(); //中介这里扩展的操作  看房
        fd.rent();//调用房东的租房方法
        hetong();//签合同
        fare();//收取中介费
    }

    //看房
    public void seeHouse(){
    
    
        System.out.println("中介带你看房..");
    }
    //收中介费
    public void fare(){
    
    
        System.out.println("中介收你中介费100元..");
    }
    //签合同
    public void hetong(){
    
    
        System.out.println("签合同...");
    }
}

クライアント:借受人

/** 静态代理模式
 * 真实的人:需要租房的人--客户
 */
public class Client {
    
    

    public static void main(String[] args) {
    
    
        //房东要出租房子
        FangD fd = new FangD();

        //代理,中介要帮房东出租房子,并且中介还可以做一些附属操作
        Proxy proxy = new Proxy(fd);

        //不用找房东,直接找中介租房
        proxy.rent();
    }

}

実行出力:
ここに写真の説明を挿入

ビジネスにおける静的プロキシモードの実現

通常プロキシモードを使用する場合は、業務にログを追加する機能を追加する必要があり、社内のソースコードを変更するのはタブーなので、以下に示すように、ログをプロキシモードに追加して水平方向に展開する必要があります。 :

ビジネスインターフェース:

public interface UserService {
    
    
    void add();
    void delete();
    void update();
    void query();
}

ビジネスインターフェイスの実装クラス:

public class UserServiceImpl implements UserService {
    
    
    @Override
    public void add() {
    
    
        System.out.println("增加了一个用户");
    }

    @Override
    public void delete() {
    
    
        System.out.println("删除了一个用户");
    }

    @Override
    public void update() {
    
    
        System.out.println("修改了一个用户");
    }

    @Override
    public void query() {
    
    
        System.out.println("查询了一个用户");
    }
}

ビジネスエージェント(ログ機能の向上を実現するため):

/**
 * 代理类,我们在此添加一个日志的功能
 */
public class UserServiceProxy implements UserService {
    
    
    private UserServiceImpl userService;
    public void setUserService(UserServiceImpl userService) {
    
    
        this.userService = userService;
    }
    @Override
    public void add() {
    
    
        log("add");
        userService.add();
    }
    @Override
    public void delete() {
    
    
        log("delete");
        userService.delete();
    }
    @Override
    public void update() {
    
    
        log("update");
        userService.update();
    }
    @Override
    public void query() {
    
    
        log("query");
        userService.query();
    }
    public void log(String msg){
    
    
        System.out.println("[Debug]-->使用了"+msg+"方法");
    }

}

テスト:

public class Client {
    
    
    public static void main(String[] args) {
    
    
        UserServiceImpl userService = new UserServiceImpl();

        UserServiceProxy proxy = new UserServiceProxy();
		
        proxy.setUserService(userService);

        proxy.add();
    }

}

出力:
ここに写真の説明を挿入

静的プロキシの利点

  • 家主は家を借りるエージェントを見つけるだけでよく、他の操作はエージェントに任されます
  • これにより、一部の公共サービスに注意を払うことなく、実際の役割の操作をより純粋にすることができます。
  • 仕事の分担を実現するために、公共事業を代理人の役割に引き継ぐ
  • 公共事業拡大時の便利な集中管理
  • 元の事業を変更することなく、プロジェクトにいくつかの機能を拡張します。代理店モデルは非常に適しており、水平開発と同等です。
    ここに写真の説明を挿入

短所

  • 実際の役割はエージェントの役割を生み出し、コードの量は2倍になり、開発効率は低下します

動的プロキシモード

  • 動的エージェントには静的エージェントと同じ役割があります。
  • 動的プロキシのプロキシクラスは動的に生成され、私たちが直接作成するのではありません。
  • 動的エージェントは、インターフェイスベースの動的エージェントとクラスベースの動的エージェントの2つのカテゴリに分類されます。

動的プロキシモードの実装は、主に次のとおりです。

インターフェイスに基づく:JDK動的プロキシ
インターフェイスに基づく:動的プロキシを実現するためのjavaバイトコード(javasist)

クラスに基づく:CGlib動的エージェント

ASM(基本的な指示、不十分な保守性)

動的プロキシの利点(静的プロキシと比較して)
抽象ロール(インターフェイス)で宣言されたすべてのメソッドは、呼び出しプロセッサの集中型メソッドに転送されるため、多くのメソッドをより柔軟かつ均一に処理できます。

以下に1つずつ紹介します。

JDKは動的プロキシを実装します

JDKの動的プロキシには、ProxyとInvocationHandlerの2つのコアクラスがあります。

  • java.lang.reflect.Proxy:役割:プロキシクラスとオブジェクトを動的に生成します

  • java.lang.reflect.InvocationHandler(プロセッサインターフェイス)
    •実際の役割へのプロキシアクセスは、invokeメソッドを介して実現できます。
    •プロキシを介してプロキシオブジェクトオブジェクトが生成されるたびに、対応するプロセッサオブジェクトを指定します

次のJDKのダイナミックなエージェンシーを実現するために家を借りる場合を考えてみましょう。

コードテスト

レンタルインターフェース:

/**
 * 租房
 */
public interface Rent {
    
    
    public void rent();//租房这个事情
}

本当の役割:地主

/**
 * 真实的角色:房东
 * 需要将房子租出去
 */
public class FangD implements Rent {
    
    
    @Override
    public void rent() {
    
    
        System.out.println("出租XXX房,两室一厅....");
    }
}

プロキシクラスを自動的に生成します(強調):

/**
 * 这个类会自动的生成代理类
 */
public class ProxyInvocationHandler implements InvocationHandler {
    
    
    //被代理的接口
    private Rent rent;
    public void setRent(Rent rent) {
    
    
        this.rent = rent;
    }

    //生成得到代理类
    //newProxyInstance:返回代理类的一个实例
    public Object getProxy(){
    
    
        return Proxy.newProxyInstance(this.getClass().getClassLoader(),
                rent.getClass().getInterfaces(),this);
    }


    //处理代理实例并返回结果
    //proxy指的是代理类
    //method指的是被代理的方法
    //args指的是该方法的参数对象
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    
    
        //动态代理的本质就是使用反射机制实现的
        seeHouse(); //扩展操作
        Object result = method.invoke(rent, args);
        writeHeTong();//扩展操作
        return result;
    }

    //扩展操作
    public void seeHouse(){
    
    
        System.out.println("中介带你看房子");
    }
    public void writeHeTong(){
    
    
        System.out.println("签署合同");
    }
}

クライアント:

/**
 * 客户
 */
public class Client {
    
    

    public static void main(String[] args) {
    
    
        //真实角色
        FangD fd = new FangD();

        //代理角色:现在是没有
        ProxyInvocationHandler pih = new ProxyInvocationHandler();
        //通过调用程序处理角色来处理我们要调用的接口对象
        pih.setRent(fd);
        //下面的proxy就是动态生成的代理对象
        Rent proxy = (Rent) pih.getProxy();
        proxy.rent();

    }

}

テスト出力:
ここに写真の説明を挿入

概要
動的プロキシの手順:
1。インターフェイスInvocationHandlerを実装するクラスを作成します。これにはinvokeメソッドを実装する必要があり
ます。2。プロキシクラスとインターフェイスを作成します
。3。プロキシ静的メソッドnewProxyInstance(ClassLoaderloader、Class []インターフェイス、InvocationHandler h)を使用して作成ます。プロキシクラス
4。プロキシを介してメソッドを呼び出す

JDKの動的プロキシ実装から、リフレクションメカニズムが実際に使用されていることがわかります。
まず、InvocationHandlerインターフェイスを実装して独自の呼び出しハンドラを作成し、次にClassLoaderオブジェクトとProxyクラスのインターフェイスのセットを指定して動的プロキシクラスを作成し、次にリフレクションメカニズムを介して動的プロキシクラスのコンストラクタを取得します。唯一のパラメータタイプは呼び出しプロセスです。プロセッサのインターフェイスタイプ、そして最後に動的プロキシクラスインスタンスがコンストラクタを介して作成され、プロセッサオブジェクトが構築中にパラメータとして呼び出されます。

ビジネスにおけるJDK動的プロキシの実現

例として、ビジネスにログを追加する上記の関数を取り上げ、同時に、以下に示すように、呼び出しプロセッサコードをツールクラスと同等の形式に特定の変更を加えます
。ProxyInvocationHandler呼び出しプロセッサ:

/**
 * 这个类会自动的生成代理类
 *
 */
public class ProxyInvocationHandler implements InvocationHandler {
    
    
    //被代理的目标接口
    private Object target;
    public void setTarget(Object target) {
    
    
        this.target = target;
    }

    //生成得到代理类
    public Object getProxy(){
    
    
        return Proxy.newProxyInstance(this.getClass().getClassLoader(),
                target.getClass().getInterfaces(),this);
    }


    //处理代理实例并返回结果
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    
    
        //动态代理的本质就是使用反射机制实现的
        log(method.getName());
        Object result = method.invoke(target, args);

        return result;
    }

    //扩展操作--日志
    public void log(String msg){
    
    
        System.out.println("[Debug]--->执行了"+msg+"方法");
    }

}

クライアント:

public class Client {
    
    

    public static void main(String[] args) {
    
    
        //真实角色
        UserServiceImpl userService = new UserServiceImpl();

        //代理角色  不存在
        ProxyInvocationHandler pih = new ProxyInvocationHandler();

        //设置要代理的对象
        pih.setTarget(userService);

        //动态的为其生成代理对象
        UserService proxy = (UserService)pih.getProxy();

        proxy.add();
    }

}

出力:
ここに写真の説明を挿入

開発フレームワークのアプリケーションシナリオ

  • struts2でのインターセプターの実装
  • データベース接続プールのクローズ処理
  • Hibernateでの遅延ロードの実装
  • mybatisにインターセプタープラグインを実装する
  • AspectJの実装
  • 春のAOPの実現
    •ログインターセプト
    •宣言的トランザクション処理
  • ウェブサービス
  • RMIリモートメソッド呼び出し
  • 実際、技術的なフレームワークを選択すると、プロキシモードが使用されます

おすすめ

転載: blog.csdn.net/weixin_43246215/article/details/108599790