【问题标题】:Guice: Cannot inject annotated type in Request scopeGuice:无法在请求范围内注入带注释的类型
【发布时间】:2012-01-23 19:34:18
【问题描述】:

我正在尝试将带注释的变量注入 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)

如果我将同一个变量绑定到全局范围,它就可以工作。如果我删除注释,它会起作用。这个问题似乎特定于请求范围的注释变量。有什么想法吗?

【问题讨论】:

  • 嘿,对于 ImmutableMap,您不需要在等号右侧的 builder() 调用中指定泛型,因为您在左侧有泛型。这就是静态 builder() 方法的重点。即 - ImmutableMap&lt;Key&lt;?&gt;, Object&gt; seedMap = ImmutableMap.builder().put(...).build().
  • @Tom,如果我这样做,我会收到编译器错误。在 Java7 更新 2 下,它默认将右侧解析为 。尝试在您的最后编译它,如果您看到相同的内容,请告诉我。

标签: java guice


【解决方案1】:

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

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-able 构造函数、一个无参数构造函数或一个提供程序来绑定类型。

【讨论】:

  • 顺便说一句,我并不是说应该从不注入注射器...但它很少需要并且可能只应在您需要的情况下使用为某人编写一个基于 guice 的框架,以便为他们提供一种特殊的注入方式。即使这样做,您也应该尝试进行适当的requireBinding 调用,以免启动后出现意外。
  • 我最终改用了子注入器(带有私有模块)。我发现这种方法比绑定一个从未使用过的 Provider 更清晰。谢谢你的澄清!
  • 考虑使用一个自定义的 Provider,它会抛出一个有用的 IllegalStateException(例如“@Named(\"name\") String instance not provided in request scope")——这样如果你犯了错误,你获得比 NullPointerException 更多的帮助
  • @chrispy -- 当然,我想,但我可能不会注入消息。我为常见的提供程序保留了一个实用程序类,其中一个是“失败的提供程序”,它允许您为其提供 RuntimeException 或包装在 IllegalStateException 中的消息。它在测试中特别有用,但也可以像我上面的例子一样使用。
猜你喜欢
  • 2017-08-18
  • 1970-01-01
  • 2011-06-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-06-13
  • 1970-01-01
相关资源
最近更新 更多