除了重新编译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.System
从rt.jar
加载java -verbose:class
Line 15: [Loaded java.lang.System from C:\jdk1.7.0_25\jre\lib\rt.jar]
我的选项已经用完了 我缺少一些方法吗?
答案 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的调用)。