代码之家  ›  专栏  ›  技术社区  ›  richq luc

可以在Java中使用monkey补丁吗?

  •  20
  • richq luc  · 技术社区  · 16 年前

    如果可能的话,我不想讨论这种方法的优点。我相信答案是“不”。但也许有人会给我一个惊喜!

    假设您有一个核心小部件类。它有一个方法 calculateHeight() 计算重量 返回一个更好的尺寸。

    现在,一个库类WindowDisplayFactory在一个相当复杂的方法中实例化DefaultWidget。您希望它使用您的小部件。factory类的方法如下所示:

    public IWidget createView(Component parent) {
        DefaultWidget widget = new DefaultWidget(CONSTS.BLUE, CONSTS.SIZE_STUPIDLY);
    
        // bunch of ifs ...
        SomeOtherWidget bla = new SomeOtherWidget(widget);
        SomeResultWidget result = new SomeResultWidget(parent);
        SomeListener listener = new SomeListener(parent, widget, flags);
    
        // more widget creation and voodoo here
    
        return result;
    }
    

    计算重量 在那里。理想情况下,我希望能够对DefaultWidget进行修补,以便它的calculateHeight做正确的事情。。。

    public class MyWindowDisplayFactory {
        public IWidget createView(Component parent) {
            DefaultWidget.class.setMethod("calculateHeight", myCalculateHeight);
            return super.createView(parent);
        }
    }
    

    setMethod() 虽然我可以选择的其他选项包括:

    • 复制和粘贴 createView() 方法转换为从factory类继承的我自己的类

    工厂类不能更改-它是核心平台API的一部分。我尝试对返回的结果进行反射,以获得(最终)添加的小部件,但它是几个小部件层,在某些地方它被用来初始化其他东西,导致奇怪的副作用。

    9 回复  |  直到 10 年前
        1
  •  9
  •   Andy    16 年前

    Spring提供了一些AOP功能,但还有其他库也提供了这些功能。

        2
  •  7
  •   Joachim Sauer    16 年前

        3
  •  3
  •   Dennis C    16 年前

    只是我的想法,

    有可能使用AOP,通过字节码工程的方式,向calculateHeight方法注入一个方面。

    然后,您可以通过ThreadLocal或else变量启用修补程序。

        4
  •  2
  •   Dan Vinton    16 年前

    cglib 是一个Java库,它可以做一些类似于猴子补丁的事情——它可以在运行时操纵字节码来改变某些行为。我不确定它是否能完全满足你的需要,但值得一看。。。

        5
  •  2
  •   Will Sargent    7 年前

    使用Unsafe.putObject和类查找器,完全可以在Java中使用monkeypatch。在这里写了一篇博文:

    https://tersesystems.com/blog/2014/03/02/monkeypatching-java-classes/

        6
  •  0
  •   Rolf Rander    16 年前

    class MyWidget implements IWidget {
        private IWidget delegate;
        public MyWidget(IWidget d) {
            this.delegate = d;
        }
        public int calculateHeight() {
            // my implementation of calculate height
        }
        // for all other methods: {
        public Object foo(Object bar) {
            return delegate.foo(bar);
        }
    }
    

    为了实现这一点,您需要拦截要替换的小部件的所有创建,这可能意味着为WidgetFactory创建一个类似的包装器。您必须能够配置要使用的WidgetFactory。

    这还取决于没有客户端试图将IWidget强制转换回DefaultWidget。。。

        7
  •  0
  •   Steve B.    16 年前

    1. 深入研究库API,看看是否有某种方法可以覆盖默认值和大小。在swing(至少对我来说)中,大小可能会混淆,如setMinimum、setMaximum、setdefault、setDefaultOnThursday等等。有可能有办法。如果您可以联系库设计师,您可能会找到一个解决方案,以减少不愉快的黑客攻击。

    2. 也许只会覆盖某些默认大小调整参数来扩展工厂?取决于工厂,但这可能是可能的。

    创建具有相同名称的类可能是唯一的其他选项,正如其他人指出的那样,它很难看,当您更新api库或在不同环境中部署时,您很容易忘记它并破坏内容,并且忘记为什么要这样设置类路径。

        8
  •  0
  •   Sebastien Lorber    9 年前

    您可以尝试使用PowerMock/Mockito之类的工具。如果可以在测试中进行模拟,那么也可以在生产中进行模拟。

    然而,这些工具并不是真正设计成这样使用的,因此您必须自己准备环境,并且不能像在测试中那样使用JUnit Runner。。。

        9
  •  -1
  •   skiphoppy    16 年前

    嗯,我一直在尝试发布建议,然后我发现它们不起作用,或者你已经提到你尝试过了。

    我能想到的最好的解决方案是将WindowDisplayFactory子类化,然后在子类的createView()方法中,首先调用super.createView(),然后修改返回的对象以完全抛出小部件,并将其替换为满足您需要的子类的实例。但是这个小部件是用来初始化东西的,所以你必须改变所有这些。

    然后我想到对createView()返回的对象使用反射,并尝试以这种方式解决问题,但同样,这很棘手,因为有太多东西是用小部件初始化的。不过,我想我会尝试使用这种方法,如果它足够简单,可以证明它优于复制和粘贴。

    我会看这部电影,并想看看我是否能想出其他的点子。Java反射确实不错,但它无法打败我在Perl和Python等语言中看到的动态内省。