Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Injecting a dependency into Guice Module

I have a module that acquires and holds an API token (simplified):

@Singleton
public class KeyHolderModule extends AbstractModule {
    // This doesn't seem to be injected
    private @Inject TokenConnector connector;
    private DateTime keyLastRefreshed;
    private String key;
    private Credentials creds = config.getCreds();

    @Override protected void configure() {
        this.key = connector.getToken(creds);
        this.keyLastRefreshed = DateTime.now();
    }

    @Provides @Named("apiKey") public String getKey() {
        // logic to check key last refreshed and handle generating a new one
        return this.key;
    }
}

I get a null pointer error on the line where I try to access the connector (this.key = connector.getToken(creds);), so the connector is obviously not getting wired up correctly.

I've tried creating a constructor and using @Inject there, but I'm manually adding these modules via new to a list in my app bootstrap class, so that's sort of out.

Obviously I'm missing something here -- I could probably just new up a TokenConnector in this case since it doesn't have any dependencies itself, but that wouldn't fix my fundamental failure to grasp what's happening here. So if you want to see (simplified) other pieces of code, or less simplified pieces of this code, let me know.

like image 643
a p Avatar asked Sep 27 '16 21:09

a p


1 Answers

Though you can't use @Inject for a Module (unless you get the Module from another Injector, which I recommend strongly against), you can easily inject into a @Provides method.

public class KeyHolderModule extends AbstractModule {
    private DateTime keyLastRefreshed;
    private String key;
    private Credentials creds = config.getCreds();

    @Override protected void configure() {}

    @Provides @Named("apiKey") public String getKey(
            TokenConnector connector) {
        // logic to check key last refreshed and handle generating a new one
        this.key = connector.getToken(creds);
        this.keyLastRefreshed = DateTime.now();

        return this.key;
    }
}

The trick here is that a Module is typically manually instantiated at injector creation time, but @Provides methods are invoked when the dependencies they provide are needed. Consequently, the Injector isn't ready to provide anything when the Module is constructed, but @Provides methods invoked throughout the application lifecycle have access to whatever other injector-provided dependencies they might need. When configure is run the Injector is not yet created, the best you can do is call getProvider (though you can't call get on those until the Injector is ready).

I wrote up a variety of other in-Module injection techniques as this SO answer.

like image 92
Jeff Bowman Avatar answered Sep 20 '22 12:09

Jeff Bowman