avaje inject 8.0 API
Overview
Annotate classes with @Singleton
/ @Factory
and avaje-inject
will generate source code to "wire them".
1. @Singleton / @Factory
Put @Singleton
on beans that we want avaje-inject to wire.
We can use @Factory/@Bean
to programmatically create dependencies
when they have interesting construction logic (e.g. construction depends on
system/environment properties etc - like Spring @Configuration).
@Singleton
public class CoffeeMaker { ... }
@Singleton
public class Pump { ... }
// Use @Factory to programmatically build dependencies
@Factory
public class MyFactory {
@Bean
Grinder buildGrinder(Pump pump) { // interesting construction logic ... }
}
2. Create and use BeanScope
Create BeanScope
using a builder.
Obtain beans from the scope and use them.
We should ensure the BeanScope is closed in order to fire preDestroy lifecycle methods. We can do this via a shutdown hook, or try with resource block or explicitly via application code.
// create BeanScope
BeanScope scope = BeanScope.newBuilder()
.build();
// use it
CoffeeMaker coffeeMaker = scope.get(CoffeeMaker.class);
coffeeMaker.makeIt();
// close it to fire preDestroy lifecycle methods
scope.close();
Default scope
All beans annotated with @Singleton
and @Factory
are by default included in
the "default scope".
When we create a BeanScope and do not specify any modules then all the beans in the default scope are included. This is done via ServiceLoader and includes all 'default scope' modules that are in the classpath (other jars can have default scope modules and these are all included).
Generated code
avaje-inject will generate a $DI
class for each bean that it is going to
wire - this has the code that instantiates the bean, performs field and method injection and
lifecycle support (PostConstruct and PreDestroy).
avaje-inject will generate a Module class for the default scope. The main job of the module code is to specify the ordering of how all the beans are instantiated.
We typically find the generated source code in target/generate-sources/annotations.
Custom scopes
We can create custom scopes that only contain the beans/components that we want to include in that scope. These beans/components in the custom scope are not included in the default scope.
To do this we:
1. Create the scope annotation
Create an annotation that has the Scope
annotation. In the example below we create the
@StoreComponent
annotation. We will put this annotation on beans that we want
included in the scope.
@Scope
public @interface StoreComponent {
}
Note that if this scope depends on external dependencies or another scope we specify that via
@InjectModule requires
. In the example below our StoreComponent depends on another
scope called QueueComponent and an external dependency - SomeExternalDependency.
@Scope
@InjectModule(requires = {QueueComponent.class, SomeExternalDependency.class})
public @interface StoreComponent {
}
2. Use the annotation
Put the @StoreComponent
annotation on the beans that we want included in the
custom scope.
@StoreComponent
public class StoreLoader {
...
}
3. Generated Module
avaje-inject will generate a StoreComponentModule
with the appropriate
code to create/wire all the beans in the custom scope.
StoreComponentModule is typically found in target/generate-sources/annotations.
For each component we will also see a $DI
class which is the generated code
that creates the component.
4. Use the custom scope
To use the custom scope we specify the StoreComponentModule
when creating the
BeanScope. Only the components in our module will be create/wired into the BeanScope.
If the scope depends on another scope then we specify that using withParent()
.
BeanScope parentScope = ...
BeanScope scope = BeanScope.newBuilder()
.withModules(new StoreComponentModule())
.withParent(parentScope)
.build());
StoreLoader storeLoader = scope.get(StoreLoader.class);
storeLoader.load();