burak emre burak emre - 1 year ago 212
Java Question

Fail-safe injector for child modules. Optional bindings in google guice

I have a child module called ChildPlugin and I inject classes from main module as follows:

public class ChildPlugin {
private ExampleClass demo;

@Inject
public void setDemo(ExampleClass demo) {
this.demo = demo;
}
}


The problem is that I don't know whether the main module binds
ExampleClass
and if it's not Guice throws an exception when creating the injector. What I want to do is to make Guice pass
null
or
Optional.empty
if ExampleClass is not binded.

I do not have access to the main module so I cannot change binder for
ExampleClass
to
OptionalBinder
, I tried
@Nullable
and
Optional<ExampleClass>
in
ChildPlugin.setDemo
method but it didn't work.

Answer Source

there are 2 ways to do this.

Optional injection

Use the com.google.inject.Inject annotation. This one allows you to specify optional on the annotation. See this example:

public class GuiceInjectOptional extends AbstractModule {

    @Override
    protected void configure() {

        // method 1: 
        bind(B.class).in(Singleton.class);

    }

    public static class A {

        private String name;
        // non null constructor so that A can't be instantiated automatically by guice
        public A(String name) {
            this.name = name;
        }

        @Override
        public String toString() {
            return "I am: " + name;
        }
    }

    public static class B {

        @Inject(optional=true)
        A obj;

        void run() {
            System.out.println("Object is: " + obj);
        }
    }

    public static void main(String[] args) {
        Injector injector = Guice.createInjector(new GuiceInjectOptional());
        injector.getInstance(B.class).run();;
    }

}

The annotation on class B indicates that A is optional. It does not get injected. Running the snippet prints:

Object is: null

Method 2 (which is the way you'd do it after guice 4+). You can specify optional bindings. These bindings even allow you to define default values if you like. You can then inject an optional value like in this snippet I wrote up:

public class GuiceInjectOptional extends AbstractModule {

    @Override
    protected void configure() {
        // set up optional binding for A.
        OptionalBinder.newOptionalBinder(binder(), A.class);

        bind(B.class).in(Singleton.class);
    }

    public static class A {

        private String name;
        // non null constructor so that A can't be instantiated automatically by guice
        public A(String name) {
            this.name = name;
        }

        @Override
        public String toString() {
            return "I am: " + name;
        }
    }

    public static class B {

        @Inject
        Optional<A> obj;

        void run() {
            System.out.println("Object is present: " + obj.isPresent());
            System.out.println("Object is: " + obj);
        }
    }

    public static void main(String[] args) {
        Injector injector = Guice.createInjector(new GuiceInjectOptional());
        injector.getInstance(B.class).run();;
    }

}

The Inject annotation is now non-optional, but guice is aware that class A may or may not have been bound. Running the snippet will print:

Object is present: false
Object is: Optional.empty

Finally, you can then just bind A normally and guice will inject it:

public class GuiceInjectOptional extends AbstractModule {

    @Override
    protected void configure() {
        // set up optional binding for A.
        OptionalBinder.newOptionalBinder(binder(), A.class);

        bind(A.class).toInstance(new A("Pandaa!"));
        bind(B.class).in(Singleton.class);
    }

    public static class A {

        private String name;
        // non null constructor so that A can't be instantiated automatically by guice
        public A(String name) {
            this.name = name;
        }

        @Override
        public String toString() {
            return "I am: " + name;
        }
    }

    public static class B {

        @Inject
        Optional<A> obj;

        void run() {
            System.out.println("Object is present: " + obj.isPresent());
            System.out.println("Object is: " + obj);
        }
    }

    public static void main(String[] args) {
        Injector injector = Guice.createInjector(new GuiceInjectOptional());
        injector.getInstance(B.class).run();;
    }

}

And the above will print:

Object is present: true
Object is: Optional[I am: Pandaa!]

And this is how you have fail safe optional bindings with guice :) I hope this helps.

Edit: I just saw the guice-3 tag, so you'll want to use the optional annotation method rather than the optional binder. With the optional annotation it will stay null instead of being an Optional value.

Artur

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download