Annotation-processor-based dependency injection for server-side developers - https://avaje.io/inject
Maven
<dependency>
<groupId>io.avaje</groupId>
<artifactId>avaje-inject</artifactId>
<version>${avaje.inject.version}</version>
</dependency>
Gradle
implementation 'io.avaje:avaje-inject:${avaje.inject.version}'
Maven
<dependency>
<groupId>io.avaje</groupId>
<artifactId>avaje-inject-generator</artifactId>
<version>${avaje.inject.version}</version>
<scope>provided</scope>
</dependency>
Gradle
annotationProcessor 'io.avaje:avaje-inject-generator:${avaje.inject.version}'
Maven
<dependency>
<groupId>io.avaje</groupId>
<artifactId>avaje-inject-test</artifactId>
<version>${avaje.inject.version}</version>
<scope>test</scope>
</dependency>
Gradle
testImplementation 'io.avaje:avaje-inject-test:${avaje.inject.version}'
@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();
}
}
BeanScope beanScope = BeanScope.builder().build()
Example ex = beanScope.get(Example.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 {
/**
* 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(builder);
build_example_DependencyClass(builder);
build_example_DependencyClass2(builder);
build_example_Example(builder);
}
@DependencyMeta(type = "org.example.ExampleFactory")
private void build_example_ExampleFactory(Builder builder) {
ExampleFactory$DI.build(builder);
}
@DependencyMeta(type = "org.example.DependencyClass")
private void build_example_DependencyClass(Builder builder) {
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(Builder builder) {
ExampleFactory$DI.build_bean(builder);
}
@DependencyMeta(
type = "org.example.Example",
dependsOn = {"org.example.DependencyClass", "org.example.DependencyClass2"})
private void build_example_Example(Builder builder) {
Example$DI.build(builder);
}
}
avaje-inject-test and @InjectTest@PostConstruct and @PreDestroy@Factory and @Bean| 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 |
| @Lazy | - | @Lazy |
| @Prototype | - | @Scope("prototype") |
| @Primary | - | @Primary |
| @Secondary | - | @Fallback |
| @InjectTest | - | @SpringBootTest |
$ claude mcp add avaje-inject \
-- python -m otcore.mcp_server <graph>