JAVA の動的プロキシ
エージェント モード
プロキシ モードは、一般的に使用される Java 設計パターンであり、その特徴は、プロキシ クラスとデリゲート クラスが同じインターフェイスを持つことです。プロキシ クラスは、主にメッセージの前処理、メッセージのフィルタリング、および転送を担当します。デリゲート クラスへのメッセージ、および後処理メッセージなど。通常、プロキシ クラスとデリゲート クラスの間には関連付けがあります。プロキシ クラスのオブジェクト自体は実際にはサービスを実装しませんが、関連するメソッドを呼び出すことによって行われます。デリゲート クラスのオブジェクトの特定のサービスを提供します。
エージェントの作成時期に応じて、エージェントクラスは2種類に分けられます。
静的プロキシ: プログラマーによって作成されるか、特定のツールによって自動的に生成され、コンパイルされます。プログラムが実行される前に、プロキシ クラスの .class ファイルはすでに存在します。
動的プロキシ: プログラムの実行時にリフレクション メカニズムを使用して動的に作成されます。
まず、静的プロキシを見てみましょう:
1、Count.java
package net.battier.dao; /** * 定义一个账户接口 * * @author Administrator * */ public interface Count { // 查看账户方法 public void queryCount(); // 修改账户方法 public void updateCount(); }
2、CountImpl.java
package net.battier.dao.impl; import net.battier.dao.Count; /** * 委托类(包含业务逻辑) * * @author Administrator * */ public class CountImpl implements Count { @Override public void queryCount() { System.out.println("查看账户方法..."); } @Override public void updateCount() { System.out.println("修改账户方法..."); } } 、CountProxy.java package net.battier.dao.impl; import net.battier.dao.Count; /** * 这是一个代理类(增强CountImpl实现类) * * @author Administrator * */ public class CountProxy implements Count { private CountImpl countImpl; /** * 覆盖默认构造器 * * @param countImpl */ public CountProxy(CountImpl countImpl) { this.countImpl = countImpl; } @Override public void queryCount() { System.out.println("事务处理之前"); // 调用委托类的方法; countImpl.queryCount(); System.out.println("事务处理之后"); } @Override public void updateCount() { System.out.println("事务处理之前"); // 调用委托类的方法; countImpl.updateCount(); System.out.println("事务处理之后"); } }
3、TestCount.java
package net.battier.test; import net.battier.dao.impl.CountImpl; import net.battier.dao.impl.CountProxy; /** *测试Count类 * * @author Administrator * */ public class TestCount { public static void main(String[] args) { CountImpl countImpl = new CountImpl(); CountProxy countProxy = new CountProxy(countImpl); countProxy.updateCount(); countProxy.queryCount(); } }
コードを観察すると、各プロキシが次のとおりであることがわかります。クラスは 1 つのインターフェイスのみを提供できるため、プログラム開発では必然的に過剰なプロキシが生成され、呼び出しメソッドを除いてすべてのプロキシ操作が同じになるため、コードを繰り返す必要があります。この問題を解決する最善の方法は、プロキシ クラスを使用してすべてのプロキシ機能を完了することです。この場合、動的プロキシを使用してそれを完了する必要があります。
動的プロキシを見てみましょう:
JDK 動的プロキシにはクラスとインターフェイスが含まれています:
InvocationHandler インターフェイス:
public interface InvocationHandler { public Object invoke(Object proxy,Method method,Object[] args) throws Throwable; }
パラメータの説明:
オブジェクト プロキシ: プロキシされるオブジェクトを指します。
Method メソッド: 呼び出されるメソッド
Object[] args: メソッドを呼び出すときに必要なパラメータ
InvocationHandler インターフェイスのサブクラスは、ProxySubject に代わるプロキシの最終操作クラスとして想像できます。
Proxy クラス:
Proxy クラスは、プロキシ操作に特化したクラスです。このクラスは、1 つ以上のインターフェイスの実装クラスを動的に生成するために使用できます。このクラスは、次の操作メソッドを提供します。 、Class6b3d0130bba23ae47fe2b8e8cddf0195[] インターフェイス、
InvocationHandler h)
Handler h: InvocationHandler インターフェイスの子を取得します。クラス インスタンス
Ps: クラス ローダー
Proxy クラスの newProxyInstance() メソッドで、ClassLoader のインスタンスClassLoader は実際にはクラスローダーに対応します。Java には主に 3 つのクラスローダーがあります。
Booststrap ClassLoader: このローダーは一般的な開発では見ることができません。 jrelibext ディレクトリ内のクラスに対応します;
AppClassLoader: (デフォルト) 指定されたクラスパスをロードします クラスはローダーのタイプとして最も一般的に使用されます。
動的プロキシ
静的プロキシ クラスと対照的に、動的プロキシ クラスのバイトコードは、プログラムの実行時に Java リフレクション メカニズムによって動的に生成され、プログラマがソース コードを手動で記述する必要はありません。 Java リフレクション メカニズムはあらゆるタイプの動的プロキシ クラスを生成できるため、動的プロキシ クラスはプログラミング作業を簡素化するだけでなく、ソフトウェア システムのスケーラビリティも向上します。 java.lang.reflect パッケージの Proxy クラスと InvocationHandler インターフェースは、動的プロキシ クラスを生成する機能を提供します。
動的プロキシの例:
package net.battier.dao; public interface BookFacade { public void addBook(); }
package net.battier.dao.impl; import net.battier.dao.BookFacade; public class BookFacadeImpl implements BookFacade { @Override public void addBook() { System.out.println("增加图书方法。。。"); } } 、BookFacadeProxy.java package net.battier.proxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; /** * JDK动态代理代理类 * * @author student * */ public class BookFacadeProxy implements InvocationHandler { private Object target; /** * 绑定委托对象并返回一个代理类 * @param target * @return */ public Object bind(Object target) { this.target = target; //取得代理对象 return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this); //要绑定接口(这是一个缺陷,cglib弥补了这一缺陷) } @Override /** * 调用方法 */ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object result=null; System.out.println("事物开始"); //执行方法 result=method.invoke(target, args); System.out.println("事物结束"); return result; } }
3、TestProxy.java
package net.battier.test; import net.battier.dao.BookFacade; import net.battier.dao.impl.BookFacadeImpl; import net.battier.proxy.BookFacadeProxy; public class TestProxy { public static void main(String[] args) { BookFacadeProxy proxy = new BookFacadeProxy(); BookFacade bookProxy = (BookFacade) proxy.bind(new BookFacadeImpl()); bookProxy.addBook(); } }
ただし、一部のクラスがインターフェイスを実装していない場合、JDK の動的プロキシはインターフェイスの実装に依存します。 , JDKプロキシは使用できないため、cglibダイナミックプロキシを使用する必要があります。
Cglib 動的プロキシ
JDK の動的プロキシ メカニズムは、インターフェイスを実装するクラスのみをプロキシできます。インターフェイスを実装できないクラスは、JDK の動的プロキシを実装できません。cglib はクラスのプロキシを実装します。その原理は、ターゲット クラスがサブクラスを生成してオーバーライドすることを指定することです。ただし、継承が使用されるため、最終的に変更されたクラスをプロキシすることはできません。例
1、BookFacadeCglib.java
package net.battier.dao; public interface BookFacade { public void addBook(); }
2、BookCadeImpl1.java
package net.battier.dao.impl; /** * 这个是没有实现接口的实现类 * * @author student * */ public class BookFacadeImpl1 { public void addBook() { System.out.println("增加图书的普通方法..."); } }
package net.battier.proxy; import java.lang.reflect.Method; import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; /** * 使用cglib动态代理 * * @author student * */ public class BookFacadeCglib implements MethodInterceptor { private Object target; /** * 创建代理对象 * * @param target * @return */ public Object getInstance(Object target) { this.target = target; Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(this.target.getClass()); // 回调方法 enhancer.setCallback(this); // 创建代理对象 return enhancer.create(); } @Override // 回调方法 public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { System.out.println("事物开始"); proxy.invokeSuper(obj, args); System.out.println("事物结束"); return null; } }
4、TestCglib.java
package net.battier.test; import net.battier.dao.impl.BookFacadeImpl1; import net.battier.proxy.BookFacadeCglib; public class TestCglib { public static void main(String[] args) { BookFacadeCglib cglib=new BookFacadeCglib(); BookFacadeImpl1 bookCglib=(BookFacadeImpl1)cglib.getInstance(new BookFacadeImpl1()); bookCglib.addBook(); } }
Java ダイナミック プロキシの詳細な理解に関連するその他の記事については、 PHP 中国語 Web サイトにご注意ください。