为什么 Groovy 闭包不能访问注入的 class 成员?

Why doesn't a Groovy closure have access to injected class member?

我们在项目中使用 Groovy 和 Guice,我遇到了以下错误:

groovy.lang.MissingPropertyException: No such property: myService for class: com.me.api.services.SomeService$$EnhancerByGuice$6bdaec

花了一点时间才弄清楚,但这是因为我引用了一个私有 class 成员,该成员被注入到闭包中。任何人都可以解释为什么会发生这种情况吗?

此外,还有什么更好的方法吗?

这里是 class 的片段:

import javax.inject.Inject
import javax.inject.Singleton

@Singleton
class MyService extends BaseService<Thing> {

    @Inject
    private ThingDao thingDao

    @Inject
    private OtherService<Thing> otherService

    @Override
    List<Thing> findAll() {
        List<Thing> things = this.dao.findAll()

        things.each { 
            //Note: This doesn't work!
            otherService.doSomething()
        }

        things
    }

我要么必须使用标准的 for 循环,要么不使用注入的成员,这往往会导致代码重复。

TLDR;

声明 otherService public(删除 private 修饰符)或添加 getter OtherService<Thing> getOtherService(){otherService}

如果您 绝对 想避免通过 属性 暴露字段,您可以使用以下技巧:在引用您的闭包范围之外创建一个局部变量服务:

OtherService<Thing> otherService=this.otherService
things.each { 
        //Note: This will work! Because now there is a local variable in the scope. 
        //This is handled by normal anonymous inner class mechanisms in the JVM.
        otherService.doSomething()
}

说明

在幕后,您的闭包是匿名对象 class,而不是具有您的私有字段的对象,otherService

这意味着它无法解析对该字段的直接引用。在闭包内部访问一个符号会首先查看局部变量,如果没有找到匹配,会调用Closure中的getProperty()方法寻找一个属性,这取决于解析策略你定义的。默认情况下,这是 OWNER_FIRST.

如果你看Closure#getProperty的代码:

        switch(resolveStrategy) {
            case DELEGATE_FIRST:
                return getPropertyDelegateFirst(property);
            case DELEGATE_ONLY:
                return InvokerHelper.getProperty(this.delegate, property);
            case OWNER_ONLY:
                return InvokerHelper.getProperty(this.owner, property);
            case TO_SELF:
                return super.getProperty(property);
            default:
                return getPropertyOwnerFirst(property);
        }

您看到所有者、委托和声明对象需要具有匹配的属性

在 groovy 中,如果您声明一个字段 private,您将不会获得自动生成的访问器方法,因此不会 public 向外部对象公开任何属性。