Search code examples
javadependency-injectionguicecode-injectionassisted-inject

How to Inject an object which is created with Assisted Injection in Guice?


I'm trying to pass an object which has a runtime variable into an other object. How can I achieve this using Guice? I'm new to dependency injection.

I would like to create several A objects (the number of them is decided at runtime) and that many B objects that uses an A object. But first let's start with one object from both of them.

Thank you for your help.

public interface IA {
    String getName();
}

public class A implements IA {
    @Getter
    protected final String name;

    @AssistedInject
    A(@Assisted String name) {
        this.name = name;
    }
}

public interface IAFactory {
    IA create(String name);
}

public interface IB {
    IA getA();
}

public class B implements IB {  
    @Getter
    protected final IA a;

    //...
    // some more methods and fields
    //...

    @Inject
    B(IA a) {
        this.a = a;
    }
}

public class MyModule extends AbstractModule {
    @Override
    protected void configure() {
        install(new FactoryModuleBuilder()
         .implement(IA.class, A.class)
         .build(IAFactory.class));

        bind(IB.class).to(B.class);
    }
}

public class Main() {
    public static void main(String[] args) throws Exception {
        if(args.size < 1) {
            throw new IllegalArgumentException("First arg is required");
        }
        String name = args[0];

        Injector injector = Guice.createInjector(new MyModule());
        IB b = injector.getInstance(IB.class);
        System.out.println(b.getA().getName());
    }
}

Solution

  • I think you are not exactly clear about this. So let me explain a little.

    Firstly, you created a Factory which you will use to create instances of A. You did that because Guice doesn't know the value of parameter name.

    Now what you want is to create an instance of B which depends on instance of A. You are asking Guice to give you an instance of B but how will Guice create an instance of B without an A? You haven't binded any instance of A.

    So to fix this issue you will either have to create an instance of B manually.

    The way you can achieve it is by following.

    Firstly, you will require a Factory for B

    public interface IBFactory {
        IB create(String name);
    }
    

    Then you need to make the following changes in your class B

    public class B implements IB {  
    
        protected final A a;
    
        @AssistedInject
        public B(@Assisted String name, IAFactory iaFactory) {
            this.a = iaFactory.create(name);
        }
    }
    

    Now in your main method

    public static void main(String[] args) throws Exception {
        if(args.size < 1) {
            throw new IllegalArgumentException("First arg is required");
        }
        String name = args[0];
    
        Injector injector = Guice.createInjector(new MyModule());
        IBFactory ibFactory = injector.getInstance(IBFactory.class);
        IB b = ibFactory.create(name)
        System.out.println(b.getA().getName());
    }
    

    Also, don't forget to update your configure method and install B factory.

    protected void configure() {
        install(new FactoryModuleBuilder()
         .implement(IA.class, A.class)
         .build(IAFactory.class));
    
        install(new FactoryModuleBuilder()
         .implement(IB.class, B.class)
         .build(IBFactory.class));
    }
    

    Note I am passing name in class B. You can update IBFactory to take IA as assisted parameter and then first create an instance of IA outside using IAFactory and pass the instance of IA to IBFactory to create an instance of IB