jetleaf 1.0.0+1 copy "jetleaf: ^1.0.0+1" to clipboard
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.

Contents #

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 profile
  • application-prod.yaml – Production profile
  • application-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-configuration
  • ApplicationContext: Central application container
  • ConfigurableApplicationContext: 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.

  1. Fork and create a feature branch.
  2. Add tests for new functionality.
  3. Run dart test and ensure lints pass.
  4. 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.

1
likes
130
points
57
downloads

Publisher

verified publisherjetleaf.hapnium.com

Weekly Downloads

A comprehensive, enterprise-grade Dart backend framework that brings Spring-like dependency injection, configuration management, and application lifecycle to Dart server-side development.

Homepage
Repository (GitHub)
View/report issues

Topics

#dart-backend #ioc #dependency-injection #configuration-management #application-framework

Documentation

API reference

License

BSD-2-Clause (license)

Dependencies

jetleaf_convert, jetleaf_core, jetleaf_env, jetleaf_lang, jetleaf_logging, jetleaf_pod, jetleaf_utils, meta

More

Packages that depend on jetleaf