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

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

问题描述

这是我的问题:

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

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. 在整个模拟过程中应该用作单例的类.以 Random 的一个实例为例.

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

一起创建的类组,在组内,每个实例都应该被视为单例.例如,假设 RootObject 是顶级类,并且依赖于 ClassAClassB,两者都依赖于 ClassD.对于任何给定的 RootObject,它的两个依赖项(ClassAClassB)都应该依赖于 ClassD 的同一个实例.但是,ClassD 的实例不应在 RootObject 的不同实例之间共享.

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.

希望这是有道理的.我可以想到两种方法.一种是将所有注入的对象标记为单例,创建根注入器,并在每次我需要创建一个新的 RootObject 实例时分离一个子注入器.然后,RootObject 的实例及其所有依赖项都创建为单例,但下次我去创建另一个 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.

Guice 文档给出了相互矛盾的建议......一方面,它说你应该有一个注入器,理想情况下它被调用一次以创建一些顶级类.另一方面,它说要远离自定义范围.

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.

推荐答案

在我看来,您需要为 RootObject 的每个实例及其所有依赖项设置一个范围.

It seems to me like you need a scope for each instance of RootObject and all its dependencies.

在 Guice 中,您可以创建自定义范围,例如 @ObjectScoped,如下所示:

In Guice you can create a custom scope, say @ObjectScoped, like this:

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

现在只需将 RootObjectABD 放入此范围:

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

现在每个 RootObject 都有自己的作用域.您可以将其实现为一个简单的 HashMap:

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);
    }

}

要将这些范围与 Guice 集成,您需要一个 com.google.inject.Scope 实现,它允许您在 Module 中切换范围和相应的接线.

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();

创建RootObject的第一个实例及其对应的作用域:

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;

这篇关于自定义 Guice 范围,还是更好的方法?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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