web-dev-qa-db-ja.com

Javaクラスは実行時に自身にメソッドを追加できますか?

クラスは実行時に(staticブロックからのように)メソッドを自身に追加できるので、誰かがこのクラスでリフレクションを実行している場合は、新しいメソッドが表示されますが、コンパイル時間?

背景:

私が使用しているフレームワークは、doAction(...)メソッドを持つActionクラスが慣例により定義されることを期待しています。フレームワークは、これらのクラスを実行時に検査して、doAction()メソッドで使用できるパラメーターのタイプを確認します。例:doAction(String a、Integer b)

各クラスで、検査時にジャストインタイムで、さまざまなパラメーターを使用してdoAction()メソッドをプログラムで生成できるようにします。メソッドの本体は空にすることができます。

56
slattery

簡単ではありません。クラスがクラスローダーによってロードされると、ロードされたクラスのメソッドを変更する方法はありません。クラスが要求されると、クラスローダーがそれをロードし、link itにします。また、リンクされたコードを変更したり、メソッドを追加/削除する方法はありません(Javaの場合)。

私の頭に浮かぶ唯一のトリックは、クラスローダーで遊ぶことです。カスタムクラスローダーを削除する場合、そのクラスローダーによってロードされたクラスも削除するか、アクセスできないようにする必要があります。私の頭に浮かぶアイデアは

  1. 1つのカスタムクラスローダーを実装する
  2. そのカスタムクラスローダーでdynamicクラスをロードする
  3. このクラスの更新バージョンがある場合、
  4. 削除カスタムクラスローダー
  5. カスタムクラスローダーの新しいインスタンスでこのクラスの新しいバージョンをロードします

food for thoughtとして、これが解決につながるか、落とし穴があるかどうかは証明できません。

質問に対する簡単な答えとして:いいえ、リフレクションでフィールドのコンテンツを変更できるように、ロードされたクラスを変更することはできません。 (フィールドを追加または削除することもできません)。

54
Andreas_D

Andres_Dは正しいです。カスタムクラスの読み込みを使用して非常にうまく行うことができます。これを行う方法の詳細なガイドを次に示します。 http://www.javaworld.com/javaworld/jw-06-2006/jw- 0612-dynamic.html?page = 1

この記事では、動的Javaコード。コードの実行方法について説明します。ランタイムソースコードのコンパイル、クラスのリロード、および呼び出し元に対して透過的な動的クラスの変更を行うプロキシデザインパターンの使用について説明します。

実際、オーストリアの研究者は、異なるタイプの階層を持つクラスの再ロードを許可するJVMを作成しました。既存のスレッドセーブポイントを使用してこれを実現し、オブジェクトの完全な「サイドユニバース」とそれに関連するすべての参照および参照コンテンツを生成し、必要なすべての変更で完全にシャッフルすると、変更されたすべてのクラスにスワップします。 [1]ここで彼らのプロジェクトへのリンク http://ssw.jku.at/dcevm/ Oracleのスポンサーシップは、将来の計画に関する興味深い憶測を確実に生み出します。

メソッド本体とフィールドへのそれほど邪魔にならない変更は、標準で既に可能ですJava VM JPDAのHot Swap機能を使用してJava 1.4:
docs.Oracle.com/javase/1.4.2/docs/guide/jpda/enhancements.html#hotswap

それが最初のものかどうかはわかりませんが、2001年のこのSunの従業員の論文は、HotSpotのHot Swapの機能に言及した初期の提案の1つであるようです。 [2]

[〜#〜] reference [〜#〜]

[1] T.Würthinger、C。Wimmer、およびL. Stadler、「Dynamic Code Evolution for Java」、2010年ウィーン、Javaでのプログラミングの原則と実践に関する第8回国際会議で発表。

[2] M. Dmitriev、「Java言語アプリケーションの実行時進化のための柔軟で安全な技術に向けて」、2001年、進化のための複雑なオブジェクト指向システムのエンジニアリングに関するOOPSLAワークショップ。

22
DrGranit

私はそのようなことを一度も試したことはありませんが、 [〜#〜] asm [〜#〜]cglib 、および Javassist

8
Ryan Stewart

いいえ、Javaでは(簡単に)不可能です。

Java動的プログラミング言語であるかのように使用しようとしているようです。たとえば、Rubyにはオープンクラスがあります。メソッドを追加および削除できますfrom Ruby実行時のクラス。Rubyでは、クラスに「method missing」メソッドを含めることもできます。このメソッドは、このようなことはJavaにも存在しません。

JVM、JRubyで実行されるRubyのバージョンがあり、JVMでオープンクラスを機能させるには非常に難しいトリックを実行する必要があります。

4
Jesper

生成されたメソッドに必要なことを行うdoActionメソッドを使用できます。生成する必要があるのか​​、それとも動的なのか?

2
Peter Lawrey

プロキシが役立つ場合があります。ただし、メソッドを追加または削除するたびにプロキシをインスタンス化する必要があります。

1
Boki

私の提案はあなたの状況で動作するはずです:1. n個のメソッドを持つ既存のクラスMyClassがあります

それを解決する私の方法は、継承です。最初のクラスMyClassを拡張するクラスMyClassPlusOneの新しい.Javaソースファイルを作成します。このクラスをコンパイルし、オブジェクトを使用します。 Java実行時にクラスをコンパイルしてデプロイするにはどうすればよいですか?

class MyClassPlusOne extends MyClass
{
     void doAction(String a, Integer b)
     {
         int myNPlus1 = a+b;
         //add whatever you want before compiling this code
      }
}
1
user9721802

Asm、cglib、javassistなどのバイトコード変更ツール/フレームワークが必要だと思います。これは、Springのようにアスペクト/ウィービングで実現できますが、メソッドを最初に定義する必要があると思います。

1
carlspring

メソッドを動的に追加する方法がないようです。ただし、メソッドのリストまたは次のようなハッシュを使用してクラスを準備できます。

import Java.lang.reflect.InvocationTargetException;
import Java.lang.reflect.Method;
import Java.lang.reflect.Modifier;
import Java.util.HashMap;

public class GenericClass {
    private HashMap<String, Method> methodMap = new HashMap<String, Method>();

    public Object call(String methodName,Object ...args) 
               throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
        Method method = methodMap.get(methodName);
        return method.invoke(null, args);
    }

    public void add(String name,Method method){
        if(Modifier.isStatic(method.getModifiers()))
            methodMap.put(name, method);
    }

    public static void main(String[] args) {
        try   {
            GenericClass task = new GenericClass();
            task.add("Name",Object.class.getMethod("Name", new Class<?>[0]));
        } catch (NoSuchMethodException | SecurityException e) {
            e.printStackTrace();
        }
   }
}

よりも、反射を使用して、属性を設定または設定解除できます。

それが可能かどうかはわかりません。ただし、AspectJ、ASMなどを使用して、これらのメソッドを適切なクラスに組み込むことができます。

もう1つの方法は、構成を使用してターゲットクラスをラップし、doActionメソッドを提供することです。この場合、ターゲットクラスに委任することになります。

0
tjg184