Injecting a dependency into Guice Module

13,226

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.

Share:
13,226
a p
Author by

a p

Startup survivor, ex-consultant, ex-AWS t1 svcs, ex-AWS biz dev, currently sr. engineering at a non-faang. Background in (theoretical, generative) linguistics.

Updated on July 18, 2022

Comments

  • a p
    a p almost 2 years

    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.