【问题标题】:guice - Provider returns always same instanceguice - 提供者总是返回相同的实例
【发布时间】:2013-02-07 10:09:23
【问题描述】:

我正在使用 guice 3 和 guice-servlet 3。在模块中,我定义了这种类型的绑定:

[...]
bind(View.class).annotatedWith(Names.named("view1")).to(View1Impl.class);
bind(View.class).annotatedWith(Names.named("view2")).to(View2Impl.class);
[...]

在注入的类 View1Impl 中我定义如下:

public class View1Impl {

    @Inject @Named("view1") Provider<View> viewProvider;

    @Inject
    void init() {
        View viewA = viewProvider.get();
        View viewB = viewProvider.get();

        log.debug(viewA == viewB);
        log.debug(viewA == this);
    }

}

两个语句都返回 true。但事实并非如此。

我做错了什么?

【问题讨论】:

  • 你的构造函数在哪里,get方法到底是做什么的?
  • @Danyel 没有构造函数,因为我不需要构造函数。实例被注入。还有提供者。见code.google.com/p/google-guice/wiki/InjectingProviders你真的知道什么是依赖注入吗?你熟悉 guice 吗?
  • 我是,但你的例子真的很奇怪。您正在递归地注入您的Provider。您实际上很幸运,它返回了相同的实例(尽管这也有些奇怪),因为否则您将进入无限循环。当你尝试:System.out.println( injector.getInstance( View.class ) == injector.getInstance( View.class ) );false 被返回。也许有人可以解释一下。
  • 这是我的问题,因为我认为应该返回 false。但是我通过进入无限循环来理解你的观点。我可能会通过不急于加载视图来解决我的问题。也许 guice 会进行循环检测。

标签: java dependency-injection guice guice-servlet


【解决方案1】:

您可能已经检查过了——你已经列出了你使用的“那种”绑定——但值得仔细检查一下,在你的非编辑代码中,所涉及的类都没有用@Singleton谨慎注释或绑定到Singleton.class 范围。此外,请确保您的任何绑定都不使用toInstance(),这当然会在所有情况下始终返回预先构造的实例,并且实际上是单例绑定。

我们有一个案例,我们重构了 bindView 方法,最终忘记了我们将其设置为始终将其参数绑定为单例(这样视图的父容器和视图的控制器可以注入相同的查看)。

除非,正如 Danyel 所暗示的那样,Guice 中编码了 circular dependency detection,并且由于您在 @Inject-annotated 方法中调用了您的 provider.get(),因此您可能正在调用它。

【讨论】:

    【解决方案2】:

    如果你查看 Guice 的源代码,就会清楚实际做了什么:

    final ThreadLocal<Object[]> localContext;
    
    /** Looks up thread local context. Creates (and removes) a new context if necessary. */
    <T> T callInContext(ContextualCallable<T> callable) throws ErrorsException {
        Object[] reference = localContext.get();
        if (reference[0] == null) {
            reference[0] = new InternalContext();
            try {
                return callable.call((InternalContext)reference[0]);
            } finally {
                // Only clear the context if this call created it.
                reference[0] = null;
            }
        } else {
            // Someone else will clean up this context.
            return callable.call((InternalContext)reference[0]);
        }
    }
    

    显然,当您的对象被注入时,Guice 将其存储在 ThreadLocal 变量中。现在,根据这段代码sn-p,它会在注入时立即释放。因此,可能在您的“范围”内,它在其他地方被初始化,可能在注入开始时 - 在注入结束时,它被释放。

    【讨论】:

    • 我认为是循环依赖检测。我明天会测试它并接受你的答案,因为你给出了正确的提示。
    • 是的,问题是无限循环。谢谢
    • 你确定是无限循环检测而不是识别资源已经被保留吗?如,它已经在构建中? reference[0] 可能在对象构造开始时设置并在结束时释放,因此在构造时它将始终返回已分配的reference[0]。那将是我的假设。我可能是错的。我所说的构造,当然是指注射。
    • 也许你是对的。但我通过稍后在注入后调用 Provider 的 get-Method 来修复它。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-11-03
    • 1970-01-01
    • 1970-01-01
    • 2012-05-19
    • 2021-08-16
    • 2012-10-05
    相关资源
    最近更新 更多