CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-com-google-dagger--dagger

A fast dependency injector for Java and Android that generates plain Java source code without using reflection or runtime bytecode generation

Pending
Overview
Eval results
Files

module-system.mddocs/

Module System

The module system in Dagger defines how dependencies are constructed and provided to the dependency injection container. Modules contain provider methods and binding methods that tell Dagger how to create instances of various types.

Capabilities

@Module Annotation

Annotates classes that contribute bindings to the object graph. Modules can include other modules and declare subcomponents.

/**
 * Annotates classes that contribute bindings to the object graph
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface Module {
    /**
     * Additional modules to include in this module
     */
    Class<?>[] includes() default {};
    
    /**
     * Subcomponent classes that can be created from this module
     */
    Class<?>[] subcomponents() default {};
}

Usage Examples:

// Basic module
@Module
public class DatabaseModule {
    @Provides
    @Singleton
    DatabaseConnection provideConnection() {
        return new DatabaseConnection("localhost", 5432);
    }
}

// Module including other modules
@Module(includes = {NetworkModule.class, CacheModule.class})
public class AppModule {
    @Provides
    AppService provideAppService(ApiClient client, Cache cache) {
        return new AppService(client, cache);
    }
}

// Module with subcomponents
@Module(subcomponents = UserSubcomponent.class)
public class ApplicationModule {
    // Module bindings...
}

@Provides Annotation

Annotates methods in modules to create provider method bindings. The method's return type is bound to the value returned by the method.

/**
 * Annotates methods in modules to create provider method bindings
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@interface Provides {}

Key Features:

  • Method return type becomes the bound type
  • Method parameters are automatically injected by Dagger
  • Supports scoping annotations (@Singleton, @Reusable, etc.)
  • Supports qualifier annotations (@Named, custom qualifiers)
  • Can return null if annotated with @Nullable

Usage Examples:

@Module
public class NetworkModule {
    // Simple provider
    @Provides
    @Singleton
    OkHttpClient provideHttpClient() {
        return new OkHttpClient.Builder()
            .connectTimeout(30, TimeUnit.SECONDS)
            .build();
    }
    
    // Provider with dependencies
    @Provides
    ApiService provideApiService(OkHttpClient client, Gson gson) {
        return new Retrofit.Builder()
            .baseUrl("https://api.example.com")
            .client(client)
            .addConverterFactory(GsonConverterFactory.create(gson))
            .build()
            .create(ApiService.class);
    }
    
    // Qualified provider
    @Provides
    @Named("api-key")
    String provideApiKey() {
        return BuildConfig.API_KEY;
    }
    
    // Nullable provider
    @Provides
    @Nullable
    CacheConfig provideCacheConfig() {
        return BuildConfig.DEBUG ? null : new CacheConfig();
    }
}

@Binds Annotation

Annotates abstract methods in modules for delegation bindings. More efficient than @Provides methods that just return an injected parameter.

/**
 * Annotates abstract methods in modules for delegation bindings
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@interface Binds {}

Key Features:

  • Must be abstract method in abstract module class
  • Single parameter that is assignable to return type
  • More efficient than equivalent @Provides method
  • Supports scoping and qualification
  • Works with multibindings (@IntoSet, @IntoMap)

Usage Examples:

@Module
public abstract class RepositoryModule {
    // Bind interface to implementation
    @Binds
    abstract UserRepository bindUserRepository(UserRepositoryImpl impl);
    
    // Bind with qualifier
    @Binds
    @Named("local")
    abstract DataSource bindLocalDataSource(LocalDataSource impl);
    
    // Bind into set
    @Binds
    @IntoSet
    abstract Validator bindEmailValidator(EmailValidator impl);
    
    // Bind into map
    @Binds
    @IntoMap
    @StringKey("user")
    abstract Repository bindUserRepository(UserRepository impl);
    
    // Can mix with @Provides in same module
    @Provides
    static Database provideDatabase() {
        return Room.databaseBuilder(...)
            .build();
    }
}

@BindsOptionalOf Annotation

Declares bindings for Optional containers of values that may or may not be present in the dependency graph.

/**
 * Annotates abstract methods that declare optional bindings
 * @Beta This API is subject to incompatible changes
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@interface BindsOptionalOf {}

Key Features:

  • Must be abstract method with no parameters
  • Returns Optional<T> if binding is present, Optional.empty() otherwise
  • Supports Optional<Provider<T>>, Optional<Lazy<T>>, Optional<Provider<Lazy<T>>>
  • Cannot bind @Nullable types to Optional<T> (compile error)

Usage Examples:

@Module
public abstract class OptionalModule {
    // Declare optional binding
    @BindsOptionalOf
    abstract Optional<FeatureFlag> optionalFeatureFlag();
    
    // Optional provider
    @BindsOptionalOf 
    abstract Optional<Provider<ExpensiveService>> optionalExpensiveService();
}

// Usage in injected class
public class FeatureService {
    private final Optional<FeatureFlag> featureFlag;
    
    @Inject
    public FeatureService(Optional<FeatureFlag> featureFlag) {
        this.featureFlag = featureFlag;
    }
    
    public boolean isFeatureEnabled() {
        return featureFlag.map(FeatureFlag::isEnabled).orElse(false);
    }
}

Module Composition

Modules can include other modules to compose larger binding graphs.

// Base modules
@Module
public class NetworkModule {
    @Provides @Singleton
    OkHttpClient provideHttpClient() { /* ... */ }
}

@Module  
public class DatabaseModule {
    @Provides @Singleton
    Database provideDatabase() { /* ... */ }
}

// Composed module
@Module(includes = {NetworkModule.class, DatabaseModule.class})
public class AppModule {
    @Provides
    AppService provideAppService(OkHttpClient client, Database database) {
        return new AppService(client, database);
    }
}

Static Provider Methods

Provider methods can be static for better performance when they don't need instance state.

@Module
public abstract class UtilityModule {
    @Provides
    static Gson provideGson() {
        return new GsonBuilder()
            .setDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'")
            .create();
    }
    
    @Provides
    static ExecutorService provideExecutorService() {
        return Executors.newFixedThreadPool(4);
    }
}

Module Validation

Dagger performs compile-time validation of modules:

  • All dependencies of provider methods must be satisfiable
  • Abstract modules with @Binds methods cannot be instantiated directly
  • Circular dependencies are detected and reported
  • Missing bindings cause compilation errors
  • Conflicting bindings (same type from multiple sources) cause errors

Module Best Practices

Granular Modules:

// Good: Specific responsibility
@Module
public class NetworkModule { /* ... */ }

@Module  
public class DatabaseModule { /* ... */ }

// Avoid: Monolithic module
@Module
public class EverythingModule { /* ... */ }

Prefer @Binds over @Provides:

// Preferred: More efficient
@Binds
abstract UserRepository bindUserRepository(UserRepositoryImpl impl);

// Less efficient
@Provides
UserRepository provideUserRepository(UserRepositoryImpl impl) {
    return impl;
}

Install with Tessl CLI

npx tessl i tessl/maven-com-google-dagger--dagger

docs

assisted-injection.md

component-framework.md

index.md

module-system.md

multibindings.md

utility-types.md

tile.json