diff --git a/blackbox-multi-scope/pom.xml b/blackbox-multi-scope/pom.xml index 017719bb..d8356987 100644 --- a/blackbox-multi-scope/pom.xml +++ b/blackbox-multi-scope/pom.xml @@ -31,7 +31,12 @@ 1.5 test - + + org.junit.jupiter + junit-jupiter-params + 5.12.1 + test + diff --git a/blackbox-multi-scope/src/main/java/org/multi/crosscut/BeanCross2.java b/blackbox-multi-scope/src/main/java/org/multi/crosscut/BeanCross2.java index a6916d41..0c909e34 100644 --- a/blackbox-multi-scope/src/main/java/org/multi/crosscut/BeanCross2.java +++ b/blackbox-multi-scope/src/main/java/org/multi/crosscut/BeanCross2.java @@ -1,8 +1,7 @@ package org.multi.crosscut; import org.multi.moda.BeanInModA; -import org.multi.modb.BeanInModB; -import org.multi.modc.modb.BeanInModC; +import org.multi.modc.BeanInModC; import org.multi.scope.CrossCutScope; @CrossCutScope diff --git a/blackbox-multi-scope/src/main/java/org/multi/crosscut/BeanCross3.java b/blackbox-multi-scope/src/main/java/org/multi/crosscut/BeanCross3.java index e8ce4701..b1a6bbd2 100644 --- a/blackbox-multi-scope/src/main/java/org/multi/crosscut/BeanCross3.java +++ b/blackbox-multi-scope/src/main/java/org/multi/crosscut/BeanCross3.java @@ -1,6 +1,6 @@ package org.multi.crosscut; -import org.multi.modc.modb.COther; +import org.multi.modc.COther; import org.multi.scope.CrossCutScope; @CrossCutScope diff --git a/blackbox-multi-scope/src/main/java/org/multi/main/CrossCutMain.java b/blackbox-multi-scope/src/main/java/org/multi/main/CrossCutMain.java index 45452311..e936ed07 100644 --- a/blackbox-multi-scope/src/main/java/org/multi/main/CrossCutMain.java +++ b/blackbox-multi-scope/src/main/java/org/multi/main/CrossCutMain.java @@ -1,6 +1,5 @@ package org.multi.main; -import io.avaje.inject.BeanScope; import org.multi.crosscut.BeanCross; import org.multi.crosscut.BeanCross2; import org.multi.crosscut.BeanCross3; @@ -9,7 +8,9 @@ import org.multi.moda.ModAModule; import org.multi.modb.BeanInModB; import org.multi.modb.ModBModule; -import org.multi.modc.modb.ModCModule; +import org.multi.modc.ModCModule; + +import io.avaje.inject.BeanScope; public class CrossCutMain { diff --git a/blackbox-multi-scope/src/main/java/org/multi/many/BeanInMany.java b/blackbox-multi-scope/src/main/java/org/multi/many/BeanInMany.java new file mode 100644 index 00000000..4ee74cde --- /dev/null +++ b/blackbox-multi-scope/src/main/java/org/multi/many/BeanInMany.java @@ -0,0 +1,20 @@ +package org.multi.many; + +import org.multi.moda.BeanInModA; +import org.multi.modc.COther; +import org.multi.mode.BeanInModE; +import org.multi.scope.ManyScope; + +@ManyScope +public class BeanInMany { + + private final BeanInModE beanInModE; + private final COther cOther; + private final BeanInModA modA; + + public BeanInMany(final BeanInModE beanInModE, final COther cOther, final BeanInModA modA) { + this.beanInModE = beanInModE; + this.cOther = cOther; + this.modA = modA; + } +} diff --git a/blackbox-multi-scope/src/main/java/org/multi/modb/BOther.java b/blackbox-multi-scope/src/main/java/org/multi/modb/BOther.java index 5fc5cd16..9ef1fd2c 100644 --- a/blackbox-multi-scope/src/main/java/org/multi/modb/BOther.java +++ b/blackbox-multi-scope/src/main/java/org/multi/modb/BOther.java @@ -1,6 +1,6 @@ package org.multi.modb; -import org.multi.modc.modb.COther; +import org.multi.modc.COther; import org.multi.scope.ModBScope; @ModBScope diff --git a/blackbox-multi-scope/src/main/java/org/multi/modc/modb/BeanInModC.java b/blackbox-multi-scope/src/main/java/org/multi/modc/BeanInModC.java similarity index 72% rename from blackbox-multi-scope/src/main/java/org/multi/modc/modb/BeanInModC.java rename to blackbox-multi-scope/src/main/java/org/multi/modc/BeanInModC.java index ff783368..f7a80f7a 100644 --- a/blackbox-multi-scope/src/main/java/org/multi/modc/modb/BeanInModC.java +++ b/blackbox-multi-scope/src/main/java/org/multi/modc/BeanInModC.java @@ -1,4 +1,4 @@ -package org.multi.modc.modb; +package org.multi.modc; import org.multi.scope.ModCScope; diff --git a/blackbox-multi-scope/src/main/java/org/multi/modc/modb/COther.java b/blackbox-multi-scope/src/main/java/org/multi/modc/COther.java similarity index 71% rename from blackbox-multi-scope/src/main/java/org/multi/modc/modb/COther.java rename to blackbox-multi-scope/src/main/java/org/multi/modc/COther.java index adec1e6b..824c72ab 100644 --- a/blackbox-multi-scope/src/main/java/org/multi/modc/modb/COther.java +++ b/blackbox-multi-scope/src/main/java/org/multi/modc/COther.java @@ -1,4 +1,4 @@ -package org.multi.modc.modb; +package org.multi.modc; import org.multi.scope.ModCScope; diff --git a/blackbox-multi-scope/src/main/java/org/multi/modd/BeanInModD.java b/blackbox-multi-scope/src/main/java/org/multi/modd/BeanInModD.java new file mode 100644 index 00000000..063caf9a --- /dev/null +++ b/blackbox-multi-scope/src/main/java/org/multi/modd/BeanInModD.java @@ -0,0 +1,14 @@ +package org.multi.modd; + +import org.multi.moda.BeanInModA; +import org.multi.scope.ModDScope; + +@ModDScope +public class BeanInModD { + + private final BeanInModA beanA; + + public BeanInModD(final BeanInModA beanInModA){ + this.beanA = beanInModA; + } +} diff --git a/blackbox-multi-scope/src/main/java/org/multi/mode/BeanInModE.java b/blackbox-multi-scope/src/main/java/org/multi/mode/BeanInModE.java new file mode 100644 index 00000000..f1cd2a77 --- /dev/null +++ b/blackbox-multi-scope/src/main/java/org/multi/mode/BeanInModE.java @@ -0,0 +1,7 @@ +package org.multi.mode; + +import org.multi.scope.ModEScope; + +@ModEScope +public class BeanInModE { +} diff --git a/blackbox-multi-scope/src/main/java/org/multi/scope/CrossCutScope.java b/blackbox-multi-scope/src/main/java/org/multi/scope/CrossCutScope.java index 0534a375..eca27885 100644 --- a/blackbox-multi-scope/src/main/java/org/multi/scope/CrossCutScope.java +++ b/blackbox-multi-scope/src/main/java/org/multi/scope/CrossCutScope.java @@ -2,9 +2,8 @@ import io.avaje.inject.InjectModule; import jakarta.inject.Scope; -import org.multi.modb.BeanInModB; @Scope -@InjectModule(requires = {ModAScope.class, ModBScope.class}, strictWiring = true) +@InjectModule(automaticallyImport = {ModAScope.class, ModBScope.class}, strictWiring = true) public @interface CrossCutScope { } diff --git a/blackbox-multi-scope/src/main/java/org/multi/scope/ManyScope.java b/blackbox-multi-scope/src/main/java/org/multi/scope/ManyScope.java new file mode 100644 index 00000000..ee12347a --- /dev/null +++ b/blackbox-multi-scope/src/main/java/org/multi/scope/ManyScope.java @@ -0,0 +1,9 @@ +package org.multi.scope; + +import io.avaje.inject.InjectModule; +import jakarta.inject.Scope; + +@Scope +@InjectModule(automaticallyImport = {ModDScope.class, CrossCutScope.class, ModEScope.class}, strictWiring = true) +public @interface ManyScope { +} diff --git a/blackbox-multi-scope/src/main/java/org/multi/scope/ModBScope.java b/blackbox-multi-scope/src/main/java/org/multi/scope/ModBScope.java index ab6b1ae9..6293fff1 100644 --- a/blackbox-multi-scope/src/main/java/org/multi/scope/ModBScope.java +++ b/blackbox-multi-scope/src/main/java/org/multi/scope/ModBScope.java @@ -4,6 +4,6 @@ import jakarta.inject.Scope; @Scope -@InjectModule(requires = ModCScope.class, strictWiring = true) +@InjectModule(automaticallyImport = ModCScope.class, strictWiring = true) public @interface ModBScope { } diff --git a/blackbox-multi-scope/src/main/java/org/multi/scope/ModDScope.java b/blackbox-multi-scope/src/main/java/org/multi/scope/ModDScope.java new file mode 100644 index 00000000..b0b9ade5 --- /dev/null +++ b/blackbox-multi-scope/src/main/java/org/multi/scope/ModDScope.java @@ -0,0 +1,10 @@ +package org.multi.scope; + +import io.avaje.inject.InjectModule; +import jakarta.inject.Scope; + +@Scope +@InjectModule(automaticallyImport = {ModAScope.class}, strictWiring = true) +public @interface ModDScope { + +} diff --git a/blackbox-multi-scope/src/main/java/org/multi/scope/ModEScope.java b/blackbox-multi-scope/src/main/java/org/multi/scope/ModEScope.java new file mode 100644 index 00000000..e46e1e83 --- /dev/null +++ b/blackbox-multi-scope/src/main/java/org/multi/scope/ModEScope.java @@ -0,0 +1,10 @@ +package org.multi.scope; + +import io.avaje.inject.InjectModule; +import jakarta.inject.Scope; + +@Scope +@InjectModule(strictWiring = true) +public @interface ModEScope { + +} diff --git a/blackbox-multi-scope/src/test/java/org/multi/crosscut/BeanCrossTest.java b/blackbox-multi-scope/src/test/java/org/multi/crosscut/BeanCrossTest.java index dad539ba..303e7cdb 100644 --- a/blackbox-multi-scope/src/test/java/org/multi/crosscut/BeanCrossTest.java +++ b/blackbox-multi-scope/src/test/java/org/multi/crosscut/BeanCrossTest.java @@ -1,24 +1,100 @@ package org.multi.crosscut; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertTrue; -import io.avaje.inject.BeanScope; -import org.junit.jupiter.api.Test; +import java.util.stream.Stream; + +import org.junit.jupiter.api.Named; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.multi.many.BeanInMany; +import org.multi.many.ManyModule; +import org.multi.moda.BeanInModA; +import org.multi.moda.ModAModule; +import org.multi.modb.BOther; import org.multi.modb.BeanInModB; +import org.multi.modb.ModBModule; +import org.multi.modc.BeanInModC; +import org.multi.modc.COther; +import org.multi.modc.ModCModule; +import org.multi.modd.BeanInModD; +import org.multi.modd.ModDModule; +import org.multi.mode.BeanInModE; +import org.multi.mode.ModEModule; -import static org.assertj.core.api.Assertions.assertThat; +import io.avaje.inject.BeanScope; +import io.avaje.inject.spi.AvajeModule; class BeanCrossTest { - @Test - void bootstrap() { + private static Stream allModulesDefined() { + return Stream.of( + Arguments.of(Named.of("In working order A", new AvajeModule[]{ + new ModAModule(), + new ModCModule(), + new ModBModule(), + new CrossCutModule(), + new ModDModule(), + new ModEModule(), + new ManyModule(), + })), + Arguments.of(Named.of("In alphabetical order", new AvajeModule[]{ + new CrossCutModule(), + new ManyModule(), + new ModAModule(), + new ModBModule(), + new ModCModule(), + new ModDModule(), + new ModEModule(), + })), + Arguments.of(Named.of("In reverse alphabetical order", new AvajeModule[]{ + new ModEModule(), + new ModDModule(), + new ModCModule(), + new ModBModule(), + new ModAModule(), + new ManyModule(), + new CrossCutModule(), + })), + Arguments.of(Named.of("In shuffled order", new AvajeModule[]{ + new ModEModule(), + new ModBModule(), + new ModDModule(), + new ManyModule(), + new ModCModule(), + new ModAModule(), + new CrossCutModule(), + })), + Arguments.of(Named.of("Only the end module is required", new AvajeModule[]{ + new ManyModule() + })) + ); + } - try (BeanScope beanScope = BeanScope.builder() -// .modules(new CrossCutModule()) - .build()) { + private static final Class[] CHECKABLE = { + BeanCross.class, + BeanCross2.class, + BeanCross3.class, + BeanInMany.class, + BeanInModA.class, + BeanInModB.class, + BOther.class, + BeanInModC.class, + COther.class, + BeanInModD.class, + BeanInModE.class + }; -// var beanInModB = beanScope.get(BeanInModB.class); -// assertThat(beanInModB).isNotNull(); - } + @ParameterizedTest(name = "Multi Scope Test: {1}") + @MethodSource("allModulesDefined") + void bootstrap(AvajeModule... modules) { + try (BeanScope beanScope = assertDoesNotThrow(() -> BeanScope.builder().modules(modules).build())) { + for (final Class clazz : CHECKABLE) { + assertTrue(beanScope.getOptional(clazz).isPresent(), "Bean not found: " + clazz.getSimpleName()); + } + } } } diff --git a/inject/src/main/java/io/avaje/inject/InjectModule.java b/inject/src/main/java/io/avaje/inject/InjectModule.java index a1e31af3..f773ad7d 100644 --- a/inject/src/main/java/io/avaje/inject/InjectModule.java +++ b/inject/src/main/java/io/avaje/inject/InjectModule.java @@ -105,6 +105,22 @@ */ Class[] requiresPackages() default {}; + /** + * Modules and scopes that are expected to be automatically imported when this module is requested manually + *

+ * Like {@code requires}, references to beans created in the imported module/scope are not treated as missing. + * Unlike {@code requires} though, referenced items will be provided automatically. + *

+ * Any item that a referenced module/scope {@code requires} must either: + *

+ *

+ * Circular dependencies are not permitted + */ + Class[] automaticallyImport() default {}; + /** * Internal use only - identifies the custom scope annotation associated to this module. *