Core dependency injection interfaces and components for the Micronaut Framework
—
Bean providers and locators offer flexible, programmatic access to beans in the Micronaut context. They enable lazy instantiation, optional dependencies, and type-safe collection access to beans.
Enhanced provider interface that extends Jakarta Provider with additional capabilities for bean introspection and collection access.
/**
* Enhanced provider interface for accessing beans with additional capabilities
* @param <T> The bean type
*/
public interface BeanProvider<T> extends jakarta.inject.Provider<T> {
/**
* Check if at least one bean of this type is available
* @return true if a bean is present
*/
boolean isPresent();
/**
* Check if exactly one bean of this type is available
* @return true if only one bean matches
*/
boolean isUnique();
/**
* Get an iterator over all available beans of this type
* @return Iterator of bean instances
*/
Iterator<T> iterator();
/**
* Get a stream of all available beans of this type
* @return Stream of bean instances
*/
Stream<T> stream();
/**
* Get the first available bean instance
* @return Bean instance
* @throws NoSuchBeanException if no bean is available
*/
@Override
T get();
}Usage Examples:
import io.micronaut.context.BeanProvider;
import jakarta.inject.Inject;
import jakarta.inject.Singleton;
@Singleton
public class NotificationService {
// Lazy provider - bean resolved on first access
private final BeanProvider<EmailService> emailProvider;
@Inject
public NotificationService(BeanProvider<EmailService> emailProvider) {
this.emailProvider = emailProvider;
}
public void sendNotification(String message) {
// Check if email service is available before using it
if (emailProvider.isPresent()) {
EmailService emailService = emailProvider.get();
emailService.send(message);
}
// Process all notification handlers
emailProvider.stream().forEach(handler -> handler.send(message));
}
}
// Multiple implementations scenario
@Singleton
public class AggregatorService {
private final BeanProvider<DataProcessor> processors;
@Inject
public AggregatorService(BeanProvider<DataProcessor> processors) {
this.processors = processors;
}
public void processData(Data data) {
// Use all available processors
processors.iterator().forEachRemaining(processor ->
processor.process(data)
);
}
}Core interface for locating beans in the context with various resolution strategies.
/**
* Core interface for bean location and resolution
*/
public interface BeanLocator {
/**
* Get a required bean of the given type
* @param beanType The bean type
* @return Bean instance
* @throws NoSuchBeanException if bean not found
*/
<T> T getBean(Class<T> beanType);
/**
* Get a required bean with qualifier
* @param beanType The bean type
* @param qualifier The qualifier
* @return Bean instance
* @throws NoSuchBeanException if bean not found
*/
<T> T getBean(Class<T> beanType, Qualifier<T> qualifier);
/**
* Find an optional bean of the given type
* @param beanType The bean type
* @return Optional bean instance
*/
<T> Optional<T> findBean(Class<T> beanType);
/**
* Find an optional bean with qualifier
* @param beanType The bean type
* @param qualifier The qualifier
* @return Optional bean instance
*/
<T> Optional<T> findBean(Class<T> beanType, Qualifier<T> qualifier);
/**
* Get all beans of the given type
* @param beanType The bean type
* @return Collection of bean instances
*/
<T> Collection<T> getBeansOfType(Class<T> beanType);
/**
* Get all beans of the given type with qualifier
* @param beanType The bean type
* @param qualifier The qualifier
* @return Collection of bean instances
*/
<T> Collection<T> getBeansOfType(Class<T> beanType, Qualifier<T> qualifier);
/**
* Get bean provider for the given type
* @param beanType The bean type
* @return BeanProvider instance
*/
<T> BeanProvider<T> getBeanProvider(Class<T> beanType);
/**
* Get bean provider for the given type with qualifier
* @param beanType The bean type
* @param qualifier The qualifier
* @return BeanProvider instance
*/
<T> BeanProvider<T> getBeanProvider(Class<T> beanType, Qualifier<T> qualifier);
}Registry interface providing metadata and introspection capabilities for bean definitions.
/**
* Registry for bean definition metadata and introspection
*/
public interface BeanDefinitionRegistry {
/**
* Check if a bean of the given type exists
* @param beanType The bean type
* @return true if bean exists
*/
<T> boolean containsBean(Class<T> beanType);
/**
* Check if a bean exists with qualifier
* @param beanType The bean type
* @param qualifier The qualifier
* @return true if bean exists
*/
<T> boolean containsBean(Class<T> beanType, Qualifier<T> qualifier);
/**
* Find bean definition for the given type
* @param beanType The bean type
* @return Optional bean definition
*/
<T> Optional<BeanDefinition<T>> findBeanDefinition(Class<T> beanType);
/**
* Find bean definition with qualifier
* @param beanType The bean type
* @param qualifier The qualifier
* @return Optional bean definition
*/
<T> Optional<BeanDefinition<T>> findBeanDefinition(Class<T> beanType, Qualifier<T> qualifier);
/**
* Get all bean definitions for the given type
* @param beanType The bean type
* @return Collection of bean definitions
*/
<T> Collection<BeanDefinition<T>> getBeanDefinitions(Class<T> beanType);
/**
* Get single bean definition for the given type
* @param beanType The bean type
* @return Bean definition
* @throws NoSuchBeanException if not found
* @throws NonUniqueBeanException if multiple found
*/
<T> BeanDefinition<T> getBeanDefinition(Class<T> beanType);
}@Singleton
public class OptionalFeatureService {
private final BeanProvider<CacheService> cacheProvider;
private final BeanProvider<MetricsService> metricsProvider;
@Inject
public OptionalFeatureService(
BeanProvider<CacheService> cacheProvider,
BeanProvider<MetricsService> metricsProvider
) {
this.cacheProvider = cacheProvider;
this.metricsProvider = metricsProvider;
}
public String processRequest(String request) {
String result = performCoreLogic(request);
// Optional caching
if (cacheProvider.isPresent()) {
cacheProvider.get().cache(request, result);
}
// Optional metrics
if (metricsProvider.isPresent()) {
metricsProvider.get().recordRequest();
}
return result;
}
}@Singleton
public class PluginManager {
private final BeanProvider<Plugin> plugins;
@Inject
public PluginManager(BeanProvider<Plugin> plugins) {
this.plugins = plugins;
}
public void initializePlugins() {
plugins.stream()
.sorted(Comparator.comparing(Plugin::getPriority))
.forEach(Plugin::initialize);
}
public void processWithPlugins(Event event) {
plugins.iterator().forEachRemaining(plugin ->
plugin.process(event)
);
}
}@Singleton
public class ExpensiveResourceManager {
private final BeanProvider<DatabaseConnection> dbProvider;
private final BeanProvider<FileSystem> fsProvider;
@Inject
public ExpensiveResourceManager(
BeanProvider<DatabaseConnection> dbProvider,
BeanProvider<FileSystem> fsProvider
) {
this.dbProvider = dbProvider;
this.fsProvider = fsProvider;
}
public void performDatabaseOperation() {
// Database connection only created when needed
DatabaseConnection db = dbProvider.get();
db.execute("SELECT * FROM users");
}
public void performFileOperation() {
// File system only initialized when needed
FileSystem fs = fsProvider.get();
fs.readFile("config.properties");
}
}public interface Qualifier<T> {
<BT extends BeanType<T>> Stream<BT> reduce(Class<T> beanType, Stream<BT> candidates);
boolean equals(Object o);
int hashCode();
}
public interface BeanResolutionContext {
BeanContext getContext();
BeanDefinition<?> getRootDefinition();
Path getPath();
BeanResolutionContext copy();
}
public interface BeanCreationContext<T> extends BeanResolutionContext {
BeanDefinition<T> definition();
CreatedBean<T> create() throws BeanCreationException;
}Install with Tessl CLI
npx tessl i tessl/maven-io-micronaut--micronaut-inject