为什么 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 向外部对象公开任何属性。
我们在项目中使用 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 向外部对象公开任何属性。