自定义范围,还是更好的方法? [英] Custom Guice Scope, or a better approach?
问题描述
这是我的问题:
首先要知道我正在编写一个模拟。这是一个独立的应用程序,是单线程的。我基本上有两类具有不同范围界定要求的对象。
-
在整个仿真过程中应该被用作单例的类。一个实例的Random,作为一个例子。
-
组合在一起,在组内,每个实例应该被视为单例。例如,说
RootObject
是顶级类,并且依赖于ClassA
和ClassB
,它们都与ClassD
相关联。对于任何给定的RootObject
,它们的两个依赖项(ClassA
和ClassB
)应该依赖于ClassD
的相同实例。但是,ClassD
的实例不应在RootObject
的不同实例中共享。
希望这是有道理的。我可以想到两种方法。一个是将所有注入的对象标记为Singletons,创建根注入器,并在每次需要创建一个新的 RootObject
实例时将子注射器分开。然后, RootObject
及其所有依赖项的实例将创建为Singletons,但下一次创建另一个时,该范围设置信息将被丢弃RootObject
。
第二种方法是实现某种类型的自定义范围。
Guice文档提供了冲突的建议。一方面,它表示你应该有一个注射器,理想情况下,它被称为一次创建一些顶级类。另一方面,它表示远离自定义范围。
在我看来,像你需要一个范围实例 RootObject
及其所有依赖项。
在Guice中,您可以创建自定义范围,例如 @ObjectScoped
,像这样:
@Target({TYPE,METHOD})
/ pre>
@Retention(RUNTIME)
@ScopeAnnotation
public @interface ObjectScoped {}
现在只需放置
RootObject
,A
B
和D
到此范围内:@ObjectScoped
public class RootObject {
private A a;
私人B b;
@Inject
public RootObject(A a,B b){
this.a = a;
this.b = b;
}
public A getA(){
return a;
}
public B getB(){
return b;
}
}
@ObjectScoped
public class A {
private D d;
@Inject
public A(D d){
this.d = d;
}
public D getD(){
return d;
}
}
// B和D
相同现在每个
RootObject
有自己的作用域。您可以将它作为简单的HashMap
实现:public class ObjectScope {
private Map< Key<?>,Object> store = new HashMap< Key<?>,Object>();
@SuppressWarnings(unchecked)
public< T> T get(Key< T> key){
return(T)store.get(key);
}
public< T> void set(Key< T> key,T instance){
store.put(key,instance);
}
}
要将这些范围与Guice集成将需要一个
com.google.inject.Scope
-implementation,您可以在模块中切换范围和相应的布线
。public class GuiceObjectScope实现Scope {
// Make这是一个用于多线程的ThreadLocal。
private ObjectScope current = null;
@Override
public< T>提供商LT; T>范围(最终的Key< T>关键字,最终提供者< T>未标出){
返回新提供者< T>(){
@Override
public T get()
//查找实例
T instance = current.get(key);
if(instance == null){
//创建实例
instance = unscoped.get();
current.set(key,instance);
}
return instance;
}
};
}
public void enter(ObjectScope scope){
current = scope;
}
public void leave(){
current = null;
}
}
public class ExampleModule extends AbstractModule {
private GuiceObjectScope objectScope = new GuiceObjectScope();
@Override
protected void configure(){
bindScope(ObjectScoped.class,objectScope);
//你的绑定
}
public GuiceObjectScope getObjectScope(){
return objectScope;
}
}
初始化您的程序,如下所示: / p>
ExampleModule module = new ExampleModule();
注射器注射器= Guice.createInjector(模块);
GuiceObjectScope objectScope = module.getObjectScope();
创建
RootObject
的第一个实例及其对应范围:ObjectScope obj1 = new ObjectScope();
objectScope.enter(obj1);
RootObject rootObject1 = injector.getInstance(RootObject.class);
objectScope.leave();
只需切换第二组对象的范围:
ObjectScope obj2 = new ObjectScope();
objectScope.enter(obj2);
RootObject rootObject2 = injector.getInstance(RootObject.class);
objectScope.leave();
测试您的要求是否满足:
assert rootObject1!= rootObject2;
assert rootObject1.getA()!= rootObject2.getA();
assert rootObject1.getA()。getD()== rootObject1.getB()。getD();
assert rootObject1.getA()。getD()!= rootObject2.getB()。getD();
要使用一组对象只需输入其范围并使用注射器:
objectScope.enter(obj1);
B b1 = injector.getInstance(B.class);
objectScope.leave();
assert rootObject1.getB()== b1;
Here's my problem:
It's first important to know that I'm writing a simulation. This is a standalone application, and is single-threaded. I have essentially two classes of objects that have different scoping requirements.
Classes that should be used as singletons throughout the entire simulation. An instance of Random, as an example.
Groups of classes that are created together, and within the group, each instance should be treated like a Singleton. For example, say
RootObject
is the top level class, and has a dependency toClassA
andClassB
, both of which have a dependency toClassD
. For any givenRootObject
, both of its dependencies (ClassA
andClassB
) should depend on the same instance ofClassD
. However, instances ofClassD
should not be shared across different instances ofRootObject
.Hopefully that makes sense. I can think of two approaches to this. One is to mark all of the injected objects as Singletons, create the root injector, and spin off a child injector each time I need to create a new
RootObject
instance. Then, the instances ofRootObject
and all of its dependencies are created as Singletons, but that scoping information is thrown away the next time I go to create anotherRootObject
.The second approach is to implement some type of custom scope.
The Guice documentation gives conflicting advice... On one hand, it says that you should have a single injector, and that ideally it is called once to create some top level class. On the other hand, it says to stay away from custom scopes.
解决方案It seems to me like you need a scope for each instance of
RootObject
and all its dependencies.In Guice you can create a custom scope, say
@ObjectScoped
, like this:@Target({ TYPE, METHOD }) @Retention(RUNTIME) @ScopeAnnotation public @interface ObjectScoped {}
Now just place
RootObject
,A
,B
andD
into this scope:@ObjectScoped public class RootObject { private A a; private B b; @Inject public RootObject(A a, B b) { this.a = a; this.b = b; } public A getA() { return a; } public B getB() { return b; } } @ObjectScoped public class A { private D d; @Inject public A(D d) { this.d = d; } public D getD() { return d; } } // The same for B and D
Now each
RootObject
has its own scope. You can implement this as a simpleHashMap
:public class ObjectScope { private Map<Key<?>,Object> store = new HashMap<Key<?>,Object>(); @SuppressWarnings("unchecked") public <T> T get(Key<T> key) { return (T)store.get(key); } public <T> void set(Key<T> key, T instance) { store.put(key, instance); } }
To integrate these scopes with Guice you will need a
com.google.inject.Scope
-implementation which lets you switch the scopes and the corresponding wiring in yourModule
.public class GuiceObjectScope implements Scope { // Make this a ThreadLocal for multithreading. private ObjectScope current = null; @Override public <T> Provider<T> scope(final Key<T> key, final Provider<T> unscoped) { return new Provider<T>() { @Override public T get() { // Lookup instance T instance = current.get(key); if (instance==null) { // Create instance instance = unscoped.get(); current.set(key, instance); } return instance; } }; } public void enter(ObjectScope scope) { current = scope; } public void leave() { current = null; } } public class ExampleModule extends AbstractModule { private GuiceObjectScope objectScope = new GuiceObjectScope(); @Override protected void configure() { bindScope(ObjectScoped.class, objectScope); // your bindings } public GuiceObjectScope getObjectScope() { return objectScope; } }
Initialize your program like this:
ExampleModule module = new ExampleModule(); Injector injector = Guice.createInjector(module); GuiceObjectScope objectScope = module.getObjectScope();
Create the first instance of
RootObject
and its corresponding scope:ObjectScope obj1 = new ObjectScope(); objectScope.enter(obj1); RootObject rootObject1 = injector.getInstance(RootObject.class); objectScope.leave();
Just switch the scope for a second group of objects:
ObjectScope obj2 = new ObjectScope(); objectScope.enter(obj2); RootObject rootObject2 = injector.getInstance(RootObject.class); objectScope.leave();
Test if your requirements are met:
assert rootObject1 != rootObject2; assert rootObject1.getA() != rootObject2.getA(); assert rootObject1.getA().getD() == rootObject1.getB().getD(); assert rootObject1.getA().getD() != rootObject2.getB().getD();
To work with a group of objects just enter its scope and use the injector:
objectScope.enter(obj1); B b1 = injector.getInstance(B.class); objectScope.leave(); assert rootObject1.getB() == b1;
这篇关于自定义范围,还是更好的方法?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!