Skip to content

SentryMan/avaje-inject

This branch is 13 commits ahead of, 152 commits behind avaje/avaje-inject:master.

Folders and files

NameName
Last commit message
Last commit date

Latest commit

9cf3ced · Nov 19, 2024
Oct 30, 2024
Aug 28, 2024
Nov 10, 2024
Nov 10, 2024
Nov 19, 2024
Nov 10, 2024
Nov 10, 2024
Nov 19, 2024
Nov 19, 2024
Nov 19, 2024
Nov 19, 2024
Nov 19, 2024
Nov 7, 2018
Nov 18, 2024
Jun 19, 2023
Sep 9, 2020
Oct 17, 2024
Aug 29, 2024
Jul 19, 2024
Aug 29, 2024
Jul 19, 2024
Nov 10, 2024
Aug 29, 2024

Repository files navigation

Discord Build JDK EA License Maven Central javadoc

APT-based dependency injection for server-side developers - https://avaje.io/inject

Quick Start

1. Add avaje-inject as a dependency.

<dependency>
  <groupId>io.avaje</groupId>
  <artifactId>avaje-inject</artifactId>
  <version>${avaje.inject.version}</version>
</dependency>

2. Add avaje-inject-generator annotation processor as a dependency with provided scope.

<dependency>
  <groupId>io.avaje</groupId>
  <artifactId>avaje-inject-generator</artifactId>
  <version>${avaje.inject.version}</version>
  <scope>provided</scope>
</dependency>

3. Create a Bean Class annotated with @Singleton

@Singleton
public class Example {

 private DependencyClass d1;
 private DependencyClass2 d2;

  // Dependencies must be annotated with singleton,
  // or else be provided from another class annotated with @Factory
  public Example(DependencyClass d1, DependencyClass2 d2) {
    this.d1 = d1;
    this.d2 = d2;
  }
}

Example factory class:

@Factory
public class ExampleFactory {
  @Bean
  public DependencyClass2 bean() {
    return new DependencyClass2();
  }
}

4. Use BeanScope to wire and retrieve the beans and use them however you wish.

BeanScope beanScope = BeanScope.builder().build()
Example ex = beanScope.get(Example.class);

Java Module Usage

When working with Java modules you need to add a provides statement in your module-info.java with the generated class.

import io.avaje.inject.spi.InjectExtension;

module org.example {

  requires io.avaje.inject;
  // you must define the fully qualified class name of the generated classes. if you use an import statement, compilation will fail
  provides InjectExtension with org.example.ExampleModule;
}

Generated Wiring Class

The inject annotation processor determines the dependency wiring order and generates an AvajeModule class that calls all the generated DI classes.

@Generated("io.avaje.inject.generator")
@InjectModule
public final class ExampleModule implements AvajeModule {

  private Builder builder;

  @Override
  public Class<?>[] classes() {
    return new Class<?>[] {
      org.example.DependencyClass.class,
      org.example.DependencyClass2.class,
      org.example.Example.class,
      org.example.ExampleFactory.class,
    };
  }

  /**
   * Creates all the beans in order based on constructor dependencies. The beans are registered
   * into the builder along with callbacks for field/method injection, and lifecycle
   * support.
   */
  @Override
  public void build(Builder builder) {
    this.builder = builder;
    // create beans in order based on constructor dependencies
    // i.e. "provides" followed by "dependsOn"
    build_example_ExampleFactory();
    build_example_DependencyClass();
    build_example_DependencyClass2();
    build_example_Example();
  }

  @DependencyMeta(type = "org.example.ExampleFactory")
  private void build_example_ExampleFactory() {
    ExampleFactory$DI.build(builder);
  }

  @DependencyMeta(type = "org.example.DependencyClass")
  private void build_example_DependencyClass() {
    DependencyClass$DI.build(builder);
  }

  @DependencyMeta(
      type = "org.example.DependencyClass2",
      method = "org.example.ExampleFactory$DI.build_bean", // factory method
      dependsOn = {"org.example.ExampleFactory"}) //factory beans naturally depend on the factory
  private void build_example_DependencyClass2() {
    ExampleFactory$DI.build_bean(builder);
  }

  @DependencyMeta(
      type = "org.example.Example",
      dependsOn = {"org.example.DependencyClass", "org.example.DependencyClass2"})
  private void build_example_Example() {
    Example$DI.build(builder);
  }
}

Similar to Dagger

  • Uses Java annotation processing for dependency injection
  • Generates source code
  • Avoids any use of reflection or classpath scanning (so low overhead and fast startup)

Differences to Dagger

  • Specifically aimed for server-side development (rather than Android)
  • Supports "component testing" via avaje-inject-test and @InjectTest
  • Provides API to obtain all bean instances that implement an interface
  • Lifecycle methods with @PostConstruct and @PreDestroy
  • Spring-like factory classes with @Factory and @Bean
  • Conditional Wiring based on active profiles or existing beans/properties

DI Framework comparison

Avaje Dagger Spring
@Singleton @Singleton @Component, @Service, @Repository
Provider<T> Provider<T> FactoryBean<T>
@Inject @Inject @Inject, @Autowired
@Inject @Nullable or @Inject Optional<T> @Inject @Nullable @Autowired(required=false)
@Qualifier/@Named @Qualifier/@Named @Qualifier
@AssistFactory @AssistedFactory -
@PostConstruct - @PostConstruct
@PreDestroy - @PreDestroy
@Factory and @Bean - @Configuration and @Bean
@RequiresBean and @RequiresProperty - @Conditional
@Primary - @Primary
@Secondary - @Fallback
@InjectTest - @SpringBootTest

About

Dependency injection via APT (source code generation) ala "Server side Dagger DI"

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

  • Java 99.1%
  • Shell 0.9%