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

除了重新编译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 { // 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#使用自定义ClassLoader (评论中建议的新测试)

虽然使用-Djava.system.class.loader替换系统CL是微不足道的, -Djava.system.class.loader JVM实际上会使用默认的classLoader加载自定义classLoader,而System甚至不会通过自定义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编译器/ weaver来编译/编织有问题的用户代码,用您自己的代码替换对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文件都应该使用您的applicatikon特定的System类。

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

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

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

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

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

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

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

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

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

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