jetleaf 1.0.0+1
jetleaf: ^1.0.0+1 copied to clipboard
A comprehensive, enterprise-grade Dart backend framework that brings Spring-like dependency injection, configuration management, and application lifecycle to Dart server-side development.
JetLeaf #
π JetLeaf is a comprehensive, enterprise-grade Dart backend framework that brings Spring-like dependency injection, configuration management, and application lifecycle to Dart server-side development.
JetLeaf provides a complete foundation for building scalable, maintainable backend applications with powerful features like annotation-driven configuration, automatic component scanning, profile-based environments, and event-driven architecture.
- Homepage: https://jetleaf.hapnium.com
- Repository: https://github.com/jetleaf/jetleaf
- License: See
LICENSE
Contents #
- Features
- Install
- Quick Start
- Core Concepts
- Usage
- Configuration Files
- CLI Tools
- API Reference
- Examples
- Changelog
- Contributing
- Compatibility
Features #
Core Framework #
- π Application Bootstrap β
JetApplication
for rapid application startup with@JetLeafApplication
- π Dependency Injection β Full IoC container with constructor, field, and setter injection
- π¦ Component Scanning β Automatic discovery of
@Component
,@Service
,@Repository
,@Controller
- βοΈ Configuration Management β YAML, JSON, Properties, and Dart-based configuration
- π Lifecycle Management β
@PostConstruct
,@PreDestroy
, and context lifecycle hooks - π― Profile Management β Environment-specific configuration with
@Profile
- π Conditional Processing β
@Conditional
,@ConditionalOnProperty
,@ConditionalOnPod
- π‘ Event System β Application-wide event publishing and listening
- π Internationalization β Multi-locale message resolution
- π¨ Banner Display β Customizable startup banners
Advanced Features #
- Auto-Configuration β
@EnableAutoConfiguration
for framework and library auto-setup - Property Binding β
@Value
for property injection with placeholders and expressions - Type Conversion β Automatic type conversion for properties and dependencies
- Circular Reference Handling β Smart resolution of circular dependencies
- Lazy Initialization β
@Lazy
for deferred pod creation - Primary Pods β
@Primary
for disambiguation when multiple candidates exist - Ordered Components β
@Order
for component ordering - Exit Code Management β Graceful shutdown with exit code generation
- Exception Handling β Comprehensive exception reporting and handling
Install #
Add to your pubspec.yaml
:
dependencies:
jetleaf:
hosted: https://onepub.flutter-io.cn/api/fahnhnofly/
version: ^1.0.0
Minimum SDK: Dart ^3.9.0
Import:
import 'package:jetleaf/jetleaf.dart';
Quick Start #
1. Create Your Application #
import 'package:jetleaf/jetleaf.dart';
void main(List<String> args) {
JetApplication.run(MyApplication, args);
}
@JetLeafApplication()
class MyApplication {}
2. Define Services #
@Service()
class UserService {
final UserRepository repository;
UserService(this.repository);
Future<User> findById(String id) async {
return await repository.findById(id);
}
}
@Repository()
class UserRepository {
Future<User> findById(String id) async {
// Database access logic
return User(id: id, name: 'John Doe');
}
}
3. Create Configuration #
@Configuration()
class DatabaseConfig {
@Pod()
DatabaseConnection databaseConnection() {
return DatabaseConnection(
host: 'localhost',
port: 5432,
database: 'myapp',
);
}
}
4. Run Your Application #
dart run lib/main.dart
Core Concepts #
Application Bootstrap #
JetLeaf applications start with the JetApplication
class, which handles:
- Runtime Detection: AOT vs JIT compilation detection
- Bootstrap Context: Early component registration
- Environment Setup: Property sources and profile activation
- Context Creation: Application context building and configuration
- Lifecycle Management: Startup, refresh, and shutdown orchestration
void main(List<String> args) async {
final app = JetApplication(MyApplication);
// Custom configuration
app.setBannerMode(BannerMode.CONSOLE);
app.setAdditionalProfiles({'production'});
app.setLazyInitialization(true);
// Add initializers
app.addInitializer((context) {
// Custom initialization
});
// Launch
final context = await app.create(args, null);
}
Dependency Injection #
JetLeaf provides comprehensive dependency injection with multiple strategies:
Constructor Injection (recommended):
@Service()
class OrderService {
final UserService userService;
final PaymentService paymentService;
OrderService(this.userService, this.paymentService);
}
Field Injection:
@Service()
class OrderService {
@Autowired()
late UserService userService;
@Autowired()
late PaymentService paymentService;
}
Auto-Injection:
@Service()
@RequiredAll()
class OrderService {
late UserService userService; // Auto-injected
late PaymentService paymentService; // Auto-injected
}
Configuration Management #
JetLeaf supports multiple configuration formats:
YAML Configuration (application.yaml
):
server:
port: 8080
host: localhost
database:
url: postgresql://localhost:5432/myapp
maxConnections: 20
logging:
level: INFO
Properties Configuration (application.properties
):
server.port=8080
server.host=localhost
database.url=postgresql://localhost:5432/myapp
database.maxConnections=20
Dart Configuration:
@Configuration()
class AppConfig {
@Pod()
ServerConfig serverConfig() {
return ServerConfig(port: 8080, host: 'localhost');
}
}
Component Scanning #
JetLeaf automatically discovers and registers components:
Stereotype Annotations:
@Component
β Generic component@Service
β Business logic layer@Repository
β Data access layer@Controller
β Presentation/routing layer
@Service()
class EmailService {
Future<void> sendEmail(String to, String subject, String body) async {
// Email sending logic
}
}
@Repository()
class UserRepository {
Future<User> save(User user) async {
// Database persistence
}
}
@Controller()
class UserController {
final UserService userService;
UserController(this.userService);
}
Lifecycle Management #
Control component lifecycle with annotations:
@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');
}
}
Profile Management #
Configure environment-specific behavior:
@Configuration()
@Profile('development')
class DevConfig {
@Pod()
Logger logger() => Logger.debug();
}
@Configuration()
@Profile('production')
class ProdConfig {
@Pod()
Logger logger() => Logger.production();
}
Activate profiles via command line:
dart run lib/main.dart --jetleaf.profiles.active=production
Usage #
Creating an Application #
import 'package:jetleaf/jetleaf.dart';
@JetLeafApplication()
class MyApplication {
static void main(List<String> args) {
JetApplication.run(MyApplication, args);
}
}
With custom configuration:
void main(List<String> args) async {
final app = JetApplication(MyApplication);
// Banner configuration
app.setBannerMode(BannerMode.CONSOLE);
// Profile activation
app.setAdditionalProfiles({'production', 'cloud'});
// Performance tuning
app.setLazyInitialization(true);
app.setAllowCircularReferences(false);
// Custom initializers
app.addInitializer((context) async {
// Pre-refresh initialization
});
// Event listeners
app.addListener(MyApplicationListener());
// Launch application
final context = await app.create(args, null);
}
Configuration Classes #
@Configuration()
class DatabaseConfig {
@Value('#{database.url}')
late String databaseUrl;
@Value('#{database.maxConnections:20}')
late int maxConnections;
@Pod()
DatabaseConnection primaryDatabase() {
return DatabaseConnection(
url: databaseUrl,
maxConnections: maxConnections,
);
}
@Pod('readOnlyDatabase')
@Scope('prototype')
DatabaseConnection readOnlyDatabase() {
return DatabaseConnection(
url: databaseUrl,
readOnly: true,
);
}
}
Services and Components #
@Service()
class UserService {
final UserRepository repository;
final EmailService emailService;
final Logger logger;
UserService(this.repository, this.emailService, this.logger);
Future<User> registerUser(CreateUserRequest request) async {
logger.info('Registering user: ${request.email}');
final user = await repository.save(User.fromRequest(request));
await emailService.sendWelcomeEmail(user.email);
return user;
}
}
@Repository()
class UserRepository {
final DatabaseConnection db;
UserRepository(this.db);
Future<User> save(User user) async {
// Database persistence logic
return user;
}
Future<User?> findByEmail(String email) async {
// Database query logic
return null;
}
}
Property Injection #
@Component()
class ApiClient {
@Value('#{api.baseUrl}')
late String baseUrl;
@Value('#{api.timeout:30}')
late int timeout;
@Value('#{api.apiKey}')
late String apiKey;
Future<Response> get(String endpoint) async {
final url = '$baseUrl$endpoint';
// HTTP request logic
}
}
Conditional Configuration #
// Conditional on property
@ConditionalOnProperty(
prefix: 'cache',
names: ['enabled'],
havingValue: 'true',
)
@Configuration()
class CacheConfig {
@Pod()
CacheManager cacheManager() => RedisCacheManager();
}
// Conditional on pod existence
@ConditionalOnPod(DatabaseConnection)
@Service()
class DatabaseMigrationService {
final DatabaseConnection db;
DatabaseMigrationService(this.db);
@PostConstruct()
Future<void> runMigrations() async {
// Migration logic
}
}
// Conditional on missing pod
@ConditionalOnMissingPod(CacheManager)
@Configuration()
class DefaultCacheConfig {
@Pod()
CacheManager cacheManager() => InMemoryCacheManager();
}
Application Runners #
Execute logic after application startup:
@Component()
class DataInitializer implements ApplicationRunner {
final UserRepository userRepository;
DataInitializer(this.userRepository);
@override
Future<void> run(ApplicationArguments args) async {
print('Initializing data...');
await userRepository.seedDefaultUsers();
}
}
@Component()
class CommandLineProcessor implements CommandLineRunner {
@override
Future<void> run(List<String> args) async {
print('Processing command line arguments: $args');
}
}
Event Handling #
// Define custom event
class UserRegisteredEvent extends ApplicationEvent {
final User user;
UserRegisteredEvent(Object source, this.user) : super(source);
}
// Publish event
@Service()
class UserService {
final ApplicationContext context;
UserService(this.context);
Future<User> registerUser(CreateUserRequest request) async {
final user = await userRepository.save(request);
// Publish event
await context.publishEvent(UserRegisteredEvent(this, user));
return user;
}
}
// Listen to event
@Component()
class UserEventListener implements ApplicationEventListener<UserRegisteredEvent> {
final EmailService emailService;
UserEventListener(this.emailService);
@override
Future<void> onApplicationEvent(UserRegisteredEvent event) async {
await emailService.sendWelcomeEmail(event.user.email);
}
}
Configuration Files #
JetLeaf supports multiple configuration file formats in the resources/
directory:
application.yaml #
jetleaf:
application:
name: MyApplication
version: 1.0.0
profiles:
active: development
server:
port: 8080
host: 0.0.0.0
database:
url: postgresql://localhost:5432/myapp
username: admin
password: secret
maxConnections: 20
logging:
level: INFO
pattern: "%d{yyyy-MM-dd HH:mm:ss} [%level] %logger - %msg"
application.properties #
jetleaf.application.name=MyApplication
jetleaf.application.version=1.0.0
jetleaf.profiles.active=development
server.port=8080
server.host=0.0.0.0
database.url=postgresql://localhost:5432/myapp
database.username=admin
database.password=secret
Profile-Specific Configuration #
application-dev.yaml
β Development profileapplication-prod.yaml
β Production profileapplication-test.yaml
β Test profile
CLI Tools #
JetLeaf provides CLI tools for development and deployment:
Development #
# Run in development mode with hot reload
jl dev
# Run with specific profile
jl dev --profile=development
Build and Deploy #
# Generate bootstrap file
jl generate
# Build production executable
jl build
# Generate and build
jl serve
# Run production build
dart run build/main.dill
API Reference #
Main Exports (lib/jetleaf.dart
) #
- Bootstrap:
BootstrapContext
- Application:
JetApplication
,JetLeafApplication
- Lang: Re-exports from
jetleaf_lang
(Class, Annotation, etc.) - Environment: Re-exports from
jetleaf_env
(Environment, PropertyResolver, etc.) - Core: Re-exports from
jetleaf_core
(ApplicationContext, annotations, etc.) - Convert: Re-exports from
jetleaf_convert
(ConversionService, Converters, etc.) - Pod: Re-exports from
jetleaf_pod
(PodFactory, PodDefinition, etc.)
Key Classes #
JetApplication
: Main application bootstrap class@JetLeafApplication
: Application entry point annotation@EnableAutoConfiguration
: Enable auto-configurationApplicationContext
: Central application containerConfigurableApplicationContext
: Configurable context interface
Annotations #
- Configuration:
@Configuration
,@AutoConfiguration
,@Pod
- Stereotypes:
@Component
,@Service
,@Repository
,@Controller
- Dependency Injection:
@Autowired
,@Value
,@Qualifier
,@RequiredAll
- Lifecycle:
@PostConstruct
,@PreDestroy
,@Lazy
- Conditional:
@Conditional
,@ConditionalOnProperty
,@ConditionalOnPod
- Others:
@Primary
,@Scope
,@Order
,@Profile
Examples #
REST API Application #
@JetLeafApplication()
class ApiApplication {
static void main(List<String> args) {
JetApplication.run(ApiApplication, args);
}
}
@Controller()
class UserController {
final UserService userService;
UserController(this.userService);
Future<Response> getUser(String id) async {
final user = await userService.findById(id);
return Response.ok(user);
}
}
Microservice with Database #
@JetLeafApplication()
@EnableAutoConfiguration()
class MicroserviceApplication {
static void main(List<String> args) {
JetApplication.run(MicroserviceApplication, args);
}
}
@Configuration()
class DatabaseConfig {
@Pod()
DatabaseConnection database(
@Value('#{database.url}') String url,
@Value('#{database.maxConnections}') int maxConnections,
) {
return DatabaseConnection(url: url, maxConnections: maxConnections);
}
}
Scheduled Tasks #
@Service()
class ScheduledTaskService {
@Scheduled(cron: '0 0 * * * *') // Every hour
Future<void> hourlyTask() async {
print('Running hourly task');
}
@Scheduled(fixedDelay: Duration(minutes: 5))
Future<void> periodicTask() async {
print('Running every 5 minutes');
}
}
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 test
and 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_core
,jetleaf_utils
,jetleaf_env
,jetleaf_pod
Built with π by the JetLeaf team.