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

时间:2013-08-14 18:48:13

标签: java time

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

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

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


2#使用像JMockit这样的模拟工具来模拟该类。

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

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

3#在启动时使用System (已修改)重新声明-Xbootclasspath/p

尽管可能,虽然您可以创建/更改方法,但有问题的方法被声明为public static native long currentTimeMillis();。如果不深入研究Sun的专有和原生代码,你就不能改变它的声明,这将使这成为逆向工程和非稳定方法的练习。 所有最近的SUN JVM都崩溃了,出现以下错误:

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

4#使用自定义ClassLoader (评论中建议的新测试)

虽然使用-Djava.system.class.loader替换系统CL是微不足道的.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.Systemrt.jar加载java -verbose:class

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

我的选项已经用完了 我缺少一些方法吗?

4 个答案:

答案 0 :(得分: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
    }
}

答案 1 :(得分: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
    }
}

而不是在每个java文件中导入自己的System类。重新组织Eclipse中的导入应该可以解决问题。 并且所有java文件都应该使用特定于appatikon的System类。

正如我所说,这不是一个很好的解决方案,因为每当Java改变原始版本时你都需要维护你的System类。此外,您必须确保使用您的课程。

答案 2 :(得分:0)

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

如果确实如此,那么调用currentTimeMillis()的应用程序代码将按预期调用替换代码。

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

如果上述所有情况都属实,那么崩溃的根本原因可能是成功替换System类。

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

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

  

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

答案 3 :(得分:0)

我尝试使用javassist删除本机currentTimeMills,添加一个纯java并使用bootclasspath / p加载它,但我得到了与你一样的异常访问冲突。我相信这可能是因为在静态块中调用的本机方法registerNatives,但是对于本机库的反汇编实在太多了。

所以,而不是更改System.currentTimeMills,如何更改用户代码?如果用户代码已编译(您没有源代码),我们可以使用findbugs之类的工具来识别currentTimeMillis的使用并拒绝代码(也许我们甚至可以用您自己的实现替换对currentTimeMills的调用)。