I'm trying to use provider injection to inject a pre-configured object into a factory:
public class CacheBuilderProvider
implements Provider<CacheBuilder<Object, Object>> {
public CacheBuilder<Object, Object> get () {
//Log the cache builder parameters before creation
CacheBuilder<Object, Object> cacheBuilder = CacheBuilder.newBuilder();
//configure the cacheBuilder
return cacheBuilder;
}
}
public class MyCacheFactory {
private final CacheBuilder<Object, Object> cacheBuilder;
@Inject
public MyFactory(CacheBuilder<Object, Object> cacheBuilder) {
this.cacheBuilder = cacheBuilder;
}
}
public class CacheModule extends AbstractModule {
@Override
protected void configure() {
bind(CacheBuilder.class)
.toProvider(CacheBuilderProvider.class)
.in(Singleton.class);
bind(MyCacheFactory.class)
.in(Singleton.class);
}
}
When I use the injector to get a MyCacheFactory
instance, I get no log output and an unconfigured CacheBuilder<Object, Object>
instance; none of my configuration has been applied. Setting breakpoints verifies that the provider is never being used.
I've also tried applying @Named("MyCacheBuilder")
on the appropriate pieces:
public class CacheBuilderProvider
implements Provider<CacheBuilder<Object, Object>> {
@Named("MyCacheBuilder")
public CacheBuilder<Object, Object> get () { //... }
}
public class MyCacheFactory {
//...
@Inject
public MyFactory(
@Named("MyCacheBuilder")
CacheBuilder<Object, Object> cacheBuilder
) {
//...
}
}
When I attempt to run this code, I get a CreationException
:
1) No implementation for com.google.common.cache.CacheBuilder annotated with @com.google.inject.name.Named(value=MyCacheBuilder) was bound.
I've also tried various combinations of raw and generic type references in the class and constructor declarations, with no additional success.
I can work around this by simply creating the provider and binding the instance returned from get()
myself, but I expect (and deisre) that Guice will do that for me.
Is there something subtly (or obviously) wrong with my setup?
This might work:
bind(new TypeLiteral<CacheBuilder<Object, Object>>() {})
.annotatedWith(Names.named("MyCacheBuilder"))
.toProvider(CacheBuilderProvider.class)
.in(Singleton.class);
The bit of magic here is the TypeLiteral
class, whose Javadoc documentation you should read. Basically it's a way of allowing you to bind to a generic type (CacheBuilder<Object, Object>
in your case). This magic is necessary because Java implements generics using erasure, so you can't just write CacheBuilder<Object, Object>.class
.