是否应使用 @Singleton 对具有昂贵成员实例的 Guice 提供程序进行注释?

是否应该用 ?我的理由是:如果 Provider 向其他 Singleton 类提供了一个对象,并且该对象本身的创建成本相对较高,那么使用 Singleton Provider 在其标记的构造函数中构造昂贵的对象,将其存储为成员,然后只在 getter 中返回已保存的全局变量,难道没有意义吗?像这样:@Singleton@Inject

@Singleton
public class MyProvider extends Provider<ExpensiveObject> {
    private ExpensiveObject obj;

    @Inject
    public MyProvider() {
        /* Create the expensive object here, set it to this.obj */
    }

    @Override
    public ExpensiveObject get() {
        return obj;
    }
}


更新

让我在这里再澄清一下。这不是关于我是否应该使用 或 。这与所创建对象的“缓存”有更多关系。@Singleton.in(Singleton.class)

假设对象创建需要多个 RPC 才能完成,例如反序列化 JSON 或发出 HTTP 请求。这可能需要相当长的时间。如果我打算多次使用此提供程序注入到类中,那么只创建一次这样的对象难道没有意义吗?

另请注意,我必须能够使用提供程序,因为我需要能够注入提供程序。


答案 1

如果您的问题是是否应该创建作用域内的提供程序绑定,或者是否应该手动在提供程序中缓存实例,那么实际上,不要尝试比 Guice 更智能:)你真的不想做任何事情,除了在方法中创建昂贵的对象。简单测试用例:get()

public class MyProvider implements Provider<String> {
    public String get() {
        System.out.println("Called MyProvider.get()");
        return "abcd";
    }
}

public class MyModule extends AbstractModule {
    protected void configure() {
        bind(String.class).toProvider(MyProvider.class).in(Singleton.class);
    }
}

Injector injector = Guice.createInjector(new MyModule());
String abcd1 = injector.getInstance(String.class);  // Prints "Called MyProvider.get()
String abcd2 = injector.getInstance(String.class);  // Prints nothing!
// Or, if you want, comment out above two lines and try the following:
Provider<String> abcdProvider = injector.getProvider(String.class);
abcdProvider.get();  // Prints "Called MyProvider.get()"
abcdProvider.get();  // Prints nothing

你看,因为消息只打印了一次,所以方法也只被调用了一次,正是因为在单例范围内绑定了。MyProvider.get()String

此处要理解的关键概念是提供程序和绑定不是单独的实体。对于每个绑定,都有一个关联的提供程序(当您使用 创建纯绑定时,将为您创建一个隐式提供程序)。这可以从方法签名中轻松观察到 - 它接受或针对您要获取的实际类,而不是针对已绑定的提供程序。创建到特定提供程序的绑定时,不要配置此提供程序,而是配置绑定。Guice 足够聪明,即使您使用显式提供程序,也可以考虑范围,因此您只需要重新发明轮子并推出自己的单例。to()getProvider()Class<T>Key<T>

如果你的问题是关于注释的使用(而不是DSL),那么我不知道它在提供者类上的存在是否会产生任何影响,但考虑到你应该使用绑定到这个提供者,我认为这并不重要。只是使用方法,它肯定会起作用。@Singletonbind()bind().toProvider()in()


答案 2

请注意,通过使用提供程序类上的注释和在提供程序类上使用注释将提供程序绑定到 Singleton 作用域之间存在重大差异。.in(Singleton.class)@Singleton

  1. 在第一种情况下,该方法仅调用一次,结果将存储在 Singleton 作用域中。get()
  2. 在第二种情况下,提供程序实例仅创建一次,但为应用程序中的每个注入点调用该方法。
    如果使用此方法,则手动缓存昂贵的对象是明智的。但这样做是没有意义的,真的。只需使用第一种方法,您就没事了。get()

您甚至可以将这两种策略结合起来,例如,通过使用 对提供程序进行注释并将提供程序结果绑定到请求范围。如果没有注释,您的提供程序将针对每个请求进行实例化,这对于它是否存储有状态数据可能很重要。@Singleton.in(RequestScoped.class)

这只是为了澄清,因为一些读者可能会偶然发现你的问题,并可能认为这两种方法在语义上是相等的。


推荐