簡體   English   中英

java.lang.System.currentTimeMillis() 替換方法

[英]java.lang.System.currentTimeMillis() replace method

除了重新編譯rt.jar之外,還有什么方法可以用我自己的方法替換currentTimeMillis()調用嗎?

1# 正確的做法是使用Clock對象和抽象時間。

我知道,但我們將運行由無數尚未實現Clock或已實現自己的開發人員開發的代碼。


2# 使用像 JMockit 這樣的模擬工具來模擬該類。

即使這僅適用於禁用熱點-Xint並且我們使用下面的代碼取得了成功,但它不會“持久”在外部庫上。 這意味着您必須在任何地方模擬它,因為代碼不受我們控制,這是不可行的。 main()下的所有代碼都返回 0 毫秒(如示例所示),但new DateTime()將返回實際的系統毫秒。

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

3# 在啟動時使用-Xbootclasspath/p重新聲明System (已編輯)

盡管可能,並且您可以創建/更改方法,但有問題的方法被聲明為public static native long currentTimeMillis(); . 如果不深入研究 Sun 的專有和本機代碼,您就無法更改它的聲明,這將使其成為逆向工程的練習,並且幾乎不是一種穩定的方法。 所有最近的 SUN JVM 都崩潰並出現以下錯誤:

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

4# 使用自定義類加載器(根據評論建議進行新測試)

雖然使用-Djava.system.class.loader替換系統 CL 很簡單, -Djava.system.class.loader JVM 實際上加載自定義 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是使用java -verbose:classrt.jar加載的

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

我的選擇不多了。
有什么我想念的方法嗎?

您可以使用 AspectJ 編譯器/編織器來編譯/編織有問題的用戶代碼,用您自己的代碼替換對 java.lang.System.currentTimeMillis() 的調用。 以下方面將做到這一點:

public aspect CurrentTimeInMillisMethodCallChanger {

    long around(): 
       call(public static native long java.lang.System.currentTimeMillis()) 
       && within(user.code.base.pckg.*) {
         return 0; //provide your own implementation returning a long
    }
}

我不是 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
    }
}

而不是在每個 java 文件中導入您自己的System類。 在 Eclipse 中重新組織導入應該可以解決問題。 並且所有 java 文件都應該使用您的應用程序特定的System類。

正如我所說,這不是一個好的解決方案,因為每當 Java 更改原始類時,您都需要維護您的System類。 此外,您必須確保始終使用您的課程。

正如評論中所討論的,原始問題中的選項 #3 可能確實有效,成功替換了默認的System類。

如果這是真的,那么調用currentTimeMillis()應用程序代碼將按預期調用替換。

也許出乎意料的是,像java.util.Timer這樣的核心類也會被替換!

如果上述所有情況均屬實,那么崩潰的根本原因可能是System類的成功替換。

為了進行測試,您可以將System替換為功能與原始版本相同的副本,以查看崩潰是否消失。

不幸的是,如果這個答案被證明是正確的,那么我們似乎又有了一個新問題。 :) 它可能是這樣的:

“您如何向應用程序類提供更改后的System.currentTimeMillis() ,但為核心類保留默認實現?”

我曾嘗試使用 javassist 刪除本機 currentTimeMills,添加一個純 Java 並使用 bootclasspath/p 加載它,但我遇到了與您相同的異常訪問沖突。 我相信這可能是因為在靜態塊中調用了本機方法 registerNatives ,但是反匯編本機庫確實太多了。

因此,與其更改 System.currentTimeMills,不如更改用戶代碼? 如果用戶代碼已經編譯(你沒有源代碼),我們可以使用 findbugs 之類的工具來識別使用 currentTimeMillis 並拒絕代碼(也許我們甚至可以用你自己的實現替換對 currentTimeMills 的調用)。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM