JAVA動的プロキシ
プロキシモード
エージェント パターンは、一般的に使用される Java 設計パターンであり、エージェント クラスとデリゲート クラスが同じインターフェイスを持つことが特徴です。エージェント クラスは、主にデリゲート クラスのメッセージの前処理、メッセージのフィルタリング、デリゲート クラスへのメッセージの転送を担当します。 、その後メッセージを処理します。通常、プロキシ クラスとデリゲート クラスの間には関連付けがあります。プロキシ クラスのオブジェクト自体は実際にはサービスを実装しませんが、関連するメソッドを呼び出すことによって行われます。デリゲート クラスのオブジェクトの特定のサービスを提供します。
エージェントの作成時期に応じて、エージェントクラスは 2 種類に分類されます。
静的プロキシ:プログラマによって作成されるか、特定のツールによって自動的に生成されてコンパイルされます。プログラムが実行される前に、プロキシ クラスの .class ファイルはすでに存在します。
動的プロキシ:プログラムの実行時にリフレクション メカニズムを使用して動的に作成されます。
まず静的プロキシを見てみましょう。
1.カウント.java
次のようにコードをコピーします。
パッケージ net.battier.dao;
/**
* アカウントインターフェースを定義する
*
* @author 管理者
*
*/
パブリック インターフェイスの数 {
// アカウントの表示メソッド
public void queryCount();
//アカウントメソッドを変更する
public void updateCount();
}
2.CountImpl.java
次のようにコードをコピーします。
パッケージ net.battier.dao.impl;
net.battier.dao.Count をインポートします。
/**
* デリゲートクラス(ビジネスロジックを含む)
*
* @author 管理者
*
*/
public class CountImpl は Count { を実装します。
@オーバーライド
public void queryCount() {
System.out.println("アカウント メソッドの表示...");
}
@オーバーライド
public void updateCount() {
System.out.println("アカウント方法を変更...");
}
}
CountProxy.java
パッケージ net.battier.dao.impl;
net.battier.dao.Count をインポートします。
/**
※プロキシクラス(拡張CountImpl実装クラス)です
*
* @author 管理者
*
*/
public class CountProxy は Count { を実装します。
プライベート CountImpl countImpl;
/**
* デフォルトのコンストラクターをオーバーライドします
*
* @param countImpl
*/
public CountProxy(CountImpl countImpl) {
this.countImpl = countImpl;
}
@オーバーライド
public void queryCount() {
System.out.println("トランザクション処理前");
// デリゲート クラスのメソッドを呼び出します。
countImpl.queryCount();
System.out.println("トランザクション処理後");
}
@オーバーライド
public void updateCount() {
System.out.println("トランザクション処理前");
// デリゲート クラスのメソッドを呼び出します。
countImpl.updateCount();
System.out.println("トランザクション処理後");
}
}
3. テストカウント.java
次のようにコードをコピーします。
パッケージ net.battier.test;
net.battier.dao.impl.CountImpl をインポートします。
net.battier.dao.impl.CountProxy をインポートします。
/**
*テスト数クラス
*
* @author 管理者
*
*/
パブリック クラス TestCount {
public static void main(String[] args) {
CountImpl countImpl = 新しい CountImpl();
CountProxy countProxy = 新しい CountProxy(countImpl);
countProxy.updateCount();
countProxy.queryCount();
}
}
コードを観察すると、各プロキシ クラスが 1 つのインターフェイスのみを提供できることがわかります。このように、プログラム開発中に必然的に過剰なプロキシが生成されます。さらに、プロキシの操作は、それらが呼び出すメソッドを除いて同じです。この時点で繰り返す必要があります。この問題を解決する最善の方法は、プロキシ クラスを使用してすべてのプロキシ機能を完了することです。この場合、動的プロキシを使用してそれを完了する必要があります。
動的プロキシを見てみましょう。
JDK 動的プロキシにはクラスとインターフェイスが含まれています。
InvocationHandler インターフェース:
パブリック インターフェイス InvocationHandler {
public Object invoke(Object proxy,Methodメソッド,Object[] args) throws Throwable;
}
パラメータの説明:
オブジェクト プロキシ: プロキシされるオブジェクトを指します。
メソッドメソッド: 呼び出されるメソッド
Object[] args: メソッドを呼び出すときに必要なパラメータ
InvocationHandler インターフェイスのサブクラスは、ProxySubject に代わるプロキシの最終操作クラスと考えることができます。
プロキシクラス:
Proxy クラスは、プロキシ操作に特化したクラスです。このクラスは、1 つ以上のインターフェイスの実装クラスを動的に生成するために使用できます。このクラスは、次の操作メソッドを提供します。
public static Object newProxyInstance(ClassLoader ローダー、Class<?>[] インターフェイス、
呼び出しハンドラ h)
IllegalArgumentException をスローします
パラメータの説明:
ClassLoaderローダー:クラスローダー
Class<?>[] インターフェイス: すべてのインターフェイスを取得します
InvocationHandler h: InvocationHandler インターフェイスのサブクラス インスタンスを取得します。
追伸: クラスローダー
ClassLoader クラスのインスタンスは、Proxy クラスの newProxyInstance() メソッドに必要です。ClassLoader は、実際にはクラス ローダーに対応します。Java には 3 つの主要なクラス ローダーがあります。
Booststrap ClassLoader: このローダーは C++ で書かれており、一般的な開発では見ることができません。
Extension ClassLoader: 拡張クラスをロードするために使用されます。通常は jre/lib/ext ディレクトリ内のクラスに対応します。
AppClassLoader: (デフォルト) クラスパスで指定されたクラスをロードします。これは最も一般的に使用されるローダーです。
動的プロキシ
静的プロキシ クラスとは対照的に、動的プロキシ クラスのバイトコードは、プログラムの実行時に Java リフレクション メカニズムによって動的に生成され、プログラマがソース コードを手動で記述する必要はありません。 Java リフレクション メカニズムはあらゆるタイプの動的プロキシ クラスを生成できるため、動的プロキシ クラスはプログラミング作業を簡素化するだけでなく、ソフトウェア システムのスケーラビリティも向上します。 java.lang.reflect パッケージの Proxy クラスと InvocationHandler インターフェースは、動的プロキシ クラスを生成する機能を提供します。
動的プロキシの例:
1.BookFacade.java
次のようにコードをコピーします。
パッケージ net.battier.dao;
パブリック インターフェイス BookFacade {
public void addBook();
}
2.BookFacadeImpl.java
次のようにコードをコピーします。
パッケージ net.battier.dao.impl;
net.battier.dao.BookFacade をインポートします。
パブリック クラス BookFacadeImpl は BookFacade を実装します {
@オーバーライド
public void addBook() {
System.out.println("ブックメソッドを追加...");
}
}
BookFacadeProxy.java
パッケージ net.battier.proxy;
インポート java.lang.reflect.InvocationHandler;
java.lang.reflect.Methodをインポートします。
java.lang.reflect.Proxyをインポートします。
/**
* JDK 動的プロキシ プロキシ クラス
*
* @著者の学生
*
*/
パブリック クラス BookFacadeProxy は InvocationHandler {を実装します。
プライベートオブジェクトターゲット。
/**
* デリゲートオブジェクトをバインドし、プロキシクラスを返します
* @paramターゲット
* @戻る
*/
public Object binding(オブジェクトターゲット) {
this.target = ターゲット;
//プロキシオブジェクトを取得する
return Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(), this); // インターフェースをバインドするには (これは欠陥であり、cglib がこの欠陥を補います)
}
@オーバーライド
/**
※呼び出し方法
*/
public Object invoke(オブジェクトプロキシ、メソッドメソッド、Object[] args)
スロー可能 {
オブジェクトの結果=null;
System.out.println("事の始まり");
//実行メソッド
result=method.invoke(ターゲット, 引数);
System.out.println("物事の終わり");
結果を返します。
}
}
3. テストプロキシ.java
次のようにコードをコピーします。
パッケージ net.battier.test;
net.battier.dao.BookFacade をインポートします。
net.battier.dao.impl.BookFacadeImpl をインポートします。
net.battier.proxy.BookFacadeProxy をインポートします。
パブリック クラス TestProxy {
public static void main(String[] args) {
BookFacadeProxy プロキシ = new BookFacadeProxy();
BookFacade bookProxy = (BookFacade) proxy.bind(new BookFacadeImpl());
bookProxy.addBook();
}
}
ただし、JDK の動的プロキシはインターフェイスの実装に依存します。一部のクラスがインターフェイスを実装していない場合は、JDK プロキシを使用できないため、cglib 動的プロキシを使用する必要があります。
Cglib ダイナミック プロキシ
JDK の動的プロキシ メカニズムは、インターフェイスを実装するクラスのみをプロキシできます。インターフェイスを実装できないクラスは、JDK の動的プロキシを実装できません。その原理は、指定されたターゲット クラスのサブクラスを生成し、メソッドをオーバーライドすることです。ただし、継承が使用されているため、最終的に変更されたクラスをプロキシすることはできません。
例
1.BookFacadeCglib.java
次のようにコードをコピーします。
パッケージ net.battier.dao;
パブリック インターフェイス BookFacade {
public void addBook();
}
2.BookCadeImpl1.java
次のようにコードをコピーします。
パッケージ net.battier.dao.impl;
/**
※インターフェースを実装しない実装クラスです
*
* @著者の学生
*
*/
パブリック クラス BookFacadeImpl1 {
public void addBook() {
System.out.println("書籍を追加する一般的な方法...");
}
}
3.BookFacadeProxy.java
次のようにコードをコピーします。
パッケージ net.battier.proxy;
java.lang.reflect.Methodをインポートします。
net.sf.cglib.proxy.Enhancer をインポートします。
net.sf.cglib.proxy.MethodInterceptor をインポートします。
net.sf.cglib.proxy.MethodProxy をインポートします。
/**
* cglib 動的プロキシを使用する
*
* @著者の学生
*
*/
パブリック クラス BookFacadeCglib は MethodInterceptor を実装します {
プライベートオブジェクトターゲット。
/**
* プロキシオブジェクトの作成
*
* @paramターゲット
* @戻る
*/
public Object getInstance(オブジェクトターゲット) {
this.target = ターゲット;
エンハンサー エンハンサー = new Enhancer();
Enhancer.setSuperclass(this.target.getClass());
// コールバックメソッド
エンハンサー.setCallback(this);
//プロキシオブジェクトを作成する
リターンenhancer.create();
}
@オーバーライド
// コールバックメソッド
public Object intercept(Object obj, Method メソッド, Object[] args,
MethodProxy プロキシ) throws Throwable {
System.out.println("事の始まり");
proxy.invokeSuper(obj, args);
System.out.println("物事の終わり");
null を返します。
}
}
4. テストCglib.java
次のようにコードをコピーします。
パッケージ net.battier.test;
net.battier.dao.impl.BookFacadeImpl1 をインポートします。
net.battier.proxy.BookFacadeCglib をインポートします。
パブリック クラス TestCglib {
public static void main(String[] args) {
BookFacadeCglib cglib=new BookFacadeCglib();
BookFacadeImpl1 bookCglib=(BookFacadeImpl1)cglib.getInstance(new BookFacadeImpl1());
bookCglib.addBook();
}
}