17

除了重新编译之外,rt.jar还有什么方法可以用currentTimeMillis()我自己的方法替换调用?

1#正确的做法是使用Clock对象和抽象时间。

我知道,但我们将运行由无数尚未实现Clock或已经自己实现的开发人员开发的代码。


2# 使用 JMockit 之类的模拟工具来模拟该类。

尽管这只适用于禁用热点-Xint并且我们使用下面的代码成功,但它不会“持久”在外部库上。这意味着您必须在任何地方模拟它,因为代码超出了我们的控制范围,这是不可行的。下面的所有代码main()都返回 0 milis(如示例所示),但 anew DateTime()将返回实际的系统 milis。

    @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# 使用自定义类加载器(评论中建议的新测试)

虽然使用-Djava.system.class.loaderJVM 替换系统 CL 很简单,但实际上使用默认的 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

10

您可以使用 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
    }
}
于 2013-08-20T14:07:00.830 回答
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

正如评论中所讨论的,原始问题中的选项 #3 可能确实有效,成功替换了默认System类。

如果这是真的,那么调用的应用程序代码currentTimeMillis()将调用替换,正如预期的那样。

也许出乎意料的是,像这样的核心类java.util.Timer也会得到替换!

如果以上所有都成立,那么崩溃的根本原因可能是成功替换System类。

要进行测试,您可以替换System为功能与原始副本相同的副本,以查看崩溃是否消失。

不幸的是,如果这个答案被证明是正确的,那么我们似乎有一个新问题。:) 它可能是这样的:

“您如何System.currentTimeMillis()为应用程序类提供更改,但为核心类保留默认实现?”

于 2013-08-14T20:54:41.867 回答