自定义范围,还是更好的方法? [英] Custom Guice Scope, or a better approach?

查看:146
本文介绍了自定义范围,还是更好的方法?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这是我的问题:



首先要知道我正在编写一个模拟。这是一个独立的应用程序,是单线程的。我基本上有两类具有不同范围界定要求的对象。


  1. 在整个仿真过程中应该被用作单例的类。一个实例的Random,作为一个例子。


  2. 组合在一起,在组内,每个实例应该被视为单例。例如,说 RootObject 是顶级类,并且依赖于 ClassA ClassB ,它们都与 ClassD 相关联。对于任何给定的 RootObject ,它们的两个依赖项( ClassA ClassB )应该依赖于 ClassD 的相同实例。但是, ClassD 的实例不应在 RootObject 的不同实例中共享。


希望这是有道理的。我可以想到两种方法。一个是将所有注入的对象标记为Singletons,创建根注入器,并在每次需要创建一个新的 RootObject 实例时将子注射器分开。然后, RootObject 及其所有依赖项的实例将创建为Singletons,但下一次创建另一个时,该范围设置信息将被丢弃RootObject



第二种方法是实现某种类型的自定义范围。



Guice文档提供了冲突的建议。一方面,它表示你应该有一个注射器,理想情况下,它被称为一次创建一些顶级类。另一方面,它表示远离自定义范围。

解决方案

在我看来,像你需要一个范围实例 RootObject 及其所有依赖项。



在Guice中,您可以创建自定义范围,例如 @ObjectScoped ,像这样:

  @Target({TYPE,METHOD})
@Retention(RUNTIME)
@ScopeAnnotation
public @interface ObjectScoped {}
/ pre>

现在只需放置 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.

  1. Classes that should be used as singletons throughout the entire simulation. An instance of Random, as an example.

  2. 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 to ClassA and ClassB, both of which have a dependency to ClassD. For any given RootObject, both of its dependencies (ClassA and ClassB) should depend on the same instance of ClassD. However, instances of ClassD should not be shared across different instances of RootObject.

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 of RootObject and all of its dependencies are created as Singletons, but that scoping information is thrown away the next time I go to create another RootObject.

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 and D 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 simple 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);
    }

}

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 your Module.

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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