blog

Java上級 - Javaにおけるプロキシ・パターン

プロキシは、ターゲットオブジェクトにアクセスする別の方法を提供するデザインパターンです。つまり、プロキシオブジェクトを通してターゲットオブジェクトにアクセスします。そうすることの利点は、ターゲットオブ...

Apr 26, 2020 · 6 min. read
シェア

I. プロキシパターン

プロキシは、ターゲットオブジェクトにアクセスする別の方法を提供するデザインパターンです。そうすることの利点は、操作を強化するための追加機能に基づいてターゲットオブジェクトを実装できること、つまりターゲットオブジェクトの機能を拡張できることです。

プロキシされたクラスの関連するメソッドを呼び出し、関連するメソッドを拡張することによって、プロキシされたクラスによく使用されます。トランザクション、ロギング、アラームメールの送信、その他の操作など、ビジネス以外のコードを追加します。

代理店モデルは、中間業者として理解することができます:顧客はメーカーから直接製品を購入することができますが、現実にはそのような販売モデルはほとんどありません。通常はメーカーがエージェントに販売を委託し、顧客は実際の生産者と直接関わることなくエージェントと取引します。つまり、代理店には中間業者的な側面があるのです。

プロキシパターンのポイント:プロキシオブジェクトとターゲットオブジェクト、プロキシオブジェクトはターゲットオブジェクトの拡張であり、ターゲットオブジェクトを呼び出します

プロキシ技術は、AOP(カッター指向プログラミング)技術の実装における重要な技術です。

Java でのプロキシ実装

Javaエージェントは、静的エージェント、動的エージェント、Cglibエージェントに分けられます。

まず、それぞれのエージェントの特徴を説明します:

  • 静的プロキシ
    • プロキシ・クラスは、ターゲット・オブジェクトと同じインターフェイスを実装することで、クラス内にプロキシ・オブジェクトを保持します。コンストラクタを通してターゲットオブジェクトに、プロキシオブジェクトに割り当てられ、プロキシオブジェクトによって実装されたインターフェイスメソッドの実装、および事前および事後インターセプトなどの必要なビジネス機能を実現します。
    • メリット:ターゲットオブジェクトを変更することなく、ターゲットオブジェクトの機能を拡張し、インターセプトすることが可能です。
    • デメリット:プロキシオブジェクトのため、ターゲットオブジェクトと同じインターフェイスを実装する必要があり、非常に多くのプロキシクラスにつながる、維持することは容易ではありませんし、一度メソッドを増加させるインターフェイスは、ターゲットオブジェクトとプロキシクラスを維持する必要があります。
  • 動的プロキシ
    • 動的プロキシは、メモリ内のプロキシオブジェクトの動的な構築を指します、つまり、JDKのAPIの使用も、JDKのプロキシまたはインターフェイスプロキシとして知られている指定されたインターフェイスのオブジェクトを生成します。
    • 利点:プロキシオブジェクトは、インターフェイスを実装する必要はありません、プロキシクラスをたくさん書く必要がなくなりますが、インターフェイスは、メソッドを増やすには、ターゲットオブジェクトとプロキシオブジェクトを維持する必要はありませんが、単にイベントハンドラのメソッドの判断を追加します。
    • 欠点:プロキシオブジェクトは、インターフェイスを実装する必要はありませんが、ターゲットオブジェクトは、インターフェイスを実装する必要があります、そうでなければ、JDKの動的プロキシを使用することはできません
  • Cglibプロキシ
    • 実行時に、プロキシされるクラスのサブクラスが動的に生成され、プロキシされるクラスの最終メソッドでないすべてのメソッドをオーバーライドします。サブクラスでは、親クラスのメソッド呼び出しをすべてインターセプトするメソッドインターセプション技術が使用され、呼び出し側のメソッドで処理が行われます。
    • 利点:JDKダイナミックプロキシは、プロキシされるクラスがインタフェースを実装している必要があります。もう一つの利点は、Cglibダイナミックプロキシは、Javaリフレクションを使用するJDKダイナミックプロキシよりも高速であることです。
    • 欠点: プロキシされるクラスの最終メソッドは、サブクラスによってオーバーライドできないため、プロキシできません。

動的プロキシ

ユーザーが注文するビジネスシナリオのシミュレーション

