Guice Providers 是否应注明@Singleton
?我的理由:如果Provider正在向其他Singleton类提供一个对象,并且该对象本身的创建成本相对较高,那么使用构造其@Inject
中昂贵对象的Singleton Provider是不合理的。构造函数,将其存储为成员,并在getter中返回已保存的全局变量?像这样:
@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请求。这可能需要一段时间。如果我要使用这个Provider多次注入类,那么仅创建一次这样的对象是不是有意义呢?
另请注意,我必须能够使用提供商,因为我需要能够注入提供商。
答案 0 :(得分:21)
如果您的问题是您是否应该创建作用域提供程序绑定,或者您是否应该手动缓存提供程序中的实例,那么实际上,不要试图比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
被绑定在单一范围内。
这里要理解的关键概念是提供者和绑定不是单独的实体。每个绑定都有一个关联的提供程序(当您使用to()
创建普通绑定时,会为您创建一个隐式提供程序)。这可以通过getProvider()
方法签名轻松地观察到 - 它接受Class<T>
或Key<T>
表示您想要获得的实际课程,而不是您已绑定的提供者。创建与特定提供程序的绑定时,如果不配置此提供程序,则需要配置绑定。即使您使用显式提供程序,Guice也足够聪明,可以将范围考虑在内,因此您不需要重新发明轮子并推出自己的单例。
如果你的问题是关于@Singleton
注释的使用(而不是bind()
DSL),那么我不知道它在提供者类上的存在是否会产生任何影响,但鉴于你应该无论如何使用bind().toProvider()
绑定到此提供程序,我认为这不重要。只需使用in()
方法,它肯定会有效。
答案 1 :(得分:8)
请注意,使用.in(Singleton.class)
并在提供程序类上使用@Singleton
注释将提供程序绑定到Singleton范围之间存在重大差异。
get()
方法只调用一次,结果将存储在Singleton范围内。get()
方法。您甚至可以将这两种策略结合起来,例如:通过使用@Singleton
注释提供程序,并使用.in(RequestScoped.class)
将提供程序结果绑定到请求范围。如果没有注释,您的提供程序将针对每个请求进行实例化,如果它存储有状态数据可能会很重要。
这只是为了澄清,因为有些读者可能会偶然发现您的问题,并且可能认为这两种方法在语义上是平等的。
答案 2 :(得分:1)
是的,原则上,但在这种情况下,你可以完全取消提供者,只需创建ExpensiveObject
作为一个渴望的单身人士。它只会在创建注入器时实例化一次,并且该实例将在需要的任何地方注入。