有什么方法可以从类外部修改 Java 中“private static final”字段的值吗?
我知道这通常相当愚蠢,但在阅读问题之前请不要开枪打我。 我保证我有充分的理由需要这样做:)
可以使用反射修改 Java 中的常规私有字段,但是当尝试对 final
字段执行相同操作时,Java 会抛出安全异常。
我认为这是严格执行的,但我想无论如何我都会问,以防有人想出一个办法来做到这一点。
假设我有一个带有“SomeClass
”类的外部库,
public class SomeClass
{
private static final SomeClass INSTANCE = new SomeClass()
public static SomeClass getInstance(){
return INSTANCE;
}
public Object doSomething(){
// Do some stuff here
}
}
我本质上是想对 SomeClass 进行 Monkey-Patch,以便我可以执行我自己的 doSomething()
版本。 由于(据我所知)没有任何方法可以在 java 中真正做到这一点,因此我唯一的解决方案是更改 INSTANCE
的值,以便它使用修改后的方法返回我的类版本。
本质上我只是想用安全检查来包装调用,然后调用原始方法。
外部库总是使用getInstance()
来获取此类的实例(即它是单例)。
编辑:只是为了澄清, getInstance()
是由外部库调用的,而不是我的代码,所以仅子类化并不能解决问题。
如果我做不到,我能想到的唯一其他解决方案是复制粘贴整个类并修改该方法。 这并不理想,因为我必须使我的叉子与库的更改保持同步。 如果有人有更易于维护的东西,我愿意接受建议。
I know this is normally rather stupid, but don't shoot me before reading the question. I promise I have a good reason for needing to do this :)
It's possible to modify regular private fields in java using reflection, however Java throws a security exception when trying to do the same for final
fields.
I'd assume this is strictly enforced, but figured I'd ask anyway just in case someone had figured out a hack to do this.
Let's just say I have an external library with a class "SomeClass
"
public class SomeClass
{
private static final SomeClass INSTANCE = new SomeClass()
public static SomeClass getInstance(){
return INSTANCE;
}
public Object doSomething(){
// Do some stuff here
}
}
I essentially want to Monkey-Patch SomeClass so that I can execute my own version of doSomething()
. Since there isn't (to my knowledge) any way to really do that in java, my only solution here is to alter the value of INSTANCE
so it returns my version of the class with the modified method.
Essentially I just want to wrap the call with a security check and then call the original method.
The external library always uses getInstance()
to get an instance of this class (i.e. it's a singleton).
EDIT: Just to clarify, getInstance()
is called by the external library, not my code, so just subclassing won't solve the issue.
If I can't do that the only other solution I can think of is to copy-paste entire class and modify the method. This isn't ideal as I'll have to keep my fork up to date with changes to the library. If someone has something a little more maintainable I'm open to suggestions.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(9)
有可能的。 我用它来修补顽皮的线程局部变量,这些线程局部变量阻止了 Web 应用程序中的类卸载。 你只需要使用反射去掉
final
修饰符,就可以修改字段了。像这样的事情就可以解决问题:
在
Field#set
周围也有一些缓存,所以如果某些代码之前已经运行过,它可能不一定有效......It is possible. I've used this to monkeypatch naughty threadlocals that were preventing class unloading in webapps. You just need to use reflection to remove the
final
modifier, then you can modify the field.Something like this will do the trick:
There is some caching as well around
Field#set
, so if some code has run before it might not necessarily work....任何 AOP 框架都可以满足您的需求。
它允许您为 getInstance 方法定义运行时重写,从而允许您返回适合您需要的任何类。
Jmockit内部使用ASM框架来做同样的事情。
Any AOP framework would fit your needs
It would allow you to define a runtime override for the getInstance method allowing you to return whatever class suits your need.
Jmockit uses the ASM framework internally to do the same thing.
您可以尝试以下操作。 注意:它根本不是线程安全的,并且这对于编译时已知的常量基元不起作用(因为它们是由编译器内联的)
You can try the following. Note: It is not at all thread safe and this doesn't work for constant primitives known at compile time (as they are inlined by the compiler)
您应该能够使用 JNI 更改它...不确定这是否适合您。
编辑:这是可能的,但不是一个好主意。
http://java.sun.com/docs/books/jni/ html/pitfalls.html
You should be able to change it with JNI... not sure if that is an option for you.
EDIT: it is possible, but not a good idea.
http://java.sun.com/docs/books/jni/html/pitfalls.html
如果你真的必须(尽管对于我们的问题,我建议你使用 CaptainAwesomePants 的解决方案)你可以看看 JMockIt。 尽管这旨在用于单元测试,但允许您重新定义任意方法。 这是通过在运行时修改字节码来完成的。
If you really must (though for our problem I'd suggest you use the solution of CaptainAwesomePants) you could have a look at JMockIt. Although this is intented to be used in unit tests if allows you to redefine arbitrary methods. This is done by modifying the bytecode at runtime.
我将在这个答案的序言中承认,这实际上并不是您所说的有关修改私有静态最终字段的问题的答案。 然而,在上面提到的具体示例代码中,我实际上可以这样做,以便您可以重写 doSomething()。 您可以做的是利用 getInstance() 是公共方法和子类这一事实:
现在只需调用 MySomeClass.getInstance() 而不是 SomeClass.getInstance() 即可。 当然,只有当您调用 getInstance() 而不是您正在使用的不可修改内容的其他部分时,这才有效。
I will preface this answer by acknowledging that this is not actually an answer to your stated question about modifying a private static final field. However, in the specific example code mentioned above, I can in fact make it so that you can override doSomething(). What you can do is to take advantage of the fact that getInstance() is a public method and subclass:
Now just invoke MySomeClass.getInstance() instead of SomeClass.getInstance() and you're good to go. Of course, this only works if you're the one invoking getInstance() and not some other part of the unmodifiable stuff you're working with.
使用 mockito 非常简单:
此代码打印“somethingchange!”; 您可以轻松替换您的单例实例。 我的 0.02 美分。
with mockito is very simple:
this code prints "something changed!"; you can easily replace your singleton instances. My 0.02$ cents.
如果没有可用的外部黑客(至少我不知道),我就会黑客该类本身。 通过添加所需的安全检查来更改代码。 因此它是一个外部库,您不会定期进行更新,而且无论如何也不会发生太多更新。 每当这种情况发生时,我都可以很高兴地重新做,因为无论如何这都不是一项艰巨的任务。
If there is no external hack available (at least I am not aware of) I would have hacked the class itself. Change the code by adding the security check you want. As such its an external library, you won't be taking the updates regularly, also not many update happens anyway. Whenever that happens I can happily re-do it as it is not a big task anyway.
在这里,您的问题是古老的依赖注入(又名控制反转)。 您的目标应该是注入
SomeClass
的实现,而不是对其进行猴子修补。 是的,这种方法需要对您现有的设计进行一些更改,但出于正确的原因(在此列出您最喜欢的设计原则) - 特别是同一个对象不应该负责创建和使用其他对象。我假设您使用
SomeClass
的方式看起来有点像这样:相反,您应该做的是首先创建实现相同接口或扩展
SomeClass
的类,然后传递将该实例添加到doEverything()
中,以便您的类对SomeClass
的实现变得不可知。 在这种情况下,调用doEverything
的代码负责传递正确的实现 - 无论是实际的SomeClass
还是经过猴子修补的MySomeClass
。Here, your problem is good-old Dependency Injection (aka Inversion of Control). Your goal should be to inject your implementation of
SomeClass
instead of monkeypatching it. And yes, this approach requires some changes to your existing design but for the right reasons (name your favorite design principle here) - especially the same object should not be responsible for both creating and using other objects.I assume the way you're using
SomeClass
looks somewhat like this:Instead, what you should do is first create your class that implements the same interface or extends
SomeClass
and then pass that instance todoEverything()
so your class becomes agnostic to implementation ofSomeClass
. In this case the code that callsdoEverything
is responsible for passing in the correct implementation - whether be the actualSomeClass
or your monkeypatchedMySomeClass
.