17

再コンパイル以外に、呼び出しを自分のものに rt.jar置き換える方法はありますか?currentTimeMillis()

1# それを行う正しい方法は、Clockオブジェクトと抽象的な時間を使用することです。

Clock私はそれを知っていますが、実装していない、または独自の実装を作成した無数の開発者によって開発されたコードを実行することになります。


2# JMockit のようなモック ツールを使用して、そのクラスをモックします。

これはホットスポットが無効-Xintになっている場合にのみ機能し、次のコードを使用して成功しましたが、外部ライブラリには「持続」しません。つまり、コードが制御できないため、どこでもモックする必要があるということです。以下のすべてのコードmain()は (例のように) 0 ミリnew DateTime()を返しますが、a は実際のシステム ミリを返します。

    @MockClass(realClass = System.class)
    public class SystemMock extends MockUp<System> { 
        // returns 1970-01-01   
        @Mock public static long currentTimeMillis() { return 0; }
    }

System3#を使用して起動時に再宣言する-Xbootclasspath/p(編集済み)

可能であり、メソッドを作成/変更することはできますが、問題のメソッドは として宣言されていpublic static native long currentTimeMillis();ます。Sun 独自のコードやネイティブコードを掘り下げずに宣言を変更することはできません。これにより、リバース エンジニアリングが行われ、安定したアプローチとは言えなくなります。最近のすべての SUN JVM が次のエラーでクラッシュします。

    EXCEPTION_ACCESS_VIOLATION (0xc0000005) at pc=0x00000, pid=4668, tid=5736  

4# カスタム ClassLoader を使用する(コメントで提案されている新しいテスト)

JVMを使用してシステムCLを置き換えるのは簡単ですが-Djava.system.class.loader、実際にはデフォルトのclassLoaderに頼るカスタムclassLoaderをロードし、システムはカスタムCLを通過することさえありません。

    public class SimpleClassLoader extends ClassLoader {
        public SimpleClassLoader(ClassLoader classLoader) {
            super(classLoader);
        }

        @Override 
        public Class<?> loadClass(String name) throws ClassNotFoundException {
            return super.loadClass(name);
        }   
    }

を使用してjava.lang.Systemロードされていることがわかりますrt.jarjava -verbose:class

Line 15: [Loaded java.lang.System from C:\jdk1.7.0_25\jre\lib\rt.jar]

オプションが不足しています。
私が見逃しているアプローチはありますか?

4

4 に答える 4

0

javassist を使用してネイティブの currentTimeMills を削除し、純粋な Java を追加して bootclasspath/p を使用してロードしようとしましたが、あなたと同じ例外アクセス違反が発生しました。おそらく静的ブロックで呼び出されるネイティブメソッド registerNatives が原因だと思いますが、ネイティブライブラリを逆アセンブルするには多すぎます。

では、System.currentTimeMills を変更する代わりに、ユーザー コードを変更してみてはどうでしょうか。ユーザー コードが既にコンパイルされている場合 (ソース コードがない場合)、findbugs などのツールを使用して currentTimeMillis の使用を識別し、コードを拒否できます (currentTimeMills の呼び出しを独自の実装に置き換えることもできます)。

于 2013-08-16T07:36:38.450 に答える
0

ここで何かを監督しているかどうかは 100% わかりませんが、次のSystemように独自のクラスを作成できます。

public static class System {
    static PrintStream err = System.err;
    static InputStream in = System.in;
    static PrintStream out = System.out;

    static void arraycopy(Object src, int srcPos, Object dest, int destPos, int length) {
        System.arraycopy(src, srcPos, dest, destPos, length);
    }

    // ... and so on with all methods (currently 26) except `currentTimeMillis()`

    static long currentTimeMillis() {
        return 4711L; // Your application specific clock value
    }
}

Systemすべての Java ファイルに独自のクラスをインポートするよりも。Eclipse でインポートを再編成するとうまくいくはずです。また、すべての Java ファイルでアプリケーション固有Systemのクラスを使用する必要があります。

System私が言ったように、 Java が元のクラスを変更するたびにクラスを維持する必要があるため、良い解決策ではありません。また、常にクラスが使用されていることを確認する必要があります。

于 2013-08-14T20:10:07.270 に答える
0

Systemコメントで説明されているように、元の質問のオプション #3 が実際に機能し、デフォルトクラスが正常に置き換えられた可能性があります。

それが真の場合、呼び出すアプリケーション コードはcurrentTimeMillis()、予想どおり、置換を呼び出します。

予想外かもしれませんが、 のようなコア クラスjava.util.Timerも置き換えられます!

上記のすべてが当てはまる場合、クラッシュの根本原因は、Systemクラスの置換の成功である可能性があります。

テストするには、代わりにSystemオリジナルと機能的に同一のコピーに置き換えて、クラッシュが消えるかどうかを確認することができます.

残念ながら、この答えが正しければ、新たな疑問が生じます。:) 次のようになります。

System.currentTimeMillis()「アプリケーション クラスに変更を加えて、コア クラスにはデフォルトの実装を残すにはどうすればよいでしょうか?」

于 2013-08-14T20:54:41.867 に答える