//オーダーインターフェース
public interface OrderService {
 public void order();
}
//ユーザーオーダークラス
public class UserAction implements OrderService {
 @Override
 public void order() {
 System.out.println("ユーザーオーダー操作...");
 }
}
//ユーザオーダプロキシクラス
public class UserAction implements OrderService {
 @Override
 public void order() {
 System.out.println("ユーザーオーダー操作...");
 }
}

テスト:

分析

  • スタティック・プロキシ・パターンは、ターゲット・オブジェクトを変更することなく、機能的な拡張を可能にします。
  • スタティックプロキシはターゲットオブジェクトのすべてのメソッドを実装しているため、ターゲットインターフェイスがメソッドを増やすと、それに応じてプロキシオブジェクトとターゲットオブジェクトの両方を変更する必要があり、メンテナンスコストが増加します。

動的プロキシ

OrderServiceとUserActionは変更せず、UserActionProxyFactoryを追加します。

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class UserActionProxyFactory {
 private Object target;
 public UserActionProxyFactory(Object target) {
 this.target = target;
 }
 public Object getProxyInstance() {
 Object obj = Proxy.newProxyInstance(
 target.getClass().getClassLoader(),
 target.getClass().getInterfaces(),
 new InvocationHandler() {
 @Override
 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
 System.out.println("ユーザーがログインしているかどうかを判定する");
 Object invoke = method.invoke(target);
 System.out.println("ユーザーの注文を記録する");
 return invoke;
 }
 }
 );
 return obj;
 }
}

テスト:

分析

  • プロキシオブジェクトはインターフェイスを実装する必要はありません。
  • プロキシオブジェクトの生成は、JDKのAPIを使用して、動的にメモリ内のプロキシオブジェクトを構築することです。
  • プロキシオブジェクトはインターフェイスを実装する必要はありませんが、ターゲットオブジェクトはインターフェイスを実装する必要があります。

動的プロキシ

静的プロキシとjdkプロキシは、ターゲット・オブジェクトへのインタフェースを実装する必要がありますが、ターゲット・オブジェクトが単一のオブジェクトであり、インタフェースを実装していない場合があります。

ログインの作成、LoginProxyFactory

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class LoginProxyFactory implements MethodInterceptor {
 private Object target;
 public LoginProxyFactory(Object target) {
 this.target = target;
 }
 /**
 * ターゲットオブジェクトのプロキシを作成する
 * @return
 */
 public Object getProxyInstance() {
 Enhancer en = new Enhancer();
 en.setSuperclass(target.getClass());
 en.setCallback(this);
 return en.create();
 }
 @Override
 public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
 System.out.println("ユーザー名とパスワードが正しいことを確認する...");
 //ターゲットオブジェクトのメソッドの実行
 Object returnValue = method.invoke(target, objects);
 System.out.println("ログインに成功したページ...");
 return returnValue;
 }
}

テスト:

分析

  • Cglibプロキシは、ターゲット・オブジェクトがインターフェースを実装することを必要とせず、メモリ上にオブジェクトのサブクラスを動的に構築し、ターゲット・オブジェクトの機能を拡張します。
  • プロキシ・クラスを final にすることはできません。
  • final/staticであるターゲット・オブジェクトのメソッドはインターセプトされません。

概要

上記のJavaプロキシを理解し、また、SpringのAOPプロキシの実装モデルを理解し、つまり、ターゲットのSpringに参加するインターフェイスの実装では、JDKの動的プロキシの使用、それ以外の場合はCglibプロキシの使用です。 Springはまた、Cglibプロキシを強制的に使用することができますを通じて、Javaバイトコード編集ライブラリのASMの操作を達成するために。バイナリ形式で直接動的にスタブクラスまたは他のプロキシクラスを生成するために、JDKよりもパフォーマンス。

Read next

Python入門小プロジェクトは、入門からこの小プロジェクトを完成させるまで、たった一日あればいい!

Pythonを始めてから小さなプロジェクトを完成させるまでは、実はそれほど難しいことではありません。 Pythonは他のプログラミング言語と比べて初心者が学ぶのに最も適しており、この特徴のおかげでPythonを始めると同時に小さなPythonプロジェクトを完成させることが全く可能なのです。 信じられないですか?まずはこのPythonの小さなプロジェクトを見てください。 私はまずアイデアを言い、次にその効果を実装し、最後に何をすべきかを教えます...

Apr 26, 2020 · 2 min read