π JetLeaf Core β Application Lifecycle & Pod Management
The core runtime that powers JetLeaf's pod discovery, context management, and application lifecycle.
π Overview
jetleaf_core is the heart of the JetLeaf framework, providing:
-
Inversion of Control (IoC) β Automatic pod discovery and registration
-
Pod Lifecycle Management β From initialization through graceful shutdown
-
Application Context β Central registry for all framework components
-
Annotation-Driven Configuration β Declarative pod and lifecycle definitions
-
Aware Interfaces β Lifecycle callbacks for framework integration
-
Event Publishing β Application-level event system
-
Ordering Mechanisms β Deterministic pod initialization sequences
-
Homepage: https://jetleaf.hapnium.com
-
Repository: https://github.com/jetleaf/jetleaf_core
-
License: See
LICENSE
Contents
- Features
- Install
- Quick Start
- Core Concepts
- Usage
- API Reference
- Testing
- Changelog
- Contributing
- Compatibility
Features
- Application Context β Central container for managing application lifecycle, configuration, and pods.
- Annotations β Rich set of annotations for configuration, components, dependency injection, and conditional processing.
- Dependency Injection β
@Autowired,@Value,@RequiredAllfor automatic dependency wiring. - Configuration β
@Configuration,@AutoConfiguration,@Podfor defining pods and settings. - Stereotypes β
@Component,@Service,@Repository,@Controllerfor semantic component roles. - Conditional Processing β
@Conditional,@ConditionalOnProperty,@ConditionalOnPod, etc. for environment-specific configuration. - Lifecycle Management β Context lifecycle (refresh, start, stop, close) and pod lifecycle hooks.
- Event System β Publish and listen to application events for decoupled communication.
- Message Source β Internationalization (i18n) support with locale-based message resolution.
- Exit Code Management β Graceful shutdown with exit code generation.
Install
Add to your pubspec.yaml:
dependencies:
jetleaf_core: ^1.0.0
Minimum SDK: Dart ^3.9.0
Import:
import 'package:jetleaf_core/annotation.dart';
import 'package:jetleaf_core/context.dart';
import 'package:jetleaf_core/core.dart';
import 'package:jetleaf_core/message.dart';
Quick Start
import 'package:jetleaf_core/annotation.dart';
import 'package:jetleaf_core/context.dart';
// Define a service
@Service()
class UserService {
void greet() => print('Hello from UserService!');
}
// Define a configuration
@Configuration()
class AppConfig {
@Pod()
Logger createLogger() => Logger('AppLogger');
}
// Create and run the application
void main() async {
final context = AnnotationConfigApplicationContext([
Class<AppConfig>(),
Class<UserService>(),
]);
await context.refresh();
final userService = await context.getPod<UserService>('userService');
userService.greet(); // Output: Hello from UserService!
await context.close();
}
Core Concepts
Application Context
The ApplicationContext is the central interface for a JetLeaf application. It provides:
- Dependency Injection: Access to managed pods via
ListablePodFactoryandHierarchicalPodFactory - Environment Management: Configuration and profile handling via
EnvironmentCapable - Internationalization: Message resolution via
MessageSource - Event System: Application-wide event publication
- Lifecycle Management: Context state tracking (active, closed)
Key implementations:
GenericApplicationContextβ Basic application contextAnnotationConfigApplicationContextβ Annotation-driven context with component scanningAbstractApplicationContextβ Base implementation with lifecycle support
Annotations
JetLeaf provides a rich set of annotations for declarative configuration:
Configuration Annotations
@Configurationβ Marks a class as a source of pod definitions@AutoConfigurationβ Auto-discovered configuration class@Podβ Marks a method as a pod provider (factory method)
Stereotype Annotations
@Componentβ Generic component for DI@Serviceβ Business logic layer@Repositoryβ Data access layer@Controllerβ Presentation/routing layer
Dependency Injection Annotations
@Autowiredβ Marks a field for automatic dependency injection@RequiredAllβ Auto-injects all eligible fields in a class@Valueβ Injects property values or pod expressions
Conditional Annotations
@Conditionalβ Conditional registration based on custom conditions@ConditionalOnPropertyβ Conditional based on property values@ConditionalOnPodβ Conditional based on pod existence@ConditionalOnMissingPodβ Conditional when pod is missing@ConditionalOnClassβ Conditional based on class presence
Lifecycle Annotations
@PostConstructβ Method called after pod initialization@PreDestroyβ Method called before pod destruction@Lazyβ Lazy initialization of pods@DependsOnβ Explicit pod dependency ordering
Other Annotations
@Primaryβ Marks a pod as primary when multiple candidates exist@Qualifierβ Specifies which pod to inject when multiple candidates exist@Scopeβ Defines the scope of a pod (singleton, prototype, etc.)@Orderβ Defines ordering for pods@Profileβ Activates pods for specific profiles
Dependency Injection
JetLeaf supports multiple injection strategies:
Field Injection:
@Service()
class OrderService {
@Autowired()
late UserService userService;
}
Constructor Injection (preferred):
@Service()
class OrderService {
final UserService userService;
OrderService(this.userService);
}
Auto-injection:
@Service()
@RequiredAll()
class OrderService {
late UserService userService; // Auto-injected
late PaymentService paymentService; // Auto-injected
}
Property Injection:
@Component()
class DatabaseService {
@Value('#{database.url}')
late String databaseUrl;
@Value('#{database.timeout:30}') // Default value
late int timeout;
}
Conditional Configuration
Control pod registration based on runtime conditions:
// Conditional on property
@ConditionalOnProperty(
prefix: 'server',
names: ['ssl.enabled'],
havingValue: 'true',
)
@Configuration()
class SslServerConfig {
@Pod()
SslContext sslContext() => SslContext();
}
// Conditional on pod existence
@ConditionalOnPod(DatabaseConnection)
@Service()
class DatabaseMigrationService {
final DatabaseConnection db;
DatabaseMigrationService(this.db);
}
// Custom condition
class OnProductionEnvironmentCondition implements Condition {
@override
bool matches(ConditionalContext context, ClassType<Object> classType) {
return context.environment.activeProfiles.contains('production');
}
}
@Conditional([ClassType<OnProductionEnvironmentCondition>()])
@Configuration()
class ProductionConfig {}
Lifecycle Management
Application contexts have a well-defined lifecycle:
- Creation β Context is instantiated
- Refresh β Pods are loaded, processed, and initialized
- Active β Context is fully operational
- Close β Resources are released, pods are destroyed
final context = AnnotationConfigApplicationContext([...]);
// Refresh to initialize
await context.refresh();
print(context.isActive()); // true
// Use the context
final service = await context.getPod<MyService>('myService');
// Close when done
await context.close();
print(context.isClosed()); // true
Event System
Publish and listen to application events for decoupled communication:
// Define an event
class OrderCreatedEvent extends ApplicationEvent {
final Order order;
OrderCreatedEvent(Object source, this.order) : super(source);
}
// Publish an event
@Service()
class OrderService {
final ApplicationContext context;
OrderService(this.context);
Future<void> createOrder(OrderRequest request) async {
final order = await orderRepository.save(request);
await context.publishEvent(OrderCreatedEvent(this, order));
}
}
// Listen to events
@Component()
class OrderEventListener implements ApplicationEventListener<OrderCreatedEvent> {
@override
Future<void> onApplicationEvent(OrderCreatedEvent event) async {
await emailService.sendConfirmation(event.order);
}
}
Message Source (i18n)
Internationalization support with locale-based message resolution:
final messageSource = ConfigurableMessageSource();
// Simple message
final greeting = messageSource.getMessage('greeting');
// Message with parameters
final welcome = messageSource.getMessage('welcome', args: ['John']);
// Message for specific locale
final bonjour = messageSource.getMessage('greeting', locale: Locale('fr'));
Usage
Creating an Application Context
import 'package:jetleaf_core/context.dart';
void main() async {
// Create context with configuration classes
final context = AnnotationConfigApplicationContext([
Class<AppConfig>(),
Class<DatabaseConfig>(),
]);
// Initialize the context
await context.refresh();
// Access context information
print('Application: ${context.getApplicationName()}');
print('Context ID: ${context.getId()}');
print('Start Time: ${context.getStartTime()}');
// Use the context
final service = await context.getPod<MyService>('myService');
await service.doWork();
// Shutdown
await context.close();
}
Configuration Classes
@Configuration()
class DatabaseConfig {
@Pod()
DatabaseConnection primaryDatabase() {
return DatabaseConnection(
url: 'postgresql://localhost:5432/primary',
maxConnections: 20,
);
}
@Pod('readOnlyDatabase')
@Scope('prototype')
DatabaseConnection readOnlyDatabase() {
return DatabaseConnection(
url: 'postgresql://localhost:5432/readonly',
readOnly: true,
);
}
}
Component Scanning
// Auto-discovered components
@Component()
class EmailService {
final EmailProvider emailProvider;
EmailService(this.emailProvider);
Future<void> sendEmail(String to, String subject, String body) async {
await emailProvider.send(to: to, subject: subject, body: body);
}
}
@Service()
class UserService {
@Autowired()
late EmailService emailService;
Future<void> registerUser(User user) async {
await userRepository.save(user);
await emailService.sendEmail(
user.email,
'Welcome!',
'Welcome to our application!',
);
}
}
Dependency Injection
@Service()
class OrderService {
final UserService userService;
final PaymentService paymentService;
final InventoryService inventoryService;
// Constructor injection (preferred)
OrderService(
this.userService,
this.paymentService,
this.inventoryService,
);
Future<Order> createOrder(CreateOrderRequest request) async {
final user = await userService.findById(request.userId);
await inventoryService.reserveItems(request.items);
final payment = await paymentService.processPayment(request.payment);
return Order(
userId: user.id,
items: request.items,
payment: payment,
createdAt: DateTime.now(),
);
}
}
Conditional Pods
// Only register in development
@ConditionalOnProperty(
prefix: 'app',
names: ['debug'],
havingValue: 'true',
)
@Component()
class DebugLogger {
void log(String message) => print('[DEBUG] $message');
}
// Only register if Redis is available
@ConditionalOnClass('redis.RedisClient')
@Service()
class RedisCacheService {
final RedisClient redis;
RedisCacheService(this.redis);
Future<String?> get(String key) => redis.get(key);
}
Lifecycle Hooks
@Service()
class DatabaseService {
late DatabaseConnection connection;
@PostConstruct()
Future<void> initialize() async {
connection = await DatabaseConnection.connect();
print('Database connected');
}
@PreDestroy()
Future<void> cleanup() async {
await connection.close();
print('Database connection closed');
}
}
Publishing Events
@Service()
class UserService {
final ApplicationContext context;
UserService(this.context);
Future<User> createUser(CreateUserRequest request) async {
final user = await userRepository.save(request);
// Publish domain event
await context.publishEvent(UserCreatedEvent(this, user));
return user;
}
}
@Component()
class UserEventListener implements ApplicationEventListener<UserCreatedEvent> {
@override
Future<void> onApplicationEvent(UserCreatedEvent event) async {
print('User created: ${event.user.email}');
await emailService.sendWelcomeEmail(event.user.email);
}
}
Internationalization
@Configuration()
class MessageConfig {
@Pod()
MessageSource messageSource() {
final source = ConfigurableMessageSource();
source.setBasename('messages');
return source;
}
}
// Usage
final messageSource = await context.getPod<MessageSource>('messageSource');
// Get localized messages
final greeting = messageSource.getMessage('greeting');
final welcome = messageSource.getMessage('welcome', args: ['Alice']);
final bonjour = messageSource.getMessage('greeting', locale: Locale('fr'));
API Reference
Core Exports (lib/core.dart)
- Condition Helpers: Conditional processing utilities
- Lifecycle: Context lifecycle management
- Aware Interfaces:
EnvironmentAware,ApplicationContextAware, etc. - Exceptions:
PodException,ContextException - Order Comparator:
AnnotationAwareOrderComparator
Context Exports (lib/context.dart)
- Core:
ApplicationContext,GenericApplicationContext,AnnotationConfigApplicationContext - Events:
ApplicationEvent,ApplicationEventListener - Exit Code:
ExitCodeGenerator,ExitCodeEvent - Helpers: Context utilities
- Module:
ApplicationModule - Registrar:
PodRegistrar
Annotation Exports (lib/annotation.dart)
- Lifecycle:
@PostConstruct,@PreDestroy,@Lazy,@DependsOn - Autowired:
@Autowired,@RequiredAll,@Value,@Qualifier - Configuration:
@Configuration,@AutoConfiguration,@Pod - Conditional:
@Conditional,@ConditionalOnProperty,@ConditionalOnPod, etc. - Others:
@Primary,@Scope,@Order,@Profile - Stereotypes:
@Component,@Service,@Repository,@Controller
Message Exports (lib/message.dart)
- MessageSource: Internationalization interface
- AbstractMessageSource: Base implementation
- ConfigurableMessageSource: Configurable message source
- MessageSourceLoader: Message loading utilities
See lib/ for the full export list and lib/src/ for implementation details.
Testing
Run tests with:
dart test
See test/ for coverage of context lifecycle, annotations, conditional processing, events, and message resolution.
Changelog
See CHANGELOG.md.
Contributing
Issues and PRs are welcome at the GitHub repository.
- Fork and create a feature branch.
- Add tests for new functionality.
- Run
dart testand ensure lints pass. - Open a PR with a concise description and examples.
Compatibility
- Dart SDK:
>=3.9.0 <4.0.0 - Depends on
jetleaf_lang,jetleaf_logging,jetleaf_convert,jetleaf_utils,jetleaf_env,jetleaf_pod(seepubspec.yaml).
Built with π by the JetLeaf team.
Libraries
- context
- π± JetLeaf Core Context
- core
- annotation Annotations
- π· JetLeaf Core Annotations
- jetleaf_core Core
- π± JetLeaf Core
- intercept Interception
- π JetLeaf Core Interception
- message Internationalization
- π JetLeaf Core Message