CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-micronaut

Modern, JVM-based framework for building modular, easily testable microservice and serverless applications with compile-time DI and fast startup.

Pending
Overview
Eval results
Files

dependency-injection.mddocs/

Dependency Injection

Micronaut's dependency injection system uses compile-time processing to eliminate reflection and provide fast application startup. It supports the full Jakarta Inject specification plus additional features for modern application development.

Capabilities

Bean Declaration

Define beans using standard Jakarta Inject annotations or Micronaut-specific annotations.

/**
 * Mark a class as a singleton bean
 */
@Singleton
public class UserService {
    private final UserRepository repository;
    
    public UserService(UserRepository repository) {
        this.repository = repository;
    }
}

/**
 * Create beans using factory methods
 */
@Factory
public class DatabaseFactory {
    
    @Bean
    @Singleton
    public DataSource dataSource() {
        return new HikariDataSource();
    }
}

/**
 * Prototype scope (new instance each time)
 */
@Prototype
public class RequestHandler {
    // New instance for each injection
}

Dependency Injection

Inject dependencies through constructor, field, or method injection.

/**
 * Constructor injection (recommended)
 */
@Singleton
public class OrderService {
    private final PaymentService paymentService;
    private final InventoryService inventoryService;
    
    public OrderService(PaymentService paymentService, 
                       InventoryService inventoryService) {
        this.paymentService = paymentService;
        this.inventoryService = inventoryService;
    }
}

/**
 * Field injection
 */
@Singleton
public class NotificationService {
    @Inject
    private EmailService emailService;
}

/**
 * Method injection
 */
@Singleton
public class ConfigService {
    private DatabaseConfig config;
    
    @Inject
    public void setDatabaseConfig(DatabaseConfig config) {
        this.config = config;
    }
}

Qualifiers

Use qualifiers to distinguish between multiple implementations of the same interface.

/**
 * Named qualifier
 */
@Singleton
@Named("mysql")
public class MySqlRepository implements Repository {
    // MySQL implementation
}

@Singleton  
@Named("postgres")
public class PostgresRepository implements Repository {
    // PostgreSQL implementation
}

/**
 * Inject specific implementation
 */
@Singleton
public class DataService {
    private final Repository repository;
    
    public DataService(@Named("mysql") Repository repository) {
        this.repository = repository;
    }
}

/**
 * Custom qualifiers
 */
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
public @interface Database {
    String value();
}

@Singleton
@Database("primary")
public class PrimaryDatabase implements DatabaseService {
    // Primary database implementation
}

Bean Scopes

Control bean lifecycle with built-in and custom scopes.

/**
 * Built-in scopes
 */
@Singleton  // Single instance per application
@Prototype  // New instance each time
@RequestScope  // One instance per HTTP request
@ThreadLocal   // One instance per thread

/**
 * Custom scope example
 */
@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface TenantScope {
}

@TenantScope
public class TenantService {
    // One instance per tenant
}

Conditional Beans

Create beans conditionally based on configuration, environment, or other factors.

/**
 * Conditional on property value
 */
@Singleton
@Requires(property = "cache.enabled", value = "true")
public class CacheService {
    // Only created if cache.enabled=true
}

/**
 * Conditional on missing bean  
 */
@Singleton
@Requires(missingBeans = CustomMetricsService.class)
public class DefaultMetricsService implements MetricsService {
    // Only created if CustomMetricsService not present
}

/**
 * Conditional on environment
 */
@Singleton
@Requires(env = Environment.DEVELOPMENT)  
public class DevelopmentDataLoader {
    // Only in development environment
}

/**
 * Custom condition
 */
@Singleton
@Requires(condition = DatabaseAvailableCondition.class)
public class DatabaseService {
    // Custom condition logic
}

Bean Factories

Create complex beans using factory classes and methods.

/**
 * Factory class for complex bean creation
 */
@Factory
public class HttpClientFactory {
    
    @Bean
    @Singleton
    @Named("api-client")
    public HttpClient createApiClient(@Value("${api.base-url}") String baseUrl) {
        return HttpClient.create(URL.of(baseUrl))
            .configuration(config -> {
                config.readTimeout(Duration.ofSeconds(30));
                config.connectTimeout(Duration.ofSeconds(10));
            });
    }
    
    @Bean
    @Singleton
    @Named("auth-client")
    public HttpClient createAuthClient(@Value("${auth.url}") String authUrl) {
        return HttpClient.create(URL.of(authUrl));
    }
}

/**
 * Parameterized factory for creating multiple similar beans
 */
@EachProperty("databases")
public class DatabaseFactory {
    private String name;
    private String url;
    private String driver;
    
    @Bean
    @Singleton
    public DataSource dataSource() {
        return DataSourceBuilder.create()
            .url(url)
            .driverClassName(driver)
            .build();
    }
    
    // getters and setters
}

Bean Validation

Validate bean properties during creation using Jakarta Bean Validation.

/**
 * Bean with validation constraints
 */
@Singleton
public class UserService {
    
    public User createUser(@Valid @NotNull CreateUserRequest request) {
        // Method parameter validation
        return new User(request.getName(), request.getEmail());
    }
}

/**
 * Configuration bean with validation
 */
@ConfigurationProperties("app")
public class AppConfiguration {
    
    @NotBlank
    private String name;
    
    @Min(1)
    @Max(65535)
    private int port;
    
    @Email
    private String adminEmail;
    
    // getters and setters with validation
}

Bean Events

Listen to bean lifecycle events and publish custom events.

/**
 * Bean lifecycle events
 */
@Singleton
public class ServiceInitializer {
    
    @EventListener
    public void onStartup(StartupEvent event) {
        // Application startup logic
    }
    
    @EventListener
    public void onShutdown(ShutdownEvent event) {
        // Cleanup logic
    }
    
    @EventListener
    public void onBeanCreated(BeanCreatedEvent<DataSource> event) {
        // React to DataSource bean creation
        DataSource dataSource = event.getBean();
        // Initialize connection pool, etc.
    }
}

/**
 * Custom event publishing
 */
@Singleton
public class OrderService {
    private final ApplicationEventPublisher eventPublisher;
    
    public OrderService(ApplicationEventPublisher eventPublisher) {
        this.eventPublisher = eventPublisher;
    }
    
    public void processOrder(Order order) {
        // Process order
        eventPublisher.publishEvent(new OrderProcessedEvent(order));
    }
}

Bean Introspection

Access bean metadata at runtime without reflection.

/**
 * Enable introspection for a class
 */
@Introspected
public class Person {
    private String firstName;
    private String lastName;
    private int age;
    
    // constructors, getters, setters
}

/**
 * Use introspection to access bean metadata
 */
@Singleton
public class ObjectMapper {
    
    public Map<String, Object> toMap(Object obj) {
        BeanIntrospection<Object> introspection = 
            BeanIntrospection.getIntrospection(obj.getClass());
            
        Map<String, Object> map = new HashMap<>();
        for (BeanProperty<Object, Object> property : introspection.getBeanProperties()) {
            Object value = property.get(obj);
            map.put(property.getName(), value);
        }
        return map;
    }
}

Types

// Core DI interfaces
public interface BeanContext extends BeanLocator, LifeCycle<BeanContext> {
    <T> T getBean(Class<T> beanType);
    <T> T getBean(Class<T> beanType, Qualifier<T> qualifier);
    <T> Optional<T> findBean(Class<T> beanType);
    <T> Collection<T> getBeansOfType(Class<T> beanType);
    <T> Stream<T> streamOfType(Class<T> beanType);
}

public interface BeanDefinition<T> extends BeanType<T> {
    Class<T> getBeanType();
    Optional<Class<?>> getDeclaringType();  
    boolean isEnabled(BeanContext context);
    boolean isSingleton();
    boolean isConfigurationProperties();
    List<Argument<?>> getRequiredArguments();
}

public interface ApplicationContext extends BeanContext, LifeCycle<ApplicationContext> {
    Environment getEnvironment();
    ConversionService getConversionService();
    void publishEvent(Object event);
    static ApplicationContext build();
    static ApplicationContext run();
}

// Bean introspection
public interface BeanIntrospection<T> {
    Class<T> getBeanType();
    Collection<BeanProperty<T, Object>> getBeanProperties();
    Collection<BeanMethod<T, Object>> getBeanMethods();
    T instantiate(Object... args);
    static <T> BeanIntrospection<T> getIntrospection(Class<T> type);
}

public interface BeanProperty<B, T> {
    Class<T> getType();
    String getName();
    T get(B bean);
    void set(B bean, T value);
    boolean isReadOnly();
    boolean isWriteOnly();
}

// Injection points
public interface InjectionPoint<T> {
    BeanDefinition<?> getDeclaringBean();
    Class<T> getType();
    Optional<String> getName();
    AnnotationMetadata getAnnotationMetadata();
}

public interface FieldInjectionPoint<B, T> extends InjectionPoint<T> {
    Field getField();
    void set(B instance, T value);
}

public interface MethodInjectionPoint<B, T> extends InjectionPoint<T> {
    Method getMethod();
    T invoke(B instance, Object... args);
}

Install with Tessl CLI

npx tessl i tessl/maven-micronaut

docs

aop.md

configuration.md

dependency-injection.md

functions.md

http-client.md

http-server.md

index.md

management.md

messaging.md

reactive.md

retry.md

scheduling.md

websocket.md

tile.json