Search code examples
guice

Google Guice: expose a named object but keep it available within the module without a name


My application is built using Guice and MyBatis. Different database connections are represented using javax.sql.DataSource. All classes that need access to a DataSource are declared within the same Guice PrivateModule that provides this DataSource as well. However, these modules grow over time and are harder to manage. In addition, I would like to be able to tie different DAO and their related classes into a separate Guice module and provide that module with a data source so the above module is better encapsulated and reusable with different data sources.

Technically speaking I would like to be able to write something like:

public class MyDataSourceModule extends PrivateModule {

   @Override
   protected void configure() {}

   @Exposed
   @Named("systemDataSource")
   @Singleton
   @Provides
   DataSource provideDataSource() {
       return ...;
   }
}

such that the DataSource will be still available without a name within the module but only with the name outside the module. Annotations can be changed as needed. Is that possible and how?


Solution

  • What you want is possible by creating two different bindings in the same module.

    (Side note: if you're not familiar with BindingAnnotations, you can find out more here. In my examples, I will be using @UsingSystemDataSource as the binding annotation.)

    Using EDSL in the configure() method:

    protected void configure() {
        // binding for private use
        bind(DataSource.class).to(SystemDataSourceImpl.class).in(Scopes.SINGLETON);
    
        // binding for public use
        bind(DataSource.class).annotatedWith(@UsingSystemDataSource.class).to(DataSource.class);
        expose(DataSource.class).annotatedWith(@UsingSystemDataSource.class);
    }
    

    Now, if you need to do this without using EDSL, then your private module @Provides methods would look like this:

    @Provides
    @Exposed
    @UsingSystemDataSource
    DataSource provideDataSourcePublicly(DataSource privatelyBoundDatasource) {
        return privatelyBoundDatasource;
    }
    
    @Provides
    @Singleton
    DataSource provideDataSource() {
        return ...;
    }
    

    Why does this even work?

    This actually creates two different bindings—one linking DataSource to SystemDataSource and another linking @UsingSystemDataSource DataSource to DataSource. Using the expose() method or @Exposed, the private module exposes only the annotated version of the binding. (I couldn't find a source that explicitly states that it works for this use case, so I tested it myself.)