Java 是否应该使用@Singleton 注释具有昂贵成员实例的 Guice Providers?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/20473606/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Should Guice Providers with expensive member instances as be annotated with @Singleton?
提问by ecbrodie
Should Guice Providersbe annotated with @Singleton
? My justification: if the Provider is providing an object to other Singleton classes and the object itself is relatively expensive to create, then wouldn't it make sense to use a Singleton Provider that constructs the expensive object in its @Inject
-marked constructor, store it as a member and just return that already-saved global variable in the getter? Something like this:
应该吉斯提供商与被标注@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;
}
}
Update
更新
Let me clarify a little bit more here. This is not about whether I should be using @Singleton
or .in(Singleton.class)
. This has to do more with the "caching" of the created object.
让我在这里稍微澄清一下。这与我是否应该使用@Singleton
或.in(Singleton.class)
. 这与创建对象的“缓存”有关。
Let's say that object creation required multiple RPCs to complete, such as deserializing JSON or making HTTP requests. This could take quite some time. If I am going to use this Provider to inject into classes multiple times, then doesn't it make sense to only create such an object once?
假设对象创建需要多个 RPC 才能完成,例如反序列化 JSON 或发出 HTTP 请求。这可能需要相当长的时间。如果我打算使用这个 Provider 多次注入到类中,那么只创建一次这样的对象是否有意义?
Also note that I must be able to use a Provider because I need to be able to inject into the Provider.
另请注意,我必须能够使用 Provider,因为我需要能够注入 Provider。
采纳答案by Vladimir Matveev
If your question is whether you should create scoped provider bindings, or if you should cache instances in your providers manually, then really, do not try to be smarter than Guice :) You really do not want to do anything more than just create your expensive object in get()
method. Simple test case:
如果您的问题是您是否应该创建作用域提供程序绑定,或者您是否应该手动在您的提供程序中缓存实例,那么真的,不要尝试比 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
You see, because the message was printed only once, MyProvider.get()
method was called only once too, exactly because String
is bound in singleton scope.
你看,因为消息只打印一次,MyProvider.get()
方法也只被调用一次,正是因为它String
被绑定在单例范围内。
The key concept to understand here is that providers and bindings are not separate entities. With every binding there is an associated provider (when you create plain bindings with to()
, an implicit provider is created for you). This can easily be observed from getProvider()
method signature - it accepts Class<T>
or Key<T>
for actual class you want to get, not for the provider you have bound. When you create a binding to specific provider, you do not configure this provider, you configure the binding. Guice is smart enough to take scopes into account even if you use explicit providers, so you just do not need to reinvent the wheel and roll out your own singleton.
这里要理解的关键概念是提供者和绑定不是独立的实体。每个绑定都有一个关联的提供程序(当您使用 来创建普通绑定时to()
,会为您创建一个隐式提供程序)。这可以从getProvider()
方法签名中轻松观察到- 它接受Class<T>
或Key<T>
针对您想要获得的实际类,而不是针对您绑定的提供者。创建到特定提供程序的绑定时,您不配置此提供程序,而是配置绑定。即使您使用显式提供程序,Guice 也足够聪明,可以将范围考虑在内,因此您不需要重新发明轮子并推出您自己的单例。
If your question is specifically about usage of @Singleton
annotation (as opposed to bind()
DSL), then I don't know whether its presence on provider class gives any effect, but given that you should use bind().toProvider()
to bind to this provider anyway, I don't think that it really matters. Just use in()
method, it will certainly work.
如果您的问题特别是关于@Singleton
注释的使用(而不是bind()
DSL),那么我不知道它在提供者类上的存在是否会产生任何影响,但考虑到您bind().toProvider()
无论如何都应该使用绑定到此提供者,我不认为这真的很重要。只要使用in()
方法,它肯定会起作用。
回答by Chris Mantle
Yes, in principle, but then in this instance, you could do away with the provider altogether and just create ExpensiveObject
as an eager singleton. It'll only be instantiated once when the injector is created, and that single instance will get injected everywhere it's required.
是的,原则上,但是在这种情况下,您可以完全取消提供程序,而只是创建ExpensiveObject
一个渴望的单身人士。它只会在注入器创建时实例化一次,并且该单个实例将在需要的任何地方注入。
回答by Daniel Beer
Please beware that there is a major difference between binding your provider to the Singleton scope by using .in(Singleton.class)
and using the @Singleton
annotation on your provider class.
请注意,通过使用.in(Singleton.class)
和使用@Singleton
提供者类上的注释将提供者绑定到单例范围之间存在重大差异。
- In the first case the
get()
method is only called once and the result will be stored in the Singleton scope. - In the second case the provider instance is just created once but the
get()
method is called for each injection point in your application.
If you use this approach it would be wise to manually cache your expensive object. But there is no point in doing so, really. Just use the first approach and you are fine.
- 在第一种情况下,该
get()
方法只调用一次,结果将存储在 Singleton 范围内。 - 在第二种情况下,提供程序实例只创建一次,但
get()
为应用程序中的每个注入点调用该方法。
如果您使用这种方法,最好手动缓存您的昂贵对象。但这样做没有意义,真的。只需使用第一种方法就可以了。
You could even combine the two strategies, e.g. by annotating the provider with @Singleton
and bind the provider result to request scope by using .in(RequestScoped.class)
. Without the annotation your provider will be instanciated for each request, which might matter if it stores stateful data.
您甚至可以将这两种策略结合起来,例如通过使用 注释提供者@Singleton
并将提供者结果绑定到请求范围.in(RequestScoped.class)
。如果没有注释,您的提供者将为每个请求实例化,如果它存储有状态数据,这可能很重要。
This is just for clarification as some reader may stumble upon your question and may think the two approaches are sematically equal.
这只是为了澄清,因为有些读者可能会偶然发现您的问题,并可能认为这两种方法在语义上是相同的。