We are using Groovy and Guice on a project and I came across the following error:
groovy.lang.MissingPropertyException: No such property: myService for class: com.me.api.services.SomeService$$EnhancerByGuice$$536bdaec
Took a bit to figure out, but it was because I was referencing a private class member, that was injected, inside of a closure. Can anyone shed any light as to why this happens?
Furthermore, is there any better way of doing this?
Here is a snippet of what the class looks like:
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
}
I either have to use a standard for loop or not use the injected member which then tends to lead to code duplication.
Either declare otherService
public (remove private
modifier) or add a getter OtherService<Thing> getOtherService(){otherService}
If you absolutely want to avoid exposing the field through a property, you can do the following trick: create a local variable outside the Closure scope that references your service:
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()
}
Under the hood, your closure is an object of an anonymous class, not the object that has your private field, otherService
.
This means that it can't resolve a direct reference to the field. Accessing a symbol inside the closure will first look at local variables, and if no match is found, the getProperty()
method in Closure
will be called to find a property, depending on the resolution strategy that you defined. By default, this is OWNER_FIRST
.
If you look the code of 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);
}
You see that the owner, delegate and declaring objects need to have matching properties.
In groovy, if you declare a field private
, you won't get auto-generated accessor methods, so no properties will be publicly exposed for outside objects.