10

要するに: 1. 動的プロキシを作成したいいくつかの最終クラスがあります。どうすればいいですか?2. MethodHandle を Method に変換できますか?

詳細 まず、MethodHandle を Method に変換する API はありますか? java.lang.invoke.MethodHandles のようなもの

public MethodHandle unreflect(Method m) throws IllegalAccessException;

しかし、反対方向のアロンド?

動的な java.lang.reflect.Method を作成したいとしましょう。として定義されます

public final
   class Method extends AccessibleObject implements GenericDeclaration,
                                                 Member ;

したがって、JDK 動的プロキシを使用する場合は、何らかのインターフェイス (メンバーなど) を使用する必要があります。ただし、主な欠点が 2 つあります。まず、次のような方法

public Class<?>[] getParameterTypes();

など

public Class<?> getReturnType();

それらは広く使用されていますが、どのインターフェースの一部でもありません。

2 番目の欠点は、ドロップイン交換を提供できないことです。つまり、java.lang.reflect.Method を期待するコードに動的プロキシを渡すことができません。

もう 1 つの方法は、CGLIB または Javaassist を使用することです。AFAIK、CGLIBは最終クラスをプロキシできませんよね?Javaassistは最終クラスをプロキシできますか? クラスから最終識別子を「削除」するにはどうすればよいですか? AFAIL、Javavassistは何とかそれを行うことができます...

4

2 に答える 2

13

必要なプロキシの種類によって異なります。これを実現する方法には基本的に 3 つのアプローチがあり、そのうちの 2 つは製品コードで実現可能です。@probrekelyが述べたように、cglibまたはjavassistの問題は、最終クラスでは不可能なサブクラスを動的に作成することです。これは次の方法で回避できます。

  • バイトコード検証を無効にします。Java ランタイムは、悪意のあるバイト コードがロードされていないことを確認するために、バイト コードを検証します。これは、たとえばアプレットなど、ネットワークまたはインターネットを介してクラスを受信する場合に重要です。このようにして、バイトコード検証機能が停止しないため、最終クラスのサブクラスを作成できます。仮説として、信頼できるコードのみを実行する場合は、この検証を無効にすることができます。これは、次を実行することで実行できます。

    java -Xverify:none ApplicationName
    

    ただし、これは私が最もお勧めしないソリューションです。私はこのアプローチを本番コードには使用しませんが、実装するのが最も簡単なソリューションであることは間違いありません。

  • finalクラスがロードされる前または後に、ロードされたクラスから修飾子を削除します。これは、 Java エージェントを使用して実現できます。Java エージェントは、アプリケーションの起動時にコマンド ラインからインストールするか、実行時にAttach APIからインストールできます。ASM のようなバイト コード ツールを使用すると、元のバイト配列を解析し、目的のすべてのクラスから final 修飾子を削除できます。すでにロードされているクラスを再定義することもできます。修飾子を削除してfinalも、古いクラス バージョンとの競合が発生しないため、そのような再定義は常に可能です。

  • 修飾子を削除して説明したのと同じことを行いfinalますが、ロードされたクラスを再定義して、元のクラス内にすべてのインストルメンテーション ロジックを実際に含めます。このアポラチは、間違いなく最大の労力を必要としますが、これにより、インストルメンテーションが他のすべてのコードに対して透過的になります。これは、すべてのソリューションの中で最もクリーンなソリューションです。

于 2013-12-01T19:43:36.503 に答える
0

申し訳ありませんが、あなたが望むものは不可能です:

CGLIB または Javassist を使用して具象クラスのプロキシを作成できます。これらのライブラリは、プロキシしようとしているクラスのサブクラスを動的に生成するためです。finalクラスはサブクラス化できないため、この方法でプロキシを作成することはできません。

PowerMock ではfinalクラスとメソッドをプロキシできますが、これは、 JavassistClassLoaderを使用してロード時にプロキシするクラスのバイトコードを変更する特別な環境でテストを実行するためです。(一般に、結果として得られるクラスの修正された「ゾンビ」バージョンは、特定の模擬単体テストを実行する以外にはあまり役に立たないため、本番環境ではこの種のものを使用したくないでしょう。)

ただし、PowerMock のアプローチはここでは機能しませんjava.lang.reflect.Method。ブートストラップ クラスパスにある proxy が必要なため、PowerMock/Javassist タイプのツールの前にロードされるため、プロキシできません。

于 2013-11-02T00:15:28.047 に答える