IAm*_*aja 5 java singleton dependency-injection guice
我对Guice以及它的单身人士是否会服从我可能尝试设置的线程限制表示关注:
public class CacheModule extends AbstractModule {
@Override
protected void configure() {
// WidgetCache.class is located inside a 3rd party JAR that I
// don't have the ability to modify.
WidgetCache widgetCache = new WidgetCache(...lots of params);
// Guice will reuse the same WidgetCache instance over and over across
// multiple calls to Injector#getInstance(WidgetCache.class);
bind(WidgetCache.class).toInstance(widgetCache);
}
}
// CacheAdaptor is the "root" of my dependency tree. All other objects
// are created from it.
public class CacheAdaptor {
private CacheModule bootstrapper = new CacheModule();
private WidgetCache widgetCache;
public CacheAdaptor() {
super();
Injector injector = Guice.createInjector(bootstrapper);
setWidgetCache(injector.getInstance(WidgetCache.class));
}
// ...etc.
}
Run Code Online (Sandbox Code Playgroud)
正如您所看到的,每当我们创建一个新实例时CacheAdaptor,CacheModule都会使用它来引导它下面的整个依赖关系树.
如果new CacheAdaptor();从多个线程内部调用会发生什么?
例如:Thread#1 CacheAdaptor通过其no-arg构造函数创建一个new ,而Thread#2执行相同的操作.Guice会WidgetCache为每个线程提供相同的实例CacheAdaptor,还是Guice会为每个线程提供2个不同的实例?即使toInstance(...)应该返回相同的单例实例,我也希望 - 因为模块是在2个不同的线程内创建的 - 每个都CacheAdaptor将接收不同的WidgetCache实例.
提前致谢!
Jef*_*ica 22
Guice不仅会为同一个注入器提供跨线程的相同单例,而且Guice 只能在你使用的线程中提供相同的单例toInstance.模块每个注入器评估一次,你给Guice一个实例,没有办法产生第二个实例.
Guice并不神奇.当试图提供一个Object的实例时,它需要(1)一个Guice友好的无参数或@Inject注释构造函数; (2)a Provider或@Provides方法,让你自己创建实例; 或者(3)你已经创建并绑定的实例,toInstanceGuice重用它,因为它不知道如何创建另一个.注意,带有a的选项2 Provider不需要保证它每次都创建一个新实例,我们可以利用它来编写Provider具有ThreadLocal缓存的实例.它看起来像这样:
public class CacheModule extends AbstractModule {
/** This isn't needed anymore; the @Provides method below is sufficient. */
@Override protected void configure() {}
/** This keeps a WidgetCache per thread and knows how to create a new one. */
private ThreadLocal<WidgetCache> threadWidgetCache = new ThreadLocal<>() {
@Override protected WidgetCache initialValue() {
return new WidgetCache(...lots of params);
}
};
/** Provide a single separate WidgetCache for each thread. */
@Provides WidgetCache provideWidgetCache() {
return threadWidgetCache.get();
}
}
Run Code Online (Sandbox Code Playgroud)
当然,如果要为多个对象执行此操作,则必须为要缓存的每个键编写ThreadLocal,然后为每个键创建一个提供程序.这似乎有点过分,这就是自定义范围的来源.
查看Scope唯一有意义的方法:
/**
* Scopes a provider. The returned provider returns objects from this scope.
* If an object does not exist in this scope, the provider can use the given
* unscoped provider to retrieve one.
*
* <p>Scope implementations are strongly encouraged to override
* {@link Object#toString} in the returned provider and include the backing
* provider's {@code toString()} output.
*
* @param key binding key
* @param unscoped locates an instance when one doesn't already exist in this
* scope.
* @return a new provider which only delegates to the given unscoped provider
* when an instance of the requested object doesn't already exist in this
* scope
*/
public <T> Provider<T> scope(Key<T> key, Provider<T> unscoped);
Run Code Online (Sandbox Code Playgroud)
正如你在Scope接口中看到的那样,一个作用域只是一个装饰器Provider,并且作用于某个线程 - 本地等同于返回一个ThreadLocal-cached副本(如果它存在)或缓存并从传递返回(Provider如果它没有).因此,我们可以轻松编写一个范围,该范围与我们上面手动执行的逻辑相同.
实际上,需要为每个线程创建一个新的FooObject(对于FooObject的任何值)是一个常见的请求 - 对于基础库来说太多的"高级功能",但通常足以让它成为如何的示例写一个自定义范围.要根据需要调整SimpleScope示例,可以省略scope.enter()和scope.exit()调用,但保持ThreadLocal<Map<Key<?>, Object>>充当对象的线程本地缓存.
那时,假设您已经@ThreadScoped使用ThreadScope已编写的实现创建了自己的注释,则可以将模块调整为如下所示:
public class CacheModule extends AbstractModule {
@Override
protected void configure() {
bindScope(ThreadScoped.class, new ThreadScope());
}
/** Provide a single separate WidgetCache for each thread. */
@Provides @ThreadScoped WidgetCache provideWidgetCache() {
return new WidgetCache(...lots of params);
}
}
Run Code Online (Sandbox Code Playgroud)
请记住,单例行为不依赖于您创建模块的线程,而是依赖于您要求的注入器.如果您创建了五个不相关的Injector实例,则每个实例都有自己的单例.如果您只是尝试以多线程方式运行一个小算法,那么您可以为每个线程创建自己的注入器,但这样您就不会有机会制作跨越线程的单个对象.