0

我了解 groovy 基础知识 - 和闭包......

我正在尝试从 java 调用 groovy:

// 病人.java

public class Patient {  
    //... other data  
    private Map<String, String> attribStore = new HashMap<String,String>();  
    // getters/setters for attribStore omitted  

    public void addAttribute(String key, String val) {
        if (!attribStore.containsKey(key)) {
            attribStore.put(key, val);
        }
    }

// GroovyHelper.java

public class GroovyHelper {
    private String codeSnippet; // groovy script code

    public String evaluateSnippetToString(Binding binding) {
        addMethodMissingHandler();
        GroovyShell shell = createGroovyShell(binding);
        Object result = shell.evaluate(codeSnippet);
        return result.toString();
    }

    // installs a patient in the binding - accesses the patient  
    // attribStore from groovy
    // The missing method is used to create an "attribute" i.e.
    // a (key,val) pair in the patient map 
    private void addMethodMissingHandler() {
        codeSnippet = "def attribStore = p.getAttribStore();\n"
        + "Patient.metaClass.methodMissing = \n{"
        + " String methodName, args -> \n"
        + "methodName = methodName.replaceFirst(/^get/, '');\n"  
        + "def attrib = methodName[0].toLowerCase() + methodName.substring(1);\n"
        + "if (!attribStore.containsKey(attrib)) { attribStore[attrib] = '0'; }\n"
        + "return attribStore[attrib]; \n" + "}\n" + codeSnippet;
    }  
}

//junit 测试代码

private Patient p;
private Binding binding;
private GroovyHelper gh;

    @Before 
    public void init() {
        p = new PatientBuilder().build();
        binding = new Binding();
        binding.setVariable("p", p);
        gh = new GroovyHelper();
    }

    @Test //passes
    public void testPopulatePatientAttribStore() throws Exception {
        p.addAttribute("xyz", "4");
        gh.setCodeSnippet("p.getXyz()");
        gh.evaluateSnippetToString(binding);
    }

    @Test
    public void testGroovy() throws Exception {
        Binding binding = new Binding();
        binding.setVariable("p", new Patient()); // new patient
        p.addAttribute("xyz", "9");

        GroovyShell gs1 = new GroovyShell(binding);

        assertEquals("9", gs1.evaluate("p.getXyz()")); // fails??? - expected: <[9]> but was: <[4]>
    }

我的问题是 - 闭包是否保留了早期绑定的属性存储?
这里到底发生了什么?
为冗长的代码道歉 - 我正在研究它
- 减少不相关的代码
以将其缩小到最低限度 - 任何指针,“更多阅读要做”提示?

4

2 回答 2

1

我认为问题的一部分是您正在元类中创建一个委托给一个特定患者methodMissing的元类。我会以不同的方式解决这个问题 - 是否可以直接在类本身中实现?PatientattribStoremethodMissingPatient

public class Patient {
  // other members as before

  public Object methodMissing(String name, Object[] args) {
    if(name != null && name.startsWith("get") && name.length() > 3) {
      String attrName = name.substring(3,1).toLowerCase() + name.substring(4);
      addAttribute(attrName, "0");
      return attribStore.get(attrName);
    } else {
      throw new MissingMethodException(name, this.getClass(), args);
    }
  }
}

或者,如果这不是一个选项,您能否GroovyHelper在 Groovy 中实现该类(使用 groovyc 编译它,您可以像任何 Java 类一样从 Java 调用它)?

public class GroovyHelper {
    static {
      // add a methodMissing to the Patient metaclass
      Patient.metaClass.methodMissing = { String name, args ->
        if(name?.startsWith("get") && name.length() > 3) {
          String attrName = name.substring(3,1).toLowerCase() + name.substring(4)
          // delegate here is the particular Patient instance on which the
          // missing method call was made
          delegate.addAttribute(attrName, "0")
          return delegate.attribStore[attrName];
        } else {
          throw new MissingMethodException(name, this.getClass(), args);
        }
      }
    }

    private String codeSnippet // groovy script code

    public String evaluateSnippetToString(Binding binding) {
        GroovyShell shell = createGroovyShell(binding)
        Object result = shell.evaluate(codeSnippet)
        return result.toString()
    }
}
于 2012-12-28T20:37:17.840 回答
0

显然 MetaClass 似乎在Patientb/wGroovyShell运行中幸存 - MetaClassRegistry可能是一个单例,因此在类加载器或 JVM 的生命周期中存在(目前无法完全考虑它在 JVM 中是如何工作的)。而且,正如您所建议的,我相信您的methodMissing闭包会围绕attribStore它最初获得的变量关闭。(快速测试似乎表明您应该可以p从该闭包中访问绑定中的 。我猜您尝试过但没有用?)。一种选择可能是在每次测试后以拆卸方法强制删除 MetaClass 。Patient

于 2012-12-28T20:34:14.537 回答