From f908ebd36a81db80ea30bdd39bdfc94f3fe405e8 Mon Sep 17 00:00:00 2001 From: Josiah Noel <32279667+SentryMan@users.noreply.github.com> Date: Fri, 28 Nov 2025 15:58:26 -0500 Subject: [PATCH 1/4] generate components before the last round --- .../io/avaje/inject/generator/AllScopes.java | 1 + .../inject/generator/InjectProcessor.java | 27 ++++++++++++------- .../inject/generator/ProcessingContext.java | 9 ++++--- 3 files changed, 23 insertions(+), 14 deletions(-) diff --git a/inject-generator/src/main/java/io/avaje/inject/generator/AllScopes.java b/inject-generator/src/main/java/io/avaje/inject/generator/AllScopes.java index a6385e80d..ba316fe64 100644 --- a/inject-generator/src/main/java/io/avaje/inject/generator/AllScopes.java +++ b/inject-generator/src/main/java/io/avaje/inject/generator/AllScopes.java @@ -43,6 +43,7 @@ void readBeans(RoundEnvironment roundEnv) { for (Data data : scopeAnnotations.values()) { for (Element customBean : roundEnv.getElementsAnnotatedWith(data.type)) { if (customBean instanceof TypeElement) { + ProcessingContext.processingOver(false); data.scopeInfo.read((TypeElement) customBean, false, false); } } diff --git a/inject-generator/src/main/java/io/avaje/inject/generator/InjectProcessor.java b/inject-generator/src/main/java/io/avaje/inject/generator/InjectProcessor.java index 6d3956c37..37fd811b4 100644 --- a/inject-generator/src/main/java/io/avaje/inject/generator/InjectProcessor.java +++ b/inject-generator/src/main/java/io/avaje/inject/generator/InjectProcessor.java @@ -6,6 +6,7 @@ import static io.avaje.inject.generator.ProcessingContext.delayedElements; import static io.avaje.inject.generator.ProcessingContext.loadMetaInfCustom; import static io.avaje.inject.generator.ProcessingContext.loadMetaInfServices; +import static io.avaje.inject.generator.ProcessingContext.processingOver; import static java.util.stream.Collectors.joining; import static java.util.stream.Collectors.toList; @@ -71,6 +72,8 @@ public final class InjectProcessor extends AbstractProcessor { private final Set pluginFileProvided = new HashSet<>(); private final Set moduleFileProvided = new HashSet<>(); private final List moduleData = new ArrayList<>(); + private int rounds; + @Override public SourceVersion getSupportedSourceVersion() { @@ -143,15 +146,14 @@ private List lines(String relativeName) { @Override public boolean process(Set annotations, RoundEnvironment roundEnv) { - if (roundEnv.errorRaised()) { + if (processingOver() || roundEnv.errorRaised()) { return false; } + processingOver(rounds++ > 0); APContext.setProjectModuleElement(annotations, roundEnv); readModule(roundEnv); - final var processingOver = roundEnv.processingOver(); - ProcessingContext.processingOver(processingOver); readBeans(delayedElements()); addImportedAspects(importedAspects(roundEnv)); @@ -192,11 +194,12 @@ public boolean process(Set annotations, RoundEnvironment maybeElements(roundEnv, ServiceProviderPrism.PRISM_TYPE).ifPresent(this::registerSPI); maybeElements(roundEnv, PluginProvidesPrism.PRISM_TYPE).ifPresent(this::registerSPI); allScopes.readBeans(roundEnv); - defaultScope.write(processingOver); - allScopes.write(processingOver); + final var over = processingOver(); + defaultScope.write(over); + allScopes.write(over); - if (processingOver) { - var order = + if (processingOver()) { + final var order = new FactoryOrder(ProcessingContext.modules(), defaultScope.pluginProvided()) .orderModules(); @@ -234,8 +237,12 @@ private void validateQualifier(ExecutableElement method) { } // Optional because these annotations are not guaranteed to exist - private static Optional> maybeElements(RoundEnvironment round, String name) { - return Optional.ofNullable(typeElement(name)).map(round::getElementsAnnotatedWith); + private Optional> maybeElements( + RoundEnvironment round, String name) { + final var op = Optional.ofNullable(typeElement(name)).map(round::getElementsAnnotatedWith); + + processingOver(processingOver() && op.filter(n -> !n.isEmpty()).isEmpty()); + return op; } private Set importedElements(RoundEnvironment roundEnv) { @@ -264,7 +271,7 @@ private boolean notAlreadyProvided(TypeElement e) { return !moduleFileProvided.contains(type) && !pluginFileProvided.contains(type); } - private static Map importedAspects(RoundEnvironment roundEnv) { + private Map importedAspects(RoundEnvironment roundEnv) { return maybeElements(roundEnv, AspectImportPrism.PRISM_TYPE).stream() .flatMap(Set::stream) .map(AspectImportPrism::getAllInstancesOn) diff --git a/inject-generator/src/main/java/io/avaje/inject/generator/ProcessingContext.java b/inject-generator/src/main/java/io/avaje/inject/generator/ProcessingContext.java index 290a22ac4..a63ab89d7 100644 --- a/inject-generator/src/main/java/io/avaje/inject/generator/ProcessingContext.java +++ b/inject-generator/src/main/java/io/avaje/inject/generator/ProcessingContext.java @@ -232,10 +232,7 @@ static Set delayedElements() { } static boolean delayUntilNextRound(TypeElement element) { - if (!processingOver) { - CTX.get().delayQueue.add(element); - } - return !processingOver; + return CTX.get().delayQueue.add(element); } static void clear() { @@ -263,6 +260,10 @@ static void processingOver(boolean over) { processingOver = over; } + static boolean processingOver() { + return processingOver; + } + static void writeSPIServicesFile() { addEventSPI(); readExistingMetaInfServices(); From 3aa3dee15185ad45bf2a63f1714c1953ead60551 Mon Sep 17 00:00:00 2001 From: Josiah Noel <32279667+SentryMan@users.noreply.github.com> Date: Sat, 29 Nov 2025 23:24:13 -0500 Subject: [PATCH 2/4] Delete Java Module Usage instructions Removed Java module usage section from README. (we flag this via compile error) --- README.md | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/README.md b/README.md index 87dc0f8bc..a0c5a31d3 100644 --- a/README.md +++ b/README.md @@ -59,19 +59,6 @@ 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. -```java -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. From 2cf6091bbbef922d1e3440344cae239164c01513 Mon Sep 17 00:00:00 2001 From: Josiah Noel <32279667+SentryMan@users.noreply.github.com> Date: Sat, 29 Nov 2025 23:29:31 -0500 Subject: [PATCH 3/4] Update ExampleModule to use Builder in methods Refactor methods to accept Builder parameter for dependency injection. --- README.md | 28 ++++++++-------------------- 1 file changed, 8 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index a0c5a31d3..6e253792d 100644 --- a/README.md +++ b/README.md @@ -67,18 +67,6 @@ The inject annotation processor determines the dependency wiring order and gener @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 @@ -89,19 +77,19 @@ public final class ExampleModule implements AvajeModule { 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(); + 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() { + private void build_example_ExampleFactory(Builder builder) { ExampleFactory$DI.build(builder); } @DependencyMeta(type = "org.example.DependencyClass") - private void build_example_DependencyClass() { + private void build_example_DependencyClass(Builder builder) { DependencyClass$DI.build(builder); } @@ -109,14 +97,14 @@ public final class ExampleModule implements AvajeModule { 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() { + 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() { + private void build_example_Example(Builder builder) { Example$DI.build(builder); } } From 2b144ea6e0651650d6c004d9236d4c7fba930a04 Mon Sep 17 00:00:00 2001 From: Rob Bygrave Date: Mon, 1 Dec 2025 08:05:25 +1300 Subject: [PATCH 4/4] Format and inline comment only --- .../java/io/avaje/inject/generator/InjectProcessor.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/inject-generator/src/main/java/io/avaje/inject/generator/InjectProcessor.java b/inject-generator/src/main/java/io/avaje/inject/generator/InjectProcessor.java index 37fd811b4..f9914948a 100644 --- a/inject-generator/src/main/java/io/avaje/inject/generator/InjectProcessor.java +++ b/inject-generator/src/main/java/io/avaje/inject/generator/InjectProcessor.java @@ -154,7 +154,6 @@ public boolean process(Set annotations, RoundEnvironment APContext.setProjectModuleElement(annotations, roundEnv); readModule(roundEnv); - readBeans(delayedElements()); addImportedAspects(importedAspects(roundEnv)); maybeElements(roundEnv, QualifierPrism.PRISM_TYPE).stream() @@ -237,10 +236,11 @@ private void validateQualifier(ExecutableElement method) { } // Optional because these annotations are not guaranteed to exist - private Optional> maybeElements( - RoundEnvironment round, String name) { - final var op = Optional.ofNullable(typeElement(name)).map(round::getElementsAnnotatedWith); + private Optional> maybeElements(RoundEnvironment round, String name) { + final var op = Optional.ofNullable(typeElement(name)) + .map(round::getElementsAnnotatedWith); + // reset processingOver flag if anything needs processing in this round processingOver(processingOver() && op.filter(n -> !n.isEmpty()).isEmpty()); return op; }