dryice
dryice copied to clipboard
Dependency injection framework for Dart
DryIce (built mirrors / reflectable)
Lightweight dependency injection framework for Dart. No need for mirrors!
Getting Started
DryIce consists of two parts.
- Modules containing your class registrations.
- Injectors that uses the Module to inject instances into your code.
The following example should get you started:
1. Add DryIce to your pubspec.yaml and run pub install
dependencies:
dryice: any
2. Create some classes and interfaces to inject
@injectable
abstract class BillingService {
Receipt chargeOrder(Order order, CreditCard creditCard);
}
@injectable
class BillingServiceImpl implements BillingService {
@inject
CreditProcessor _processor;
Receipt chargeOrder(Order order, CreditCard creditCard) {
if(!_processor.validate(creditCard)) {
throw new ArgumentError("payment method not accepted");
}
// :
}
}
3. Register types and classes in a module
class ExampleModule extends Module {
@override
configure() {
// register [CreditProcessor] as a singleton
bind(CreditProcessor).to(CreditProcessorImpl).asSingleton();
// register [BillingService] so a new version is created each time its requested
bind(BillingService).toType(BillingServiceImpl);
}
}
4. Run it
import "package:dryice/dryice.dart";
// This is the file that gets created
// if you run 'pub run build_runner build'
import 'main.reflectable.dart';
main() {
initializeReflectable();
final injector = new Injector(new ExampleModule());
final billingService = injector.getInstance(BillingService);
final creditCard = new CreditCard("VISA");
final order = new Order("Dart: Up and Running");
billingService.chargeOrder(order, creditCard);
}
for more information see the full example here.
Dependency Injection with DryIce
You can use the @injectable annotation to mark classes as injectable, use @inject annotation to mark objects, functions and constructors for injection the following ways: (It is not necessary to mark a default constructor with @inject - only complex CTORs must be marked)
- Injection of public and private fields (object/instance variables)
@injectable
class MyOtherClass {
@inject
SomeClass field;
@inject
SomeOtherClass _privateField;
}
- Injection of constructor parameters
@injectable
class MyClass {
@inject
MyClass(this.field);
MyOtherClass field;
}
- Injection of public and private setters
@injectable
class SomeClass {
@inject
set value(SomeOtherClass val) => _privateValue = val;
@inject
set _value(SomeOtherClass val) => _anotherPrivateValue = val;
SomeOtherClass _privateValue, _anotherPrivateValue;
}
The injected objects are configured either by extending the Module class and using one of its bind functions or directly on the Injector.
- register type MyType.
bind(MyType)
- register interface MyType to a class implementing it.
bind(MyType).toType(MyTypeImpl)
- register a singleton
bind(MyType).to(MySuperType).asSingleton();
- register type MyType to existing object (another way for singleton injections)
bind(MyType).toInstance(object)
- register a typedef to a function matching it.
bind(MyTypedef).toFunction(function)
- register MyType to function that can build instances of it
bind(MyType).toBuilder(() => new MyType())
- use Module to install other modules configuration
class MyApplicationModule extends Module {
@override
configure() {
install(new ComponentModule());
bind(Emailer).to(EmailerToGMX).asSingleton();
}
}
Named Injections
DryIce supports named injections by using the @Named annotation. Currently this annotation works everywhere the @inject annotation works.
class MyClass {
@inject
@Named('my-special-implementation')
SomeClass _someClass;
}
The configuration is as before except you now provide an additional name paramater.
bind(MyType, named: "my-name").toType(MyTypeImpl)
The configuration is as before except you now provide an additional name paramater.
Annotated (typed) Injections
You can also use other classes for annotation. works everywhere the @inject annotation works.
@injectable
class UrlGoogle { const UrlGoogle(); }
@injectable
class UrlFacebook { const UrlFacebook(); }
class MyModule extends Module {
@override
configure() {
// annotated
bind(String,annotatedWith: UrlGoogle ).toInstance("http://www.google.com/");
bind(String,annotatedWith: UrlFacebook ).toInstance("http://www.facebook.com/");
}
}
@injectable
class MyClass {
@inject
@UrlGoogle()
String url;
}
The configuration is as before except you now provide an additional annotation.
Advanced Features
- Get instances directly Instead of using the @inject annotation to resolve injections you can use the injectors getInstance method.
MyClass instance = injector.getInstance(MyClass);
- Get named instances directly Instead of using the @Named annotation to resolve named injections you can use the injectors getInstance method with its named parameter.
MyType instance = injector.getInstance(MyType, named: "my-name");
- Get annotated instances directly Instead of using the appropriate annotation to resolve annotated injections you can use the injectors getInstance method with its annotatedWith parameter.
String url = injector.getInstance(MyType, annotatedWith: UrlGoogle);
- To register and resole configuration values You can use named or annotated registrations to inject configuration values into your application.
class TestModule extends Module {
configure() {
bind(String, named: "web-service-host").toInstace("http://test-service.name");
bind(String, annotatedWith: UrlGoogle ).toInstance("http://www.google.com/");
}
}
// application code
String get webServiceHost => injector.getInstance(String, named: "web-service-host");
String get webServiceHost2 => injector.getInstance(String, annotatedWith: UrlGoogle);
- Constructor injection DryIce also support constructors with optional params.
@injectable
class MyClass {
String getName() => "MyClass";
}
@injectable
class CTOROptionalInjection extends MyClass {
final String url;
final String lang;
@inject
CTOROptionalInjection(@UrlGoogle() final String this.url,[ final String language ])
: lang = language ?? "C++";
@override
String getName() => "CTORInjection - $url ($lang)";
}
final injector = new Injector()
..bind(String,annotatedWith: UrlGoogle ).toInstance("http://www.google.com/")
..bind(MyClass).toType(CTOROptionalInjection)
;
final MyClass mc = injector.getInstance(MyClass);
- Registering dependencies at runtime You can bind dependencies at runtime directly on the Injector.
injector.bind(User).toInstance(user);
var user = injector.getInstance(User);
- Unregistering dependencies at runtime You can unregister dependencies at runtime using the unregister method on the Injector.
injector.unregister(User);
- Using multiple modules You can compose modules using the Injector.fromModules constructor.
class MyModule extends Module {
configure() {
register(MyClass).toType(MyClass);
}
}
class YourModule extends Module {
configure() {
register(YourClass).toType(YourClass);
}
}
var injector = new Injector.fromModules([new MyModule(), new YourModule()]);
var myClass = injector.getInstance(MyClass);
var yourClass = injector.getInstance(YourClass);
- Install other modules within main module
class MyModule extends Module {
configure() {
register(MyClass).toType(MyClass);
}
}
class MyMainModule extends Module {
configure() {
install(new MyModule());
register(YourClass).toType(YourClass);
}
}
var injector = new Injector( new MyMainModule());
var myClass = injector.getInstance(MyClass);
var yourClass = injector.getInstance(YourClass);
- Joining injectors You can join multiple injector instances to one using the Injector.fromInjectors constructor.
var myInjector = new Injector();
myInjector.register(MyClass).toType(MyClass);
var yourInjector = new Injector();
yourInjector.register(YourClass).toType(YourClass);
var injector = new Injector.fromInjectors([myInjector, yourInjector]);
var myClass = injector.getInstance(MyClass);
var yourClass = injector.getInstance(YourClass);
Compatibility / migration from di:package
To make migration easier we provide the following functions:
-
Injector.bind
is the same asInjector.register
. -
Module.bind
is the same asModule.register
-
Injector.get
is the same asInjector.getInstance
-
Registration.to
is the same asRegistration.toType
Be aware that Injector.register
and may become
depreciated in one of the next releases.
Prefer the bind
, to and the
get` version over its equivalent.
Thanks
This package is based "Dice" - Thanks Lars Tackmann!