Guice不仅 会 为同一注射器提供相同的单线程跨线程,而且如果您使用,Guice 只能
提供相同的单线程跨线程
toInstance。每个进样器对模块进行一次评估,您给了Guice一个实例,而没有办法再生产第二个。
Guice不是魔术。尝试提供Object的实例时,它要么需要(1)一个Guice友好的无参数或带有
@Inject注释的构造函数;(2)
Provider或
@Provides方法,让您自己创建实例;或(3)您已经创建并与绑定的实例,
toInstanceGuice重用了它,因为它不知道如何创建另一个实例。请注意,带有的选项2
Provider并不需要保证每次都创建一个新实例,我们可以利用它来编写
Provider具有ThreadLocal缓存的。它看起来像这样:
public class CacheModule extends AbstractModule { @Override protected void configure() {} private ThreadLocal<WidgetCache> threadWidgetCache = new ThreadLocal<>() { @Override protected WidgetCache initialValue() { return new WidgetCache(...lots of params); } }; @Provides WidgetCache provideWidgetCache() { return threadWidgetCache.get(); }}当然,如果要对多个对象执行此操作,则必须为要缓存的每个键编写一个ThreadLocal,然后为每个键创建一个提供程序。这似乎有点多余,这就是自定义作用域引入的地方。
创建自己的ThreadLocal范围
查看Scope唯一有意义的方法:
public <T> Provider<T> scope(Key<T> key, Provider<T> unscoped);
从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()); } @Provides @ThreadScoped WidgetCache provideWidgetCache() { return new WidgetCache(...lots of params); }}记住,单例行为并不取决于您在哪个线程中创建模块,而是取决于您要问的是哪个注入器。如果创建了五个不相关的
Injector实例,则每个实例将具有自己的单例。如果您只是尝试以多线程方式运行小型算法,则可以为每个线程创建自己的注入器,但是那样您将失去创建跨线程的单例对象的机会。



