有没有办法从类外部修改Java中的`private static final`字段的值?

Jam*_*ies 11 java monkeypatching

我知道这通常是相当愚蠢的,但在阅读这个问题之前不要开枪.我保证我有充分的理由需要这样做:)

可以使用反射修改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 
  }
} 
Run Code Online (Sandbox Code Playgroud)

我基本上想要Monkey-Patch SomeClass,以便我可以执行我自己的版本doSomething().由于没有(据我所知)在java中真正做到这一点的任何方法,我在这里唯一的解决方案是改变它的值,INSTANCE因此它返回我修改后的方法的类版本.

基本上我只想用安全检查包装调用,然后调用原始方法.

外部库总是getInstance()用来获取这个类的实例(即它是一个单例).

编辑:只是澄清,getInstance()由外部库调用,而不是我的代码,所以只是子类化不会解决问题.

如果我不能这样做,我能想到的唯一其他解决方案是复制粘贴整个类并修改方法.这并不理想,因为我必须更新库以更新库.如果某人有更多可维护的东西,我愿意接受建议.

ben*_*phy 10

有可能的.我已经用它来monkeypatch淘气的threadlocals,它阻止了webapps中的类卸载.您只需要使用反射来删除final修改器,然后您可以修改该字段.

像这样的东西可以解决这个问题:

private void killThreadLocal(String klazzName, String fieldName) {
    Field field = Class.forName(klazzName).getDeclaredField(fieldName);
    field.setAccessible(true);  
    Field modifiersField = Field.class.getDeclaredField("modifiers");
    modifiersField.setAccessible(true);
    int modifiers = modifiersField.getInt(field);
    modifiers &= ~Modifier.FINAL;
    modifiersField.setInt(field, modifiers);
    field.set(null, null);
}
Run Code Online (Sandbox Code Playgroud)

还有一些缓存Field#set,所以如果一些代码运行之前它可能不一定工作....


Jea*_*ean 5

任何AOP框架都能满足您的需求

它允许您为getInstance方法定义运行时覆盖,允许您返回任何适合您需要的类.

Jmockit在内部使用ASM框架来做同样的事情.