-
-
Notifications
You must be signed in to change notification settings - Fork 31
Panda Utilities: Dependency Injection
The lightweight and actively developed built-in dependency injection library. Supported operations:
- Creating a new instance of the specified type using Injector (constructors)
- Invoking methods using Injector (methods)
Full example can be found as a test class: DependencyInjectionTest.java
Firstly, you have to create Injector which keeps all the registered bindings (injected values)
DependencyInjection.createInjector()
// If you are able to register binding at the init time, it's also recommended to use the following structure
Injector injector = DependencyInjection.createInjector(resources -> {
// bind resources
});Injector supports three main ways to bind with a value:
- Binding to the specified type
resources.on(<Type>) - Binding to the specified annotation
resources.annotatedWith(<Annotation>) -
[Experimental] Binding to the specified annotation mapped into the metadata type. It might be useful to bind similar annotations (annotations cannot be inherited) and it's required for *wired structures
resources.annotatedWithMetadata(<Annotation>)
Each binding supports three ways of assigning value:
-
assign(<Type>)- creates a new instance of type per call -
assignInstance(<Object>)/assignInstance(Supplier<Object>)- binds the specified value/some kind of lazy values -
assignHandler((<Expected Type Of Value>, <Annotation>) -> { /* logic */ })- binds custom handler
Let's build random example based on these methods
@Injectable // mark annotation as DI ready annotation
@Retention(RetentionPolicy.RUNTIME) // make sure that the annotation is visible at runtime
@interface AwesomeRandom { }
static final class Entity {
private final UUID id;
@Inject //it's not required, but it might be useful to disable "unused method" warnings/scanning for annotations
private Entity(@AwesomeRandom UUID random) {
this.id = random;
}
public UUID getId() {
return id;
}
}We'd like to generate a new id per each Entity instance with a private constructor. It's also important for us, to support id in two forms:
- String
- UUID
Injector injector = DependencyInjection.createInjector(resources -> {
resources.annotatedWith(AwesomeRandom.class).assignHandler((expectedType, annotation) -> {
if (expectedType.equals(String.class)) {
return UUID.randomUUID().toString();
}
if (expectedType.equals(UUID.class)) {
return UUID.randomUUID();
}
throw new IllegalArgumentException("Unsupported type " + expectedType);
});
});
// Create entities using the injector instance
Entity entityA = injector.newInstance(Entity.class);
Entity entityB = injector.newInstance(Entity.class);
// Print generated values
System.out.println(entityA.getId());
System.out.println(entityB.getId());The output produces some random identifiers as intended 👍
e23442b2-f695-41fa-9290-0f1192118a1a
9f92121c-096e-4bdb-b6ad-0901974bbe37
Process finished with exit code 0
Full example is available here -> DependencyInjectionWikiTest.java
Wired, or to be more appropriate - weird, structures allow us to annotate properties using the metadata format. It should be treated only as some kind of funny feature and should not be used in a serious projects.
@Wired({
@Wired.Link(parameter = "value", with = CustomAnnotation.class, value = "hello-wired")
})
void wired(String value) { }Considering the possibilities of wired annotations, it might be tricky workaround to use some external annotations which are not marked as @Injectable and to change (not improve) the readability of methods with a hundred of annotated parameters.