Guice:无法在Request范围中注入带注释的类型

时间:2012-01-23 19:34:18

标签: java guice

我试图将一个带注释的变量注入REQUEST范围:

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();

其中,InjectingCallable在REQUEST范围内注入GetModule:

/**
 * 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定义如下:

@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;
    }
}

当我运行此代码时,我收到此错误:

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)

如果我将同一个变量绑定到全局范围,它就可以工作。如果我删除注释,它的工作原理。此问题似乎特定于Request-scoped注释变量。有什么想法吗?

1 个答案:

答案 0 :(得分:5)

问题是您没有此类型的绑定。仅仅因为您明确播种该值并不意味着您不必绑定它。你可以说:

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

然后如果name变量的值为"foo",您将会注入"foo",因为您正在播种它。播种值会将其置于作用域(这只是一个缓存)中,这样Guice就不会运行该值的提供者。通过使用null提供程序,如果没有播种,可以让值放大。

简而言之,Guice要求您指定一种方法来配置每个依赖项,无论您是否计划手动设置范围(这应该是一个相当罕见的事情)。

一些未经请求的建议: - 请避免注射注射器。它使得更难以发现这类问题。最好有一个“根对象”。这是您需要调用injector.getInstance来创建的单个对象。对于很多应用程序,这可能只是您的应用程序服务器。 (例如 - injector.getInstance(MyServer.class).startServer())。为什么这对你有帮助?它使得在启动时更容易检测到所有依赖项都得到满足。如果在请求期间注入注入器并且可以调用它来创建任意对象,那么在运行时期间由于缺少绑定而导致出现一些设置错误的风险。此外,如果您在早期执行所有getInstance调用,则更容易编写为您执行此操作的测试,以便您只需运行测试即可知道您的Guice绑定已得到满足。

更新:

  

如果我将同一个变量绑定到全局范围,它就可以工作。

嗯,你基本上做了我做的事吗?如果是这样,我上面的解释解释了为什么这样做: - )。

  

如果删除注释,则可以正常工作。

这有效的原因是因为Guice确实对String有绑定,因为String有一个空的构造函数:-)。基本上你必须有一个@Inject能够构造函数,一个无参数构造函数或一个绑定类型的提供者。