Custom Guice Scope, or a better approach?

后端 未结 4 1479
迷失自我
迷失自我 2020-12-13 14:55

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

4条回答
  •  爱一瞬间的悲伤
    2020-12-13 15:43

    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,Object> store = new HashMap,Object>();
    
        @SuppressWarnings("unchecked")
        public  T get(Key key) {
            return (T)store.get(key);
        }
    
        public  void set(Key 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  Provider scope(final Key key, final Provider unscoped) {
            return new Provider() {
    
                @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;
    

提交回复
热议问题