I am attempting to inject an annotated variable into the REQUEST scope:
Map<Key<?>, Object> seedMap = ImmutableMap.<Key<?>, Object>builder().
put(Key.get(String.class, Names.named("name")), name).build();
return ServletScopes.scopeRequest(new InjectingCallable<>(injector,
GetModule.class), seedMap).call();
Where, InjectingCallable injects GetModule inside the REQUEST scope:
/**
* A Callable that is constructed in one scope and injects a Callable into a potentially separate
* scope.
* <p/>
* @param <V> the type of object returned by the Callable
* @author Gili Tzabari
*/
public final class InjectingCallable<V> implements Callable<V>
{
private final Injector injector;
private final Class<? extends Callable<V>> delegate;
/**
* Creates a new InjectingCallable.
* <p/>
* @param injector the Guice injector
* @param delegate the class to inject and delegate to
*/
public InjectingCallable(Injector injector, Class<? extends Callable<V>> delegate)
{
Preconditions.checkNotNull(injector, "injector may not be null");
Preconditions.checkNotNull(delegate, "delegate may not be null");
this.injector = injector;
this.delegate = delegate;
}
@Override
public V call() throws Exception
{
return injector.getInstance(delegate).call();
}
}
GetModule is defined as follows:
@RequestScoped
private static class GetModule implements Callable<Module>
{
private final String name;
private final Session session;
@Inject
public GetModule(@Named("name") String name, Session session)
{
this.name = name;
this.session = session;
}
}
When I run this code I get this error:
1) No implementation for java.lang.String annotated with @com.google.inject.name.Named(value=name) was bound.
while locating java.lang.String annotated with @com.google.inject.name.Named(value=name)
If I bind the same variable to the global scope it works. If I remove the annotation, it works. This problem seems to be specific to Request-scoped annotated variables. Any ideas?
The problem is that you don’t have a binding for this type. Just because you are explicitly seeding the value does not mean you don’t have to bind it. you could say:
and then if the
namevariable has the value"foo", you will get"foo"injected because you’re seeding it. Seeding a value places it in the scope (which is just a cache) so that Guice won’t run the provider of the value. By using a provider of null you can just let the value blow up if it’s not seeded.In short, Guice requires that you specify a way to provision every dependency, regardless of whether you plan to manually seed scopes (which should be a fairly rare thing btw).
Some unsolicited advice:
– Please avoid injecting the injector. It makes catching these kinds of problems harder. It’s best to have a single “root object”. This is the single object you need to call
injector.getInstanceto create. For a lot of applications, this can just be your application server. (e.g. –injector.getInstance(MyServer.class).startServer()). Why does this help you? It makes it easier to detect at startup that all your dependencies are satisfied. If you inject the injector during a request and can call it to create arbitrary objects, you run the risk of getting some provision error due to a missing binding much later during runtime. Also if you do all your getInstance calls early on, it’s easier to write tests that do this for you so that you can simply run a test to know that your Guice bindings are satisfied.UPDATE:
Hmm, did you basically do what I did? If so, my explanation above explains why that works :-).
The reason this works is because Guice does have a binding for
StringsinceStringhas an empty constructor :-). Basically you have to have a single@Inject-able constructor, a no-arg constructor, or a provider to bind a type.