Java プロキシとは何ですか?
プロキシとは何ですか? 実際、理解するのは簡単です。つまり、ターゲットに直接アクセスするのではなく、次のような中間層を通じてアクセスします:
Java の静的プロキシ
たとえば、Java コードで書かれたバナナやリンゴなどの果物の一部は、おそらく次のようになります: (推奨チュートリアル: java チュートリアル)
//Fruit.java/** * 水果的接口 */public interface Fruit { /** * 获取水果的名字 */ public String getName(); }//Apple.javapublic class Apple implements Fruit { @Override public String getName() { return "苹果"; } }//Banana.javapublic class Banana implements Fruit { @Override public String getName() { return "香蕉"; } }
果物を食べるには、皮をむかなければなりません。果物ごとにサブクラスを書くことはできません。皮をむくのはクラスが処理します。したがって、エージェントを作成し、食べる前にリンゴの皮をむくことができます。以下のように、元のフルーツを 1 つのレイヤーでラップします。
//PeelFruitProxy.java/** * 代理,让每个水果去皮 */public class PeelFruitProxy implements Fruit { private Fruit mFruit; public PeelFruit(Fruit fruit) { this.mFruit = fruit; } @Override public String getName() { System.out.println("proxt:" + proxy.getClass().getName()); return "去皮的" + mFruit.getName(); } }
テスト クラスを追加しました。テスト クラスは次のとおりです。
//Main.javapublic class Main { public static void main(String[] args) { Apple apple=new Apple();//原始的苹果 Banana banana=new Banana();//原始的香蕉 PeelFruitProxy peelApple=new PeelFruitProxy(apple);//代理,添加去皮功能 PeelFruitProxy peelBanana=new PeelFruitProxy(banana);//代理,添加去皮功能 System.out.println(peelApple.getName()); System.out.println(peelBanana.getName()); } }
上記は Java の静的プロキシです簡単に言うと、元のターゲット オブジェクトをレイヤーでラップし、新しいものを追加して、ターゲット自体を呼び出すことです。しかし、このような静的プロキシの場合、インターフェイスにはプロキシが必要になり、実装は非常に面倒ではないでしょうか?
Java の動的プロキシ
Java には、これを行う Proxy というクラスがあり、リフレクションとプロキシ インターセプトを直接使用できます。このクラスについて簡単に紹介します。実際、最も一般的に使用される静的メソッドは Proxt.newProxyInstance() で、次のようになります:
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
まず、InvocationHandler を実装し、invoke メソッドを実装し、ターゲットを呼び出す必要があります。 object.time では、invoke メソッドが最初に呼び出され、実装者はこのメソッド内で呼び出し先メソッドを積極的に呼び出す必要があります。
//FruitInvocationHandler.java/** * 调用方法拦截器 */public class FruitInvocationHandler implements InvocationHandler { private Fruit mFruit; public FruitInvocationHandler(Fruit fruit) { this.mFruit = fruit; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { String result = (String) method.invoke(mFruit, args);//需要在这个方法里面,主动调用被代理的对象。 return "去皮的" + result; } }
実行します:
//Main.Javapublic class Main { public static void main(String[] args) { Apple apple = new Apple(); Fruit proxyApple = (Fruit) Proxy.newProxyInstance(Fruit.class.getClassLoader(), new Class[]{Fruit.class}, new FruitInvocationHandler(apple)); System.out.println(proxyApple.getClass().getName()); System.out.println(proxyApple.getName()); Banana banana = new Banana(); Fruit proxyBanana = (Fruit) Proxy.newProxyInstance(Fruit.class.getClassLoader(), new Class[]{Fruit.class}, new FruitInvocationHandler(banana)); System.out.println(proxyApple.getClass().getName()); System.out.println(proxyBanana.getName()); } }
このメソッドは、上記のように PeelFruitProxy を生成します (もちろん、表示される名前は com.sun です)。 proxy.$Proxy0) は、毎回記述する必要がないように動的に生成されます。実行時に任意のクラスをプロキシできるため、これが動的プロキシと呼ばれる理由でもあります。多くのプログラムの AOP はこの方法で実装されていますが、newProxyInstance() の 2 番目のパラメータはインターフェイスのリストですが、なぜこのリストがあるのでしょうか?
動的に生成されたプロキシ クラスも、簡単に下方に変換してそのメソッドを使用できるように、インターフェイスを実装する必要があるためです。そうしないと、生成されたクラスのクラス名は com.sun.proxy.$ になります。 Proxy0. がメモリ内にあるため、生成されたメソッドを呼び出すことができません。 ** したがって、この動的プロキシ メソッドには致命的な欠点があります。それは、プロキシされたクラスがインターフェイスを実装する必要があるということです。 **
CGLib プロキシ
cglib is a powerful, high performance and quality Code Generation Library, It is used to extend JAVA classes and implements interfaces at runtime.
もう 1 つのよく知られた Java プロキシ実装は、ASM に基づくコード生成フレームワークである CGLib (コード生成ライブラリ) です。これを使用できます。クラスを動的に生成してからメソッドのインターセプトを実装すると、ターゲット クラスがインターフェイスを実装する必要があるという JDK の動的プロキシの問題を回避できます。つまり、CGLib を使用して、上記の PeelFruitProxy を生成できます。
使い方を簡単にご紹介します まず、この CGLib はサードパーティのライブラリですので、それに依存する必要があります:
compile 'cglib:cglib:3.2.8 '
最新バージョンはここで見ることができます (新バージョン) [https://github.com/cglib/cglib/releases] それでは、試してみましょう。上記のエージェントを実装しましょう
//FruitMethodInterceptor.java/** * CGLib代理的方法拦截器 */public class FruitMethodInterceptor implements MethodInterceptor{ @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { String result = (String) proxy.invokeSuper(obj, args);//主要,这里调用的是父类,也就是说, 生成的类和原始类是继承关系 return "去皮的"+result; } }//Main.javapublic class Main { public static void main(String[] args) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(Apple.class); enhancer.setCallback(new FruitMethodInterceptor()); Apple apple = (Apple) enhancer.create(); System.out.println(apple.getClass().getName()); System.out.println(apple.getName()); } }
実行 結果は次のとおりです:
#同じ関数が実装されていることがわかりますが、Apple は元の Apple クラスではなくなりました。 com.zjiecode.learn.java. proxy.Apple$$EnhancerByCGLIB$$44ade224, はい、オリジナルの Apple ではなく、実際にこのクラスを使用しています。このクラスは Apple から継承し、最終的に Apple クラスのプロキシを実装します。このように、継承が使用されるため、プロキシされたクラスがインターフェイスを実装する必要はありません。もちろん、インターフェイスを介してエージェントを実装することもできます。
概要
最初のタイプのプロキシについてはここでは説明しませんが、単一のインターフェイス プロキシにのみ適しており、コンパイル時に決定されます。
2 番目と 3 番目のタイプのプロキシはすべて動的プロキシですが、次のような違いがあります:
1) JDK の動的プロキシはインターフェイス プロキシのみを実装でき、パッケージ化されたプロキシです。つまり、プロキシ プロセス中には、プロキシ オブジェクトとターゲット オブジェクトの 2 つのオブジェクトが存在し、ターゲット オブジェクトはプロキシ オブジェクトにパッケージ化されます。
2) CGLib のプロキシは、ターゲット オブジェクトを継承し、新しいクラスを生成し、プロキシを実装します。このように、メモリ内にプロキシ オブジェクトがあり、ターゲット オブジェクトはありません。直接継承が使用されます。メソッド
プロキシ クラスの生成は実行時に行われ、コンパイル時にクラスを生成する Javapoet とは異なります。
以上がJavaプロキシとは何ですかの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。