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:class
从rt.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的调用)。