Alex
Alex

Reputation: 81

Guice - Field Injection limitations

Suppose that we have a class, called Bean, that depends on another class, called Service, using Fields Injection and we can’t change the code of these classes. Another class (App) uses a Provider in order to construct new instances of the Bean whenever it wants and we are free to change the code in these classes.

Since in the Provider's get() method the new instance is created using the new operator it is impossible to inject the Service into the Bean, thus the Bean instance will be incomplete (Service field will be null).

One way to bypass the problem would be to inject the Injector in the Provider and call injector.injectMembers() in the get() method, on the new instance.

An example of the code is the following (I avoided using interfaces to make the code shorter).

class Bean {
    @Inject private Service service;

    public Bean() {
    }

    public void bar() {
        this.service.foo();
    }
}

class Service {

    public void foo() {
        System.out.println("foo");
    }
}

class App {
    @Inject private Provider<Bean> beanProvider;

    public App() {
    }

    public void run() {
        this.beanProvider.get().bar();
    }
}

class BeanProvider implements Provider<Bean> {
    @Inject Injector injector;

    public Bean get() {
        Bean mybean = new Bean();
        this.injector.injectMembers(mybean);

        return mybean;
    }
}

// A simple module to bind the classes
class MyModule extends AbstractModule {

    @Override
    protected void configure() {
        bind(Service.class);
        bind(Bean.class).toProvider(BeanProvider.class);
        bind(App.class);
    }
}


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

The other way would be to change the Bean to use Construction Injection, but as I said let's assume that we can't do that.

I know that most people claim that injecting the Injector should be avoided as it breaks the whole DI philosophy since the injector should be used only at the entry point of an application and nowhere else, but since Guice offers different ways to use DI in an app, such as Field Injection, situations like the one I described may occur. Also, I've seen Field Injection to be used pretty often in several projects so cases like this can be often.

So, is there a "cleaner" way to avoid injecting the Injector in the problem I described?

Upvotes: 1

Views: 3853

Answers (1)

the baldheadedguy
the baldheadedguy

Reputation: 183

This seems like it should be a non-issue with your module. It doesn't look like you should have to create a provider at all.

The bindings that you've setup in your module should cause Service to be injected into Bean wherever you need it. So, you should just be able to replace the provider declaration in App with an injection of Bean.

class App {
     @Inject private Bean beanObject;

     public App() {
     }

     public void run() {
         this.beanObject.bar();
     }
}

// A simple module to bind the classes
class MyModule extends AbstractModule {
    @Override
    protected void configure() {
        //bind this to whatever you want to inject into Bean...
        bind(Service.class); 
        bind(Bean.class);
        bind(App.class);
    }
}

If that doesn't work for you, can you clarify what you're trying to do and why you need a provider?

Upvotes: 4

Related Questions