热代码替换应该适用于 Eclipse 中的 Groovy 吗?

Is hot code replace supposed to work for Groovy in Eclipse?

我想知道是否有人能够让 Groovy 热替换在 Eclipse 中可靠地工作。我找不到任何关于此的有用信息,所以我不确定它是否 b/c 它只适用于其他人?或者没有人使用 Eclipse 进行 Groovy 开发?

我已经尝试使用最新的 Eclipse (4.5 Mars) 和最新的 Groovy-Eclipse 插件(Groovy 来自 http://dist.springsource.org/snapshot/GRECLIPSE/e4.5/ 的 Eclipse 2.9.2),但我仍然不能获得可靠的热替换。

一些简单的热替换方案工作正常。然而,稍微复杂一点就会导致奇怪的 Groovy 异常。我在不同的情况下遇到不同的错误,但我能够在一个简单的 junit 中重现一个错误,所以我将用一些简化的域对象来演示那个错误。

HotSwapTests.groovy:

class HotSwapTests {
    @Test
    public void testHotReplace() {
        DefaultTxView transactionGroup = new DefaultTxView();

        List<Default> defaults = [];

        Default d1 = new Default(ProducerAccountTransactionType.REPAID_AMOUNT, ParticipantAccountType.DEFAULT);
        Default d2 = new Default(ProducerAccountTransactionType.REPAID_AMOUNT, ParticipantAccountType.DEFAULT);

        d1.setCancelledDefault(d2);

        defaults << d1;

        transactionGroup.setDefaultTransactions(defaults);


        while (true) {
            Default result = transactionGroup.getRepaymentTransaction();
            println result
        } 
    }
}

DefaultTxView.groovy:

public class DefaultTxView {

    def List<Default> defaultTransactions;

    public Default getRepaymentTransaction() { return getTransactionOfType(REPAID_AMOUNT); }

    public Default getTransactionOfType(ProducerAccountTransactionType type) {
        return defaultTransactions.find { it.getType() == type };
    }

Default.java:

The contents of this domain object are not really important - it's a simple POJO.

现在,为了测试热插拔,我在标记的行放置了一个断点:

while (true) {
    Default result = transactionGroup.getRepaymentTransaction(); <<< break
    println result
} 

然后我转到 DefaultTxView.groovy 并修改传递给 find 方法的闭包内的代码:

public Default getTransactionOfType(ProducerAccountTransactionType type) {
    return defaultTransactions.find { it.getType() == type && it.getCancelledDefault() == null};
}

保存文件时我没有收到任何警告或错误消息,但如果我现在尝试跨过修改后的行,我会收到以下异常:

java.lang.ArrayIndexOutOfBoundsException: 2
    at ca.gc.agr.app.web.jsf.producer.DefaultTxView$_getTransactionOfType_closure1.doCall(DefaultTxView.groovy:15)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:90)
    at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:324)
    at org.codehaus.groovy.runtime.metaclass.ClosureMetaClass.invokeMethod(ClosureMetaClass.java:278)
    at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1016)
    at org.codehaus.groovy.runtime.callsite.PogoMetaClassSite.call(PogoMetaClassSite.java:39)
    at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:45)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:108)
    at org.codehaus.groovy.runtime.callsite.BooleanReturningMethodInvoker.invoke(BooleanReturningMethodInvoker.java:48)
    at org.codehaus.groovy.runtime.callsite.BooleanClosureWrapper.call(BooleanClosureWrapper.java:50)
    at org.codehaus.groovy.runtime.DefaultGroovyMethods.find(DefaultGroovyMethods.java:3060)
    at org.codehaus.groovy.runtime.dgm5.invoke(Unknown Source)
    at org.codehaus.groovy.runtime.callsite.PojoMetaMethodSite$PojoMetaMethodSiteNoUnwrapNoCoerce.invoke(PojoMetaMethodSite.java:271)
    at org.codehaus.groovy.runtime.callsite.PojoMetaMethodSite.call(PojoMetaMethodSite.java:53)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:116)
    at ca.gc.agr.app.web.jsf.producer.DefaultTxView.getTransactionOfType(DefaultTxView.groovy:15)
    at ca.gc.agr.app.web.jsf.producer.DefaultTxView$getTransactionOfType.callCurrent(Unknown Source)
    at ca.gc.agr.app.web.jsf.producer.DefaultTxView.getRepaymentTransaction(DefaultTxView.groovy:11)
    at ca.gc.agr.app.web.jsf.producer.DefaultTxView$getRepaymentTransaction[=16=].call(Unknown Source)
    at ca.gc.agr.app.web.jsf.temp.HotSwapTests.testHotReplace(HotSwapTests.groovy:29)

当 运行 我的 webapp 在 TomCat 时,我得到非常相似的结果,但在修改该行后出现相同的异常。重新启动 junit,或 TomCat 使新线路正常工作,因此这绝对是一个热替换问题。

那我做错了什么?如有任何建议,我们将不胜感激。

我过去使用 eclipse 插件在 Web 开发环境中使用过热部署 with groovy successfully

IIRC,我使用了groovyReset.jar、DCEVM和jdk1.7.

groovyReset.jar 应在 class 路径中并设置为 java agent。我使用了 groovy-eclipse 插件文件夹中的那个(比如 eclipse/plugins/org.codehaus.groovy_2.3.7.xx-201411061335-e44-RELEASE/extras/groovyReset.jar

-javaagent:/groovyReset.jar

无需重新部署即可立即看到新的闭包和方法。当然,在方法中包含一个简单的 LOC 也是可行的。有时我需要重新启动虚拟机,但仍然呼吸新鲜空气。


在你的情况下,我认为至少 groovyReset.jar 必须存在。它负责重置 metaclass。如果你反编译 groovy class,你可以使用 java.lang.Method 的数组检查通过反射调用的方法调用。热代码交换后,此数组出现故障,需要重置。