Search code examples
javadependency-injectionannotationsguiceassisted-inject

Guice inject different instance based on the parent instance annotation


I am using guice framework in my application. I have a scenario where a single class might require multiple instances of same interface C (but for different purposes) as shown in the example. I am trying to resolve this using the annotation facility in guice.

As shown in the example below, I want the config for ConcreteImpl also to be injected by guice. But the problem is the config for type1, type2 and type3 instances could be different. Assuming that I have the configurations for these instances apriori, is there a facility to inject them according to the context (denoted by annotation) of instance requesting the config?

    class A {
        @Inject
        public A(@Purpose1 C type1, @Purpose2 C type2, @Purpose3 C type3) {

        }
    }

    interface C {}

    class ConcreteImpl implements C {
        @Inject
        public ConcreteImpl(ConcreteImplConfig config) {}
    }

    class ConcreteImplConfig {
        String pty1;
        String pty2;
    }  

My module binding is like this -

    bind(C.class)
            .annotatedWith(Purpose1.class)
            .to(purpose1Cklass/**obtained from config**/);

    bind(C.class)
            .annotatedWith(Purpose2.class)
            .to(purpose2Cklass/**obtained from config**/);

    bind(C.class)
            .annotatedWith(Purpose3.class)
            .to(purpose3Cklass/**obtained from config**/);

And this is pretty much what I want to do

    bind(ConcreteImplConfig.class)
            .requestedThrough(Purpose1.class)
            .toInstance(purpose1config);

    bind(ConcreteImplConfig.class)
            .requestedThrough(Purpose2.class)
            .toInstance(purpose2config);

    bind(ConcreteImplConfig.class)
            .requestedThrough(Purpose3.class)
            .toInstance(purpose3config);

I already had a look at assisted inject, which can inject a factory and then we use factory.create(config) but I am not inclined to that as the contracts tend to become slightly uglier and all the more i have all the configs at the start of my application and should be able to inject them.


Solution

  • This is the Robot Leg Problem. You need to create a private module for C.

    abstract class CModule extends PrivateModule {
        private final Class<? extends Annotation> annotation;
    
        CModule(Class<? extends Annotation> annotation) {
            this.annotation = annotation;
        }
    
        @Override protected void configure() {
            bind(C.class).annotatedWith(annotation).to(C.class);
            expose(C.class).annotatedWith(annotation);
    
            bindConfig();
        }
    
        abstract void bindConfig();
    }
    
    public static void main(String[] args) {
            Injector injector = Guice.createInjector(
                    new CModule(Propsal1.class) {
                        @Override void bindConfig() {
                            bind(ConcreteImplConfig.class).toInstance(new ConcreteImplConfig());
                        }
                    },
                    new CModule(Propsal2.class) {
                        @Override void bindConfig() {
                            bind(ConcreteImplConfig.class).toInstance(new ConcreteImplConfig());
                        }
                    },
                    new CModule(Propsal2.class) {
                        @Override void bindConfig() {
                            bind(ConcreteImplConfig.class).toInstance(new ConcreteImplConfig());
                        }
                    }
                    );
        }