How to pass an object to a class that will be injected later

142 views Asked by At

I am pretty new to google guice .

I am writing a nexus plugin where nexus injects some classes . for example say the class to be injected in class A.

Now before that class A is injected, I have another class B that was instantiated and inside it I have a method where an object (say obj) is initialised .

I have to pass this initialised object to the class A.

Normally if instantiation is under our control I will do as

A a = new A();

A.setObject(obj);

But now given that the class will be injected by the system , I don't know how to pass this initialised object to this class A.

1

There are 1 answers

0
Vladimir Matveev On

If I understand correctly, you have something like

public class C {
}

public class A {
    private C c;

    public void setC(C c) {
        this.c = c;
    }
}

public class B {
    private final C c;

    public B() {
        this.c = new C();
    }

    public C getC() {
        return this.c;
    }
}

public class Main {
    public static void main(String[] args) {
        B b = new B();
        A a = new A();

        C c = b.getC();
        a.setC(c);
    }
}

This design does not follow DI principles and should be refactored. You should let Guice create your C instance. In that case you will have something like

public class C {
}

public class A {
    private final C c;

    @Inject
    A(C c) {
        this.c = c;
    }
}

public class B {
    private final C c;

    @Inject
    B(C c) {
        this.c = c;
    }
}

public class Main {
    public static void main(String[] args) {
        Injector injector = Guice.createInjector();
        A a = injector.getInstance(A.class);
        B b = injector.getInstance(B.class);
    }
}

And here you have C automatically injected both into A and B.

If you really cannot refactor you code, consider using providers then:

public class AProvider extends Provider<A> {
    private final B b;

    @Inject
    AProvider(B b) {
        this.b = b;
    }

    @Override
    public A get() {
        A a = new A();
        C c = b.getC();
        a.setC(c);
        // Or, better
        // A a = new A(b.getC());
        return a;
    }
}

public class Main {
    public static void main(String[] args) {
        Injector injector = Guice.createInjector(new AbstractModule() {
            @Override
            protected void configure() {
                bind(A.class).toProvider(AProvider.class);
            }
        });
        A a = injector.getInstance(A.class);
    }
}

In this case a provider is used to create A and set its C dependency.

But sometimes even that is not enough (e.g. when your B.getC() behavior depends on user input). In that case you have to use assisted inject extension:

public interface AFactory {
    public A create(C c);
}

public class A {
    private final C c;
    private final Other other;

    @Inject
    A(@Assisted C c, Other other) {
        this.c = c;
        this.other = other;
    }
}

public class Main {
    public static void main(String[] args) {
        Injector injector = Guice.createInjector(new AbstractModule() {
            @Inject
            protected void configure() {
                install(new FactoryModuleBuilder()
                    .build(AFactory.class));
            }
        });

        B b = injector.getInstance(B.class);
        C c = b.getC(someUserInput);
        AFactory af = injector.getInstance(AFactory.class);
        A a = af.create(c);
    }
}

In the last example two object will be injected into A via af.create(c) invocation: first, that c you have provided, and second, and instance of Other class which is resolved automatically by Guice. In other words, assisted inject allows you to instantiate classes which will have one part of their dependencies resolved by you, and other part - by Guice.