avaje inject 8.0 API


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).

    public class CoffeeMaker { ... }

    public class Pump { ... }

    // Use @Factory to programmatically build dependencies

    public class MyFactory {

      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()

    // use it
    CoffeeMaker coffeeMaker = scope.get(CoffeeMaker.class);

    // close it to fire preDestroy lifecycle methods


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.

     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.

     @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.

    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())

    StoreLoader storeLoader = scope.get(StoreLoader.class);