有没有办法从类的外部修改`私有静态final`领域的Java中的价值?

StackOverflow https://stackoverflow.com/questions/767008

  •  12-09-2019
  •  | 
  •  

我知道这通常是相当愚蠢的,但看完这个问题之前,不要拍我。我保证我有需要做一个很好的理由:)

这是可能的修改使用反射在Java正则私人领域,但是,Java抛出试图为final领域做同样的,当一个安全异常。

我认为这是严格执行的,但想我会问反正以防万一有人想出了一个黑客做到这一点。

让我们只说我有一类“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的,这样我可以执行我自己doSomething()版本。由于没有(据我所知)没有办法真正做到这一点在Java中,我在这里唯一的解决办法是这样,它返回我的类版本与修改后的方法来改变INSTANCE的值。

基本上我只是想安全检查包裹的呼叫,然后调用原始的方法。

在外部库总是使用getInstance()获得这个类的一个实例(即它是一个单件)。

编辑:只是为了澄清,getInstance()由外部库调用,而不是我的代码,所以只是继承并不能解决问题。

如果我不能做到这一点唯一的其他解决方案,我能想到的是复制粘贴整个类和修改方法。这不是理想的,因为我必须保持我的叉子了解最新的更改库。如果有人有东西多一点维护我开放的建议。

有帮助吗?

解决方案

有可能的。我用这个来猴补丁是被防止类卸货的webapps顽皮threadlocals。你只需要使用反射来去除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);
}

有一些缓存以及周围Field#set,因此,如果一些代码运行之前它可能不一定工作....

其他提示

任何AOP框架将满足您的需求。

这将允许你定义一个运行时覆盖的getInstance方法允许你返回任何类适合您的需求。

Jmockit使用ASM框架内部做同样的事情。

您可以尝试以下。注:这是不是在所有线程安全的,这不适合在编译时已知的常元的工作(因为它们是由编译器内联)

Field field = SomeClass.class.getDeclareField("INSTANCE");
field.setAccessible(true); // what security. ;)
field.set(null, newValue);

您应该能够JNI来改变它......不知道这是一个选择。

编辑:它是可能的,但不是一个好主意

http://java.sun.com/docs/books /jni/html/pitfalls.html

  

10.9违反访问控制规则

     

在JNI不强制类,字段   和方法的访问控制限制   可以在Java表达   通过编程语言水平   使用改性剂如私人和   最后。它可以编写本地   代码来访问或修改的字段   对象即使在这样做   Java编程语言水平将   导致IllegalAccessException。   JNI的放纵是一个有意识   设计决定,鉴于本地   代码可以访问和修改的任何存储器   位置在堆中反正。

     

本机代码绕过   源语言级别的访问检查   可能有不良影响   程序执行。例如,一个   不一致性可被如果创建   本地方法修改的最终场   一个刚刚在时间(JIT)编译器后   器内联访问到外地。   同样,本机方法不应该   修改不变对象如   在实例字段   java.lang.String或者是java.lang.Integer中。   这样做可能导致的破损   在Java平台不变   实施

如果你真的必须的(尽管我们的问题,我建议你使用CaptainAwesomePants的解决方案),你可以看看的 JMockIt 。虽然这是intented在单元测试中使用,如果让你重新定义任意的方法。这是通过在运行时修改字节码来完成。

我会承认,这并不是真正的回答你有关修改私有静态最终字段中声明的问题前言这个答案。然而,在上述具体示例代码,我其实可以让这个你可以覆盖DoSomething的()。你可以做的是采取的事实的getInstance()是一个公共方法和子类的优势:

public class MySomeClass extends SomeClass
{
   private static final INSTANCE = new MySomeClass();

   public SomeClass getInstance() {
        return INSTANCE;
   }

   public Object doSomething() {
      //Override behavior here!
   }
}

现在只是调用MySomeClass.getInstance()而不是SomeClass.getInstance()和你去好。当然,如果你是一个调用的getInstance(),而不是你正在使用的不可修改的东西,其他部分时才能这样。

的Mockito是非常简单的:

import static org.mockito.Mockito.*;

public class SomeClass {

    private static final SomeClass INSTANCE = new SomeClass();

    public static SomeClass getInstance() {
        return INSTANCE;
    }

    public Object doSomething() {
        return "done!";
    }

    public static void main(String[] args) {
        SomeClass someClass = mock(SomeClass.getInstance().getClass());
        when(someClass.doSomething()).thenReturn("something changed!");
        System.out.println(someClass.doSomething());
    }
}

此代码打印“的东西变了!”您可以轻松地更换你的单实例。我的$ 0.02美分。

如果没有可用的外部黑客攻击(至少我不知道),我会砍死类本身。加入你想要的安全检查更改代码。因此,其外部库,你会不会考虑定期更新,也没有太多的更新发生反正。每当出现这种情况我可以高兴地重新做它,因为它不是一个大任务呢。

在这里,你的问题是好大的依赖注入(控制反转又名)。你的目标应该是注入,而不是它的Monkeypatching你SomeClass的实现。是的,这种方法需要一些改变现有的设计,但正确的原因(这里命名为您喜欢的设计原理) - 尤其是相同的对象不应该是负责创建和使用的其他对象。

我假设你正在使用SomeClass的方式看起来有点像这样:

public class OtherClass {
  public void doEverything() {
    SomeClass sc = SomeClass.geInstance();
    Object o = sc.doSomething();

    // some more stuff here...
  }
}

相反,你应该做的是首先创建类实现相同的接口或扩展SomeClass,然后通过该实例doEverything()让你的类成为不可知的实施SomeClass的。在这种情况下,调用doEverything的代码负责传递正确的实现 - 无论是实际SomeClass或您的monkeypatched MySomeClass

public class MySomeClass() extends SomeClass {
  public Object doSomething() {
    // your monkeypatched implementation goes here
  }
}

public class OtherClass {
  public void doEveryting(SomeClass sc) {
    Object o = sc.doSomething();

    // some more stuff here...
  }
}
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top