Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
452 views
in Technique[技术] by (71.8m points)

java - Guice: Cannot inject annotated type in Request scope

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?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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:

bind(String.class)
    .annotatedWith(Names.named("name"))
    .toProvider(Providers.<String>of(null));

and then if the name variable 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.getInstance to 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:

If I bind the same variable to the global scope it works.

Hmm, did you basically do what I did? If so, my explanation above explains why that works :-).

If I remove the annotation, it works.

The reason this works is because Guice does have a binding for String since String has an empty constructor :-). Basically you have to have a single @Inject-able constructor, a no-arg constructor, or a provider to bind a type.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...