CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-com-google-inject--guice

Lightweight dependency injection framework for Java 8 and above that eliminates factories and the use of 'new' through @Inject annotation

Pending
Overview
Eval results
Files

annotations.mddocs/

Essential Annotations

Key annotations for marking injection points, scoping instances, and creating binding qualifiers.

Capabilities

@Inject Annotation

Marks members (constructors, methods, fields) for dependency injection.

/**
 * Annotates members of your implementation class (constructors, methods,
 * and fields) into which the Injector should inject values.
 */
@Target({METHOD, CONSTRUCTOR, FIELD})
@Retention(RUNTIME)
public @interface Inject {
    /**
     * If true, and the appropriate binding is not found,
     * the Injector will skip injection of this method or field
     * rather than produce an error.
     * @return true if injection is optional
     */
    boolean optional() default false;
}

Usage Examples:

public class UserService {
    // Field injection
    @Inject
    private DatabaseService databaseService;
    
    // Optional field injection
    @Inject(optional = true)
    private CacheService cacheService;
    
    // Constructor injection (recommended)
    @Inject
    public UserService(DatabaseService databaseService, LoggingService logger) {
        this.databaseService = databaseService;
        this.logger = logger;
    }
    
    // Method injection
    @Inject
    public void setConfiguration(Configuration config) {
        this.config = config;
    }
    
    // Optional method injection
    @Inject(optional = true)
    public void setMetrics(MetricsService metrics) {
        this.metrics = metrics; // Only called if binding exists
    }
}

@Singleton Annotation

Scope annotation ensuring only one instance per Injector.

/**
 * Apply this to implementation classes when you want only one instance
 * (per Injector) to be reused for all injections.
 */
@Target({TYPE, METHOD})
@Retention(RUNTIME)
@ScopeAnnotation
public @interface Singleton {}

Usage Examples:

// Singleton class
@Singleton
public class DatabaseConnectionPool {
    private final List<Connection> connections;
    
    @Inject
    public DatabaseConnectionPool(DatabaseConfig config) {
        this.connections = createConnections(config);
    }
}

// Singleton binding in module
public class DatabaseModule extends AbstractModule {
    @Override
    protected void configure() {
        bind(DatabaseService.class).to(PostgreSQLService.class).in(Singleton.class);
    }
}

// Singleton provider method
public class ConfigModule extends AbstractModule {
    @Provides
    @Singleton
    Configuration provideConfiguration() {
        return Configuration.load("app.properties");
    }
}

@Provides Annotation

Marks methods in Modules as provider methods for creating bindings.

/**
 * Annotates methods in Modules to create bindings. The method's return type
 * is bound to its returned value. Guice will pass dependencies to the method
 * as parameters.
 */
@Target(METHOD)
@Retention(RUNTIME)
public @interface Provides {}

Usage Examples:

public class ApplicationModule extends AbstractModule {
    @Override
    protected void configure() {
        // Regular bindings here
    }
    
    // Simple provider method
    @Provides
    DatabaseConfig provideDatabaseConfig() {
        return new DatabaseConfig("localhost", 5432, "myapp");
    }
    
    // Provider method with dependencies
    @Provides
    DatabaseService provideDatabaseService(DatabaseConfig config, Logger logger) {
        return new PostgreSQLService(config, logger);
    }
    
    // Singleton provider method  
    @Provides
    @Singleton
    ConnectionPool provideConnectionPool(DatabaseConfig config) {
        return new HikariConnectionPool(config);
    }
    
    // Named provider method
    @Provides
    @Named("primary")
    Cache providePrimaryCache() {
        return new RedisCache("primary-redis:6379");
    }
    
    // Provider method with complex initialization
    @Provides
    EmailService provideEmailService(
        @Named("smtp.host") String smtpHost,
        @Named("smtp.port") int smtpPort,
        EmailConfig config
    ) {
        EmailService service = new EmailService();
        service.configure(smtpHost, smtpPort);
        service.setTemplateDirectory(config.getTemplateDir());
        service.initialize();
        return service;
    }
}

@Named Annotation

Binding annotation for distinguishing multiple bindings of the same type by name.

/**
 * Annotates named things.
 */
@Retention(RUNTIME)
@Target({FIELD, PARAMETER, METHOD})
@BindingAnnotation
public @interface Named {
    String value();
}

Usage Examples:

// Multiple bindings of the same type
public class CacheModule extends AbstractModule {
    @Override
    protected void configure() {
        bind(Cache.class).annotatedWith(Names.named("primary"))
                         .to(RedisCache.class);
        bind(Cache.class).annotatedWith(Names.named("secondary"))  
                         .to(MemcachedCache.class);
        bind(Cache.class).annotatedWith(Names.named("local"))
                         .to(InMemoryCache.class);
    }
}

// Provider methods with names
public class DatabaseModule extends AbstractModule {
    @Provides
    @Named("primary-db")
    DatabaseConnection providePrimaryConnection() {
        return DriverManager.getConnection("jdbc:postgresql://primary-db/app");
    }
    
    @Provides  
    @Named("secondary-db")
    DatabaseConnection provideSecondaryConnection() {
        return DriverManager.getConnection("jdbc:postgresql://secondary-db/app");
    }
}

// Injection with named dependencies
public class UserRepository {
    private final DatabaseConnection primaryDb;
    private final DatabaseConnection secondaryDb;
    private final Cache primaryCache;
    
    @Inject
    public UserRepository(
        @Named("primary-db") DatabaseConnection primaryDb,
        @Named("secondary-db") DatabaseConnection secondaryDb,
        @Named("primary") Cache primaryCache
    ) {
        this.primaryDb = primaryDb;
        this.secondaryDb = secondaryDb;
        this.primaryCache = primaryCache;
    }
}

// Constants binding with names
public class ConfigModule extends AbstractModule {
    @Override
    protected void configure() {
        bindConstant().annotatedWith(Names.named("api.timeout")).to(30000);
        bindConstant().annotatedWith(Names.named("api.retries")).to(3);
        bindConstant().annotatedWith(Names.named("app.version")).to("1.2.0");
    }
}

@BindingAnnotation Meta-Annotation

Meta-annotation for creating custom binding annotations.

/**
 * Annotates annotations which are used for binding. Only one such annotation
 * may apply to a single injection point. You must also annotate the annotation
 * with @Retention(RUNTIME).
 */
@Target(ANNOTATION_TYPE)
@Retention(RUNTIME)
public @interface BindingAnnotation {}

Usage Examples:

// Create custom binding annotations
@BindingAnnotation
@Target({FIELD, PARAMETER, METHOD})
@Retention(RUNTIME)
public @interface Primary {}

@BindingAnnotation
@Target({FIELD, PARAMETER, METHOD})
@Retention(RUNTIME)
public @interface Secondary {}

@BindingAnnotation
@Target({FIELD, PARAMETER, METHOD})
@Retention(RUNTIME)
public @interface LoggerFor {
    Class<?> value();
}

// Use custom binding annotations
public class ServiceModule extends AbstractModule {
    @Override
    protected void configure() {
        bind(DatabaseService.class).annotatedWith(Primary.class)
                                   .to(PrimaryDatabaseService.class);
        bind(DatabaseService.class).annotatedWith(Secondary.class)
                                   .to(SecondaryDatabaseService.class);
    }
}

// Inject with custom annotations
public class DataProcessor {
    @Inject @Primary DatabaseService primaryDb;
    @Inject @Secondary DatabaseService secondaryDb;
    @Inject @LoggerFor(DataProcessor.class) Logger logger;
}

@ScopeAnnotation Meta-Annotation

Meta-annotation for creating custom scope annotations.

/**
 * Annotates annotations which are used for scoping. Only one such annotation
 * may apply to a single implementation class. You must also annotate the 
 * annotation with @Retention(RUNTIME).
 */
@Target(ANNOTATION_TYPE)
@Retention(RUNTIME)
public @interface ScopeAnnotation {}

Usage Examples:

// Create custom scope annotation
@ScopeAnnotation
@Target({TYPE, METHOD})
@Retention(RUNTIME)
public @interface RequestScoped {}

// Implement the scope
public class RequestScope implements Scope {
    private final ThreadLocal<Map<Key<?>, Object>> requestScopedObjects = 
        new ThreadLocal<Map<Key<?>, Object>>();
        
    @Override
    public <T> Provider<T> scope(Key<T> key, Provider<T> unscoped) {
        return () -> {
            Map<Key<?>, Object> scopedObjects = requestScopedObjects.get();
            if (scopedObjects == null) {
                scopedObjects = new HashMap<>();
                requestScopedObjects.set(scopedObjects);
            }
            
            @SuppressWarnings("unchecked")
            T current = (T) scopedObjects.get(key);
            if (current == null) {
                current = unscoped.get();
                scopedObjects.put(key, current);
            }
            return current;
        };
    }
}

// Bind the scope
public class WebModule extends AbstractModule {
    @Override
    protected void configure() {
        bindScope(RequestScoped.class, new RequestScope());
        
        bind(UserSession.class).in(RequestScoped.class);
    }
}

// Use the custom scope
@RequestScoped
public class UserSession {
    private String userId;
    private Map<String, Object> attributes = new HashMap<>();
    
    // Implementation
}

Annotation Utilities

Names Class

Utilities for working with @Named annotations.

/**
 * Utility methods for use with @Named.
 */
public final class Names {
    /**
     * Creates a @Named annotation with the given name.
     * @param name Name for the annotation
     * @return Named annotation instance
     */
    public static Named named(String name);
    
    /**
     * Binds properties from a Map to named constants.
     * @param binder Binder to use
     * @param properties Map of property names to values
     */
    public static void bindProperties(Binder binder, Map<String, String> properties);
    
    /**
     * Binds properties from Properties to named constants.
     * @param binder Binder to use  
     * @param properties Properties object
     */
    public static void bindProperties(Binder binder, Properties properties);
}

Usage Examples:

// Create named annotations programmatically
Named primaryNamed = Names.named("primary");
Key<Cache> primaryCacheKey = Key.get(Cache.class, primaryNamed);

// Bind properties from configuration files
public class ConfigModule extends AbstractModule {
    @Override
    protected void configure() {
        Properties props = new Properties();
        props.load(getClass().getResourceAsStream("/app.properties"));
        Names.bindProperties(binder(), props);
        
        // Now you can inject properties like:
        // @Inject @Named("database.url") String dbUrl;
        // @Inject @Named("connection.pool.size") int poolSize;
    }
}

Install with Tessl CLI

npx tessl i tessl/maven-com-google-inject--guice

docs

advanced.md

annotations.md

core-injection.md

index.md

modules.md

multibindings.md

providers-scopes.md

spi.md

types-keys.md

tile.json