From 43c0c7145e421e272911243c63ec6261b456b1f2 Mon Sep 17 00:00:00 2001 From: Josiah Noel <32279667+SentryMan@users.noreply.github.com> Date: Sat, 5 Jul 2025 10:32:45 -0400 Subject: [PATCH 1/6] Determine Priority annotations at compile time - read the annotations at compile time - simplify bean matching logic - can use priority on factory beans now --- .../io/avaje/inject/generator/BeanReader.java | 4 + .../avaje/inject/generator/MethodReader.java | 9 ++- .../java/io/avaje/inject/generator/Util.java | 18 ++++- .../avaje/inject/xtra/SystemContextTest.java | 4 +- .../org/example/coffee/CoffeeMakerTest.java | 2 +- .../coffee/priority/base/PriorityFactory.java | 23 ++++++ .../coffee/priority/base/PriorityTest.java | 16 +++- .../main/java/io/avaje/inject/BeanEntry.java | 6 +- .../main/java/io/avaje/inject/BeanScope.java | 24 +++--- .../main/java/io/avaje/inject/Priority.java | 24 +++--- .../java/io/avaje/inject/spi/Builder.java | 6 ++ .../java/io/avaje/inject/spi/DBeanMap.java | 22 +++++- .../java/io/avaje/inject/spi/DBeanScope.java | 74 +------------------ .../io/avaje/inject/spi/DBeanScopeProxy.java | 11 +-- .../java/io/avaje/inject/spi/DBuilder.java | 6 ++ .../io/avaje/inject/spi/DContextEntry.java | 54 +++++++------- .../avaje/inject/spi/DContextEntryBean.java | 34 ++++----- .../io/avaje/inject/BeanScopeBuilderTest.java | 7 +- 18 files changed, 174 insertions(+), 170 deletions(-) create mode 100644 inject-test/src/test/java/org/example/coffee/priority/base/PriorityFactory.java diff --git a/inject-generator/src/main/java/io/avaje/inject/generator/BeanReader.java b/inject-generator/src/main/java/io/avaje/inject/generator/BeanReader.java index 47bf9f54a..5e0ca8c1e 100644 --- a/inject-generator/src/main/java/io/avaje/inject/generator/BeanReader.java +++ b/inject-generator/src/main/java/io/avaje/inject/generator/BeanReader.java @@ -54,6 +54,7 @@ final class BeanReader { private boolean suppressGeneratedImport; private Set allUTypes; private final boolean delayed; + private final Integer priority; BeanReader(TypeElement beanType, boolean factory, boolean importedComponent) { this.beanType = beanType; @@ -69,6 +70,7 @@ final class BeanReader { || importedComponent && ProcessingContext.isImportedPrototype(actualType); this.primary = PrimaryPrism.isPresent(actualType); this.secondary = !primary && SecondaryPrism.isPresent(actualType); + this.priority = Util.getPriority(actualType); var beanTypes = BeanTypesPrism.getOptionalOn(actualType) .map(BeanTypesPrism::value) @@ -368,6 +370,8 @@ void buildRegister(Append writer) { writer.append("asPrimary()."); } else if (secondary) { writer.append("asSecondary()."); + } else if (priority != null) { + writer.append("asPriority(%s).", priority); } writer.append("register(bean);").eol(); } diff --git a/inject-generator/src/main/java/io/avaje/inject/generator/MethodReader.java b/inject-generator/src/main/java/io/avaje/inject/generator/MethodReader.java index 8a17d2820..d91b4b9bd 100644 --- a/inject-generator/src/main/java/io/avaje/inject/generator/MethodReader.java +++ b/inject-generator/src/main/java/io/avaje/inject/generator/MethodReader.java @@ -37,6 +37,7 @@ final class MethodReader { private final boolean prototype; private final boolean primary; private final boolean secondary; + private final Integer priority; private final boolean lazy; private final boolean proxyLazy; private final TypeElement lazyProxyType; @@ -68,6 +69,7 @@ final class MethodReader { prototype = PrototypePrism.isPresent(element); primary = PrimaryPrism.isPresent(element); secondary = SecondaryPrism.isPresent(element); + priority = Util.getPriority(element); lazy = LazyPrism.isPresent(element) || LazyPrism.isPresent(element.getEnclosingElement()); conditions.readAll(element); this.lazyProxyType = lazy ? Util.lazyProxy(element) : null; @@ -76,6 +78,7 @@ final class MethodReader { prototype = false; primary = false; secondary = false; + priority = null; lazy = false; this.proxyLazy = false; this.lazyProxyType = null; @@ -273,6 +276,8 @@ void builderAddBeanProvider(Append writer) { writer.append(".asPrototype()"); } else if (secondary) { writer.append(".asSecondary()"); + } else if (priority != null) { + writer.append(".asPriority(%s)", priority); } if (proxyLazy) { @@ -331,6 +336,8 @@ void builderBuildAddBean(Append writer) { writer.append(".asPrimary()"); } else if (secondary) { writer.append(".asSecondary()"); + } else if (priority != null) { + writer.append(".asPriority(%s)", priority); } else if (prototype) { writer.append(".asPrototype()"); } @@ -526,7 +533,7 @@ boolean isLazy() { } boolean isUseProviderForSecondary() { - return secondary && !optionalType && !Util.isProvider(returnTypeRaw); + return (secondary || priority != null) && !optionalType && !Util.isProvider(returnTypeRaw); } boolean isPublic() { diff --git a/inject-generator/src/main/java/io/avaje/inject/generator/Util.java b/inject-generator/src/main/java/io/avaje/inject/generator/Util.java index 7e7feb505..05f4fde12 100644 --- a/inject-generator/src/main/java/io/avaje/inject/generator/Util.java +++ b/inject-generator/src/main/java/io/avaje/inject/generator/Util.java @@ -430,6 +430,20 @@ static boolean hasNoArgConstructor(TypeElement beanType) { } public static String shortNameLazyProxy(TypeElement lazyProxyType) { - return shortName(lazyProxyType.getQualifiedName().toString()) - .replace(".", "_"); } + return shortName(lazyProxyType.getQualifiedName().toString()).replace(".", "_"); + } + + static Integer getPriority(Element element) { + + for (final var mirror : element.getAnnotationMirrors()) { + if (mirror.getAnnotationType().asElement().getSimpleName().toString().contains("Priority") + && mirror.getElementValues().size() == 1) { + var value = mirror.getElementValues().values().iterator().next().getValue(); + if (value instanceof Integer) { + return (Integer) value; + } + } + } + return null; + } } diff --git a/inject-test/src/test/java/io/avaje/inject/xtra/SystemContextTest.java b/inject-test/src/test/java/io/avaje/inject/xtra/SystemContextTest.java index 2f5019def..3e9a7c38f 100644 --- a/inject-test/src/test/java/io/avaje/inject/xtra/SystemContextTest.java +++ b/inject-test/src/test/java/io/avaje/inject/xtra/SystemContextTest.java @@ -10,6 +10,7 @@ import org.example.coffee.priority.base.BBasei; import org.example.coffee.priority.base.BaseIface; import org.example.coffee.priority.base.CBasei; +import org.example.coffee.priority.base.PriorityFactory.DBasei; import org.junit.jupiter.api.Test; import javax.annotation.Priority; @@ -23,11 +24,12 @@ public class SystemContextTest { public void getBeansByPriority() { try (BeanScope context = BeanScope.builder().build()) { final List beans = context.listByPriority(BaseIface.class); - assertThat(beans).hasSize(3); + assertThat(beans).hasSize(4); assertThat(beans.get(0)).isInstanceOf(CBasei.class); assertThat(beans.get(1)).isInstanceOf(BBasei.class); assertThat(beans.get(2)).isInstanceOf(ABasei.class); + assertThat(beans.get(3)).isInstanceOf(DBasei.class); } } diff --git a/inject-test/src/test/java/org/example/coffee/CoffeeMakerTest.java b/inject-test/src/test/java/org/example/coffee/CoffeeMakerTest.java index f85e0f642..454db106b 100644 --- a/inject-test/src/test/java/org/example/coffee/CoffeeMakerTest.java +++ b/inject-test/src/test/java/org/example/coffee/CoffeeMakerTest.java @@ -68,7 +68,7 @@ void beanScope_all() { assertThat(entry.qualifierName()).isEqualTo("B"); assertThat(entry.keys()).containsExactlyInAnyOrder(name(BSomei.class), name(Somei.class)); assertThat(entry.type()).isEqualTo(BSomei.class); - assertThat(entry.priority()).isEqualTo(0); + assertThat(entry.priority()).isEqualTo(-1); assertThat(entry.bean()).isEqualTo(context.get(Somei.class, "B")); assertThat(entry.bean()).isEqualTo(context.get(BSomei.class)); } diff --git a/inject-test/src/test/java/org/example/coffee/priority/base/PriorityFactory.java b/inject-test/src/test/java/org/example/coffee/priority/base/PriorityFactory.java new file mode 100644 index 000000000..a8564c933 --- /dev/null +++ b/inject-test/src/test/java/org/example/coffee/priority/base/PriorityFactory.java @@ -0,0 +1,23 @@ +package org.example.coffee.priority.base; + +import io.avaje.inject.Bean; +import io.avaje.inject.Factory; +import io.avaje.inject.Priority; + +@Factory +public class PriorityFactory { + + @Bean + @Priority(69) + BaseIface iface() { + return new DBasei(); + } + + public static class DBasei implements BaseIface { + + @Override + public String other() { + return "b"; + } + } +} diff --git a/inject-test/src/test/java/org/example/coffee/priority/base/PriorityTest.java b/inject-test/src/test/java/org/example/coffee/priority/base/PriorityTest.java index d0b13c865..48e9eca27 100644 --- a/inject-test/src/test/java/org/example/coffee/priority/base/PriorityTest.java +++ b/inject-test/src/test/java/org/example/coffee/priority/base/PriorityTest.java @@ -1,13 +1,15 @@ package org.example.coffee.priority.base; -import io.avaje.inject.xtra.ApplicationScope; -import org.junit.jupiter.api.Test; +import static org.assertj.core.api.Assertions.assertThat; import java.util.List; -import static org.assertj.core.api.Assertions.assertThat; +import org.example.coffee.priority.base.PriorityFactory.DBasei; +import org.junit.jupiter.api.Test; -public class PriorityTest { +import io.avaje.inject.xtra.ApplicationScope; + +class PriorityTest { @Test void listByPriority() { @@ -15,9 +17,15 @@ void listByPriority() { assertExpectedOrder(sorted); } + @Test + void testGet() { + assertThat(ApplicationScope.get(BaseIface.class)).isInstanceOf(CBasei.class); + } + private void assertExpectedOrder(List sorted) { assertThat(sorted.get(0)).isInstanceOf(CBasei.class); assertThat(sorted.get(1)).isInstanceOf(BBasei.class); assertThat(sorted.get(2)).isInstanceOf(ABasei.class); + assertThat(sorted.get(3)).isInstanceOf(DBasei.class); } } diff --git a/inject/src/main/java/io/avaje/inject/BeanEntry.java b/inject/src/main/java/io/avaje/inject/BeanEntry.java index 889757e4c..0cb28dcc2 100644 --- a/inject/src/main/java/io/avaje/inject/BeanEntry.java +++ b/inject/src/main/java/io/avaje/inject/BeanEntry.java @@ -12,12 +12,12 @@ public interface BeanEntry { /** * Priority of externally supplied bean. */ - int SUPPLIED = 2; + int SUPPLIED = Integer.MAX_VALUE; /** * Priority of @Primary bean. */ - int PRIMARY = 1; + int PRIMARY = Integer.MAX_VALUE - 1; /** * Priority of normal bean. @@ -27,7 +27,7 @@ public interface BeanEntry { /** * Priority of @Secondary bean. */ - int SECONDARY = -1; + int SECONDARY = Integer.MIN_VALUE; /** * Return the bean name. diff --git a/inject/src/main/java/io/avaje/inject/BeanScope.java b/inject/src/main/java/io/avaje/inject/BeanScope.java index 0a480b4a0..eff8f680b 100644 --- a/inject/src/main/java/io/avaje/inject/BeanScope.java +++ b/inject/src/main/java/io/avaje/inject/BeanScope.java @@ -198,21 +198,21 @@ default T get(Type type) { */ List list(Type type); - /** - * Return the list of beans that implement the interface sorting by priority. - */ - List listByPriority(Class type); + /** Return the list of beans that implement the class sorting by priority. */ + default List listByPriority(Class type) { + return listByPriority((Type) type); + } + + /** Return the list of beans that implement the type sorting by priority. */ + List listByPriority(Type type); /** - * Return the beans that implement the interface sorting by the priority annotation used. - *

- * The priority annotation will typically be either javax.annotation.Priority - * or jakarta.annotation.Priority. - * - * @param type The interface type of the beans to return - * @param priority The priority annotation used to sort the beans + * @deprecated use {@link #listByPriority(Class)} */ - List listByPriority(Class type, Class priority); + @Deprecated(forRemoval = true) + default List listByPriority(Class type, Class priority) { + return listByPriority(type); + } /** * Return the beans for this type mapped by their qualifier name. diff --git a/inject/src/main/java/io/avaje/inject/Priority.java b/inject/src/main/java/io/avaje/inject/Priority.java index 937d9b142..f7115e710 100644 --- a/inject/src/main/java/io/avaje/inject/Priority.java +++ b/inject/src/main/java/io/avaje/inject/Priority.java @@ -1,26 +1,26 @@ package io.avaje.inject; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.Target; -import static java.lang.annotation.ElementType.TYPE; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - /** - * The Priority annotation can be applied to classes to indicate - * in what order they should be returned via @{@link BeanScope#listByPriority(Class)}. - *

- * Beans can be returned using other Priority annotation such as javax.annotation.Priority - * or any custom priority annotation that has an int value() attribute. - *

+ * The Priority annotation can be applied to classes to indicate the wiring priority of + * a bean to resolve cases where multiple beans of the same type exist. + * + *

Beans can be returned using other Priority annotation such as + * jakartak.annotation.Priority + * or any custom priority annotation that has an int value() attribute. * - * @see BeanScope#listByPriority(Class) - * @see BeanScope#listByPriority(Class, Class) + * @see BeanScope#listByPriority(Type) */ @Documented @Retention(RUNTIME) -@Target(TYPE) +@Target({TYPE, METHOD}) public @interface Priority { int value(); } diff --git a/inject/src/main/java/io/avaje/inject/spi/Builder.java b/inject/src/main/java/io/avaje/inject/spi/Builder.java index 56caf92fa..e40129af6 100644 --- a/inject/src/main/java/io/avaje/inject/spi/Builder.java +++ b/inject/src/main/java/io/avaje/inject/spi/Builder.java @@ -69,6 +69,12 @@ default boolean isBeanAbsent(Type... types) { */ Builder asSecondary(); + /** + * Register the next bean as having Secondary priority. Lowest priority, only used if no other + * matching beans are available. + */ + Builder asPriority(int priority); + /** * Register the next bean as having Prototype scope. */ diff --git a/inject/src/main/java/io/avaje/inject/spi/DBeanMap.java b/inject/src/main/java/io/avaje/inject/spi/DBeanMap.java index 15710b965..a43b195f1 100644 --- a/inject/src/main/java/io/avaje/inject/spi/DBeanMap.java +++ b/inject/src/main/java/io/avaje/inject/spi/DBeanMap.java @@ -1,11 +1,10 @@ package io.avaje.inject.spi; -import io.avaje.inject.BeanEntry; -import io.avaje.inject.BeanScope; -import jakarta.inject.Provider; +import static java.util.stream.Collectors.toList; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; +import java.util.Comparator; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; @@ -13,6 +12,10 @@ import java.util.Optional; import java.util.Set; +import io.avaje.inject.BeanEntry; +import io.avaje.inject.BeanScope; +import jakarta.inject.Provider; + /** * Map of types (class types, interfaces and annotations) to a DContextEntry where the * entry holds a list of bean instances for that type. @@ -121,6 +124,19 @@ T get(Type type, String name) { return (T) entry.get(name, currentModule); } + public List listByPriority(Type type) { + + DContextEntry entry = beans.get(type.getTypeName()); + if (entry == null) { + return List.of(); + } + + return entry.entries().stream() + .sorted(Comparator.comparingInt(DContextEntryBean::priority).reversed()) + .map(e -> (T) e.bean()) + .collect(toList()); + } + @SuppressWarnings("unchecked") Provider provider(Type type, String name) { DContextEntry entry = beans.get(type.getTypeName()); diff --git a/inject/src/main/java/io/avaje/inject/spi/DBeanScope.java b/inject/src/main/java/io/avaje/inject/spi/DBeanScope.java index 430a081ac..56f60dd39 100644 --- a/inject/src/main/java/io/avaje/inject/spi/DBeanScope.java +++ b/inject/src/main/java/io/avaje/inject/spi/DBeanScope.java @@ -7,7 +7,6 @@ import java.lang.annotation.Annotation; import java.lang.reflect.Type; import java.util.ArrayList; -import java.util.Collections; import java.util.IdentityHashMap; import java.util.List; import java.util.Map; @@ -23,7 +22,6 @@ import io.avaje.applog.AppLog; import io.avaje.inject.BeanEntry; import io.avaje.inject.BeanScope; -import io.avaje.inject.Priority; @NullMarked final class DBeanScope implements BeanScope { @@ -191,37 +189,8 @@ static List combine(List values, List parentValues) { } @Override - public List listByPriority(Class type) { - return listByPriority(type, Priority.class); - } - - @Override - public List listByPriority(Class type, Class priorityAnnotation) { - List list = list(type); - return list.size() > 1 ? sortByPriority(list, priorityAnnotation) : list; - } - - private List sortByPriority(List list, final Class priorityAnnotation) { - boolean priorityUsed = false; - List> tempList = new ArrayList<>(list.size()); - for (T bean : list) { - SortBean sortBean = new SortBean<>(bean, priorityAnnotation); - tempList.add(sortBean); - if (!priorityUsed && sortBean.priorityDefined) { - priorityUsed = true; - } - } - if (!priorityUsed) { - // nothing with Priority annotation so return original order - return list; - } - Collections.sort(tempList); - // unpack into new sorted list - List sorted = new ArrayList<>(tempList.size()); - for (SortBean sortBean : tempList) { - sorted.add(sortBean.bean); - } - return sorted; + public List listByPriority(Type type) { + return beans.listByPriority(type); } @Override @@ -249,7 +218,7 @@ DBeanScope start(long start) { } finally { lock.unlock(); } - log.log(INFO, "Wired beans in {0}ms", (System.currentTimeMillis() - start)); + log.log(INFO, "Wired beans in {0}ms", System.currentTimeMillis() - start); return this; } @@ -299,41 +268,4 @@ public void run() { scope.shutdown(); } } - - private static class SortBean implements Comparable> { - - private final T bean; - - private boolean priorityDefined; - - private final int priority; - - SortBean(T bean, Class priorityAnnotation) { - this.bean = bean; - this.priority = initPriority(priorityAnnotation); - } - - int initPriority(Class priorityAnnotation) { - // Avoid adding hard dependency on javax.annotation-api by using reflection - try { - final Annotation ann = bean.getClass().getDeclaredAnnotation(priorityAnnotation); - if (ann != null) { - final int newPriority = (Integer) priorityAnnotation.getMethod("value").invoke(ann); - priorityDefined = true; - return newPriority; - } - } catch (Exception e) { - // If this happens, something has gone very wrong since a non-confirming @Priority was found... - throw new UnsupportedOperationException("Problem instantiating @Priority", e); - } - // Default priority as per javax.ws.rs.Priorities.USER - // User-level filter/interceptor priority - return 5000; - } - - @Override - public int compareTo(SortBean o) { - return Integer.compare(priority, o.priority); - } - } } diff --git a/inject/src/main/java/io/avaje/inject/spi/DBeanScopeProxy.java b/inject/src/main/java/io/avaje/inject/spi/DBeanScopeProxy.java index 384d4a56d..9112e527e 100644 --- a/inject/src/main/java/io/avaje/inject/spi/DBeanScopeProxy.java +++ b/inject/src/main/java/io/avaje/inject/spi/DBeanScopeProxy.java @@ -106,7 +106,7 @@ public List list(Type type) { } @Override - public List listByPriority(Class type) { + public List listByPriority(Type type) { if (delegate != null) { return delegate.listByPriority(type); } else { @@ -114,15 +114,6 @@ public List listByPriority(Class type) { } } - @Override - public List listByPriority(Class type, Class priority) { - if (delegate != null) { - return delegate.listByPriority(type, priority); - } else { - throw illegal("listByPriority"); - } - } - @Override public Map map(Type type) { diff --git a/inject/src/main/java/io/avaje/inject/spi/DBuilder.java b/inject/src/main/java/io/avaje/inject/spi/DBuilder.java index def27eb2a..fde5ac3b1 100644 --- a/inject/src/main/java/io/avaje/inject/spi/DBuilder.java +++ b/inject/src/main/java/io/avaje/inject/spi/DBuilder.java @@ -170,6 +170,12 @@ public Builder asSecondary() { return this; } + @Override + public Builder asPriority(int priority) { + beanMap.nextPriority(priority * -1); + return this; + } + @Override public Builder asPrototype() { beanMap.nextPrototype(); diff --git a/inject/src/main/java/io/avaje/inject/spi/DContextEntry.java b/inject/src/main/java/io/avaje/inject/spi/DContextEntry.java index f16d0f930..fc6945edf 100644 --- a/inject/src/main/java/io/avaje/inject/spi/DContextEntry.java +++ b/inject/src/main/java/io/avaje/inject/spi/DContextEntry.java @@ -1,12 +1,13 @@ package io.avaje.inject.spi; -import jakarta.inject.Provider; - import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import io.avaje.inject.BeanEntry; +import jakarta.inject.Provider; + /** * Entry for a given key (bean class, interface class or annotation class). *

@@ -149,37 +150,29 @@ private void checkMatch(DContextEntryBean entry) { match = entry; return; } - if (match.isSupplied()) { + if (match.priority() > entry.priority()) { // existing supplied match always wins return; - } else if (entry.isSupplied()) { + } else if (match.priority() < entry.priority()) { // new supplied wins match = entry; return; } - if (match.isSecondary() && !entry.isSecondary()) { - // secondary loses - match = entry; - return; - } if (match.isPrimary()) { if (entry.isPrimary()) { - throw new IllegalStateException("Expecting only 1 bean match but have multiple primary beans " + match.bean() + " and " + entry.bean()); + throw new IllegalStateException( + "Expecting only 1 bean match but have multiple primary beans " + + match.bean() + + " and " + + entry.bean()); } // leave as is, current primary wins return; - } - if (entry.isSecondary()) { - if (match.isSecondary()) { - ignoredSecondaryMatch = entry; - } - return; - } - if (entry.isPrimary()) { - // new primary wins - match = entry; + } else if (impliedName) { + ignoredSecondaryMatch = entry; return; } + // try to resolve match using qualifier name (including null) if (match.isNameEqual(name) && !entry.isNameEqual(name)) { ignoredSecondaryMatch = entry; @@ -197,8 +190,12 @@ private void checkMatch(DContextEntryBean entry) { match = entry; return; } - throw new IllegalStateException("Expecting only 1 bean match but have multiple matching beans " + match.bean() - + " and " + entry.bean() + ". Maybe need a rebuild is required after adding a @Named qualifier?"); + throw new IllegalStateException( + "Expecting only 1 bean match but have multiple matching beans " + + match.bean() + + " and " + + entry.bean() + + ". Maybe need a rebuild is required after adding a @Named qualifier or @Priority annotation?"); } private DContextEntryBean candidate() { @@ -210,10 +207,17 @@ private DContextEntryBean candidate() { } private void checkSecondary() { - if (match.isSecondary() && ignoredSecondaryMatch != null) { - throw new IllegalStateException("Expecting only 1 bean match but have multiple secondary beans " + match.bean() + " and " + ignoredSecondaryMatch.bean()); + if (match.priority() != BeanEntry.SUPPLIED + && match.priority() != BeanEntry.NORMAL + && ignoredSecondaryMatch != null + && ignoredSecondaryMatch.priority() == match.priority()) { + throw new IllegalStateException( + "Expecting only 1 bean match but have multiple beans with the same priority" + + match.bean() + + " and " + + ignoredSecondaryMatch.bean() + + ". Maybe need a rebuild is required after adding a @Named qualifier or @Priority annotation?"); } } - } } diff --git a/inject/src/main/java/io/avaje/inject/spi/DContextEntryBean.java b/inject/src/main/java/io/avaje/inject/spi/DContextEntryBean.java index d1f2e7a79..8fdf1e99d 100644 --- a/inject/src/main/java/io/avaje/inject/spi/DContextEntryBean.java +++ b/inject/src/main/java/io/avaje/inject/spi/DContextEntryBean.java @@ -11,22 +11,22 @@ class DContextEntryBean { /** * Create taking into account if it is a Provider or the bean itself. */ - static DContextEntryBean of(Object source, String name, int flag, Class currentModule) { + static DContextEntryBean of(Object source, String name, int priority, Class currentModule) { if (source instanceof Provider) { - return new ProtoProvider((Provider)source, name, flag, currentModule); + return new ProtoProvider((Provider)source, name, priority, currentModule); } else { - return new DContextEntryBean(source, name, flag, currentModule); + return new DContextEntryBean(source, name, priority, currentModule); } } /** * Create an entry with supplied Providers using a 'Once' / 'one instance' provider. */ - static DContextEntryBean supplied(Object source, String name, int flag) { + static DContextEntryBean supplied(Object source, String name, int priority) { if (source instanceof Provider) { - return new OnceBeanProvider((Provider)source, name, flag, null); + return new OnceBeanProvider((Provider)source, name, priority, null); } else { - return new DContextEntryBean(source, name, flag, null); + return new DContextEntryBean(source, name, priority, null); } } @@ -37,12 +37,12 @@ static DContextEntryBean provider(boolean prototype, Provider provider, Strin protected final Object source; protected final String name; protected final Class sourceModule; - private final int flag; + private final int priority; - private DContextEntryBean(Object source, String name, int flag, Class currentModule) { + private DContextEntryBean(Object source, String name, int priority, Class currentModule) { this.source = source; this.name = name; - this.flag = flag; + this.priority = priority; this.sourceModule = currentModule; } @@ -51,13 +51,13 @@ public final String toString() { return "Bean{" + "source=" + source + ", name='" + name + '\'' + - ", flag=" + flag + + ", priority=" + priority + ", sourceModule=" + sourceModule + '}'; } final DEntry entry() { - return new DEntry(name, flag, bean()); + return new DEntry(name, priority, bean()); } /** @@ -98,19 +98,15 @@ Provider provider() { } final boolean isPrimary() { - return flag == BeanEntry.PRIMARY; + return priority == BeanEntry.PRIMARY; } - final boolean isSecondary() { - return flag == BeanEntry.SECONDARY; - } - - final boolean isSupplied() { - return flag == BeanEntry.SUPPLIED; + final int priority() { + return priority; } final boolean isSupplied(String qualifierName) { - return flag == BeanEntry.SUPPLIED && (qualifierName == null || qualifierName.equals(name)); + return priority == BeanEntry.SUPPLIED && (qualifierName == null || qualifierName.equals(name)); } /** diff --git a/inject/src/test/java/io/avaje/inject/BeanScopeBuilderTest.java b/inject/src/test/java/io/avaje/inject/BeanScopeBuilderTest.java index 52b6d5049..ab8d76c6b 100644 --- a/inject/src/test/java/io/avaje/inject/BeanScopeBuilderTest.java +++ b/inject/src/test/java/io/avaje/inject/BeanScopeBuilderTest.java @@ -305,12 +305,7 @@ public List list(Type type) { } @Override - public List listByPriority(Class type) { - return null; - } - - @Override - public List listByPriority(Class type, Class priority) { + public List listByPriority(Type type) { return null; } From 5ba0f5a8d9ffe3b1a9aa0547cc632d0882a5b6f5 Mon Sep 17 00:00:00 2001 From: Josiah Noel <32279667+SentryMan@users.noreply.github.com> Date: Sat, 5 Jul 2025 11:42:26 -0400 Subject: [PATCH 2/6] Update BeanEntry.java --- inject/src/main/java/io/avaje/inject/BeanEntry.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inject/src/main/java/io/avaje/inject/BeanEntry.java b/inject/src/main/java/io/avaje/inject/BeanEntry.java index 0cb28dcc2..600fe9de5 100644 --- a/inject/src/main/java/io/avaje/inject/BeanEntry.java +++ b/inject/src/main/java/io/avaje/inject/BeanEntry.java @@ -45,7 +45,7 @@ public interface BeanEntry { Class type(); /** - * Return the priority indicating if the bean is Supplied Primary, Normal or Secondary. + * Return the wiring priority of the bean. */ int priority(); From 614c5b2cc40906355fe93cf7783eb58601f39733 Mon Sep 17 00:00:00 2001 From: Josiah Noel <32279667+SentryMan@users.noreply.github.com> Date: Sat, 5 Jul 2025 14:17:40 -0400 Subject: [PATCH 3/6] flip signs --- .../src/test/java/org/example/coffee/CoffeeMakerTest.java | 2 +- inject/src/main/java/io/avaje/inject/BeanEntry.java | 6 +++--- inject/src/main/java/io/avaje/inject/spi/DBeanMap.java | 2 +- inject/src/main/java/io/avaje/inject/spi/DBuilder.java | 2 +- inject/src/main/java/io/avaje/inject/spi/DContextEntry.java | 4 ++-- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/inject-test/src/test/java/org/example/coffee/CoffeeMakerTest.java b/inject-test/src/test/java/org/example/coffee/CoffeeMakerTest.java index 454db106b..dbec44a94 100644 --- a/inject-test/src/test/java/org/example/coffee/CoffeeMakerTest.java +++ b/inject-test/src/test/java/org/example/coffee/CoffeeMakerTest.java @@ -68,7 +68,7 @@ void beanScope_all() { assertThat(entry.qualifierName()).isEqualTo("B"); assertThat(entry.keys()).containsExactlyInAnyOrder(name(BSomei.class), name(Somei.class)); assertThat(entry.type()).isEqualTo(BSomei.class); - assertThat(entry.priority()).isEqualTo(-1); + assertThat(entry.priority()).isEqualTo(1); assertThat(entry.bean()).isEqualTo(context.get(Somei.class, "B")); assertThat(entry.bean()).isEqualTo(context.get(BSomei.class)); } diff --git a/inject/src/main/java/io/avaje/inject/BeanEntry.java b/inject/src/main/java/io/avaje/inject/BeanEntry.java index 600fe9de5..9e75ada96 100644 --- a/inject/src/main/java/io/avaje/inject/BeanEntry.java +++ b/inject/src/main/java/io/avaje/inject/BeanEntry.java @@ -12,12 +12,12 @@ public interface BeanEntry { /** * Priority of externally supplied bean. */ - int SUPPLIED = Integer.MAX_VALUE; + int SUPPLIED = Integer.MIN_VALUE; /** * Priority of @Primary bean. */ - int PRIMARY = Integer.MAX_VALUE - 1; + int PRIMARY = Integer.MIN_VALUE + 1; /** * Priority of normal bean. @@ -27,7 +27,7 @@ public interface BeanEntry { /** * Priority of @Secondary bean. */ - int SECONDARY = Integer.MIN_VALUE; + int SECONDARY = Integer.MAX_VALUE; /** * Return the bean name. diff --git a/inject/src/main/java/io/avaje/inject/spi/DBeanMap.java b/inject/src/main/java/io/avaje/inject/spi/DBeanMap.java index a43b195f1..2f3d32693 100644 --- a/inject/src/main/java/io/avaje/inject/spi/DBeanMap.java +++ b/inject/src/main/java/io/avaje/inject/spi/DBeanMap.java @@ -132,7 +132,7 @@ public List listByPriority(Type type) { } return entry.entries().stream() - .sorted(Comparator.comparingInt(DContextEntryBean::priority).reversed()) + .sorted(Comparator.comparingInt(DContextEntryBean::priority)) .map(e -> (T) e.bean()) .collect(toList()); } diff --git a/inject/src/main/java/io/avaje/inject/spi/DBuilder.java b/inject/src/main/java/io/avaje/inject/spi/DBuilder.java index fde5ac3b1..382595c2b 100644 --- a/inject/src/main/java/io/avaje/inject/spi/DBuilder.java +++ b/inject/src/main/java/io/avaje/inject/spi/DBuilder.java @@ -172,7 +172,7 @@ public Builder asSecondary() { @Override public Builder asPriority(int priority) { - beanMap.nextPriority(priority * -1); + beanMap.nextPriority(priority); return this; } diff --git a/inject/src/main/java/io/avaje/inject/spi/DContextEntry.java b/inject/src/main/java/io/avaje/inject/spi/DContextEntry.java index fc6945edf..09b8267a6 100644 --- a/inject/src/main/java/io/avaje/inject/spi/DContextEntry.java +++ b/inject/src/main/java/io/avaje/inject/spi/DContextEntry.java @@ -150,10 +150,10 @@ private void checkMatch(DContextEntryBean entry) { match = entry; return; } - if (match.priority() > entry.priority()) { + if (match.priority() < entry.priority()) { // existing supplied match always wins return; - } else if (match.priority() < entry.priority()) { + } else if (match.priority() > entry.priority()) { // new supplied wins match = entry; return; From 9c0af6326cd452155bd6cd893772c5d8c1e62a8c Mon Sep 17 00:00:00 2001 From: Josiah Noel <32279667+SentryMan@users.noreply.github.com> Date: Sun, 6 Jul 2025 15:34:16 -0400 Subject: [PATCH 4/6] never beat primary priority --- .../src/main/java/io/avaje/inject/generator/Util.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/inject-generator/src/main/java/io/avaje/inject/generator/Util.java b/inject-generator/src/main/java/io/avaje/inject/generator/Util.java index 05f4fde12..9a981fdde 100644 --- a/inject-generator/src/main/java/io/avaje/inject/generator/Util.java +++ b/inject-generator/src/main/java/io/avaje/inject/generator/Util.java @@ -440,7 +440,8 @@ static Integer getPriority(Element element) { && mirror.getElementValues().size() == 1) { var value = mirror.getElementValues().values().iterator().next().getValue(); if (value instanceof Integer) { - return (Integer) value; + var val = (Integer) value; + return val <= Integer.MIN_VALUE + 1 ? Integer.MIN_VALUE + 2 : val; } } } From 4089b0339e06d5df1e5055e9259e38aeb33d2375 Mon Sep 17 00:00:00 2001 From: Josiah Noel <32279667+SentryMan@users.noreply.github.com> Date: Sun, 6 Jul 2025 20:02:48 -0700 Subject: [PATCH 5/6] Update Builder.java --- inject/src/main/java/io/avaje/inject/spi/Builder.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/inject/src/main/java/io/avaje/inject/spi/Builder.java b/inject/src/main/java/io/avaje/inject/spi/Builder.java index e40129af6..dae14918b 100644 --- a/inject/src/main/java/io/avaje/inject/spi/Builder.java +++ b/inject/src/main/java/io/avaje/inject/spi/Builder.java @@ -59,18 +59,18 @@ default boolean isBeanAbsent(Type... types) { /** * Register the next bean as having Primary priority. - * Highest priority, will be used over any other matching beans. + * Highest priority, wired over any other matching beans. */ Builder asPrimary(); /** * Register the next bean as having Secondary priority. - * Lowest priority, only used if no other matching beans are available. + * Lowest priority, wired when no other matching beans are available. */ Builder asSecondary(); /** - * Register the next bean as having Secondary priority. Lowest priority, only used if no other + * Register the next bean as having the given priority. Wired only if no other higher priority * matching beans are available. */ Builder asPriority(int priority); From f97c625f0b68e0eb42dcd5c8b5bd32a08cdea563 Mon Sep 17 00:00:00 2001 From: Rob Bygrave Date: Sun, 13 Jul 2025 14:44:21 +1200 Subject: [PATCH 6/6] Rename Util.priority() method, some javadoc --- .../java/io/avaje/inject/generator/BeanReader.java | 2 +- .../java/io/avaje/inject/generator/MethodReader.java | 10 +++++++++- .../src/main/java/io/avaje/inject/generator/Util.java | 10 ++++++---- 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/inject-generator/src/main/java/io/avaje/inject/generator/BeanReader.java b/inject-generator/src/main/java/io/avaje/inject/generator/BeanReader.java index 5e0ca8c1e..820d10f85 100644 --- a/inject-generator/src/main/java/io/avaje/inject/generator/BeanReader.java +++ b/inject-generator/src/main/java/io/avaje/inject/generator/BeanReader.java @@ -70,7 +70,7 @@ final class BeanReader { || importedComponent && ProcessingContext.isImportedPrototype(actualType); this.primary = PrimaryPrism.isPresent(actualType); this.secondary = !primary && SecondaryPrism.isPresent(actualType); - this.priority = Util.getPriority(actualType); + this.priority = Util.priority(actualType); var beanTypes = BeanTypesPrism.getOptionalOn(actualType) .map(BeanTypesPrism::value) diff --git a/inject-generator/src/main/java/io/avaje/inject/generator/MethodReader.java b/inject-generator/src/main/java/io/avaje/inject/generator/MethodReader.java index d91b4b9bd..aee034a21 100644 --- a/inject-generator/src/main/java/io/avaje/inject/generator/MethodReader.java +++ b/inject-generator/src/main/java/io/avaje/inject/generator/MethodReader.java @@ -4,6 +4,7 @@ import static io.avaje.inject.generator.Constants.CONDITIONAL_DEPENDENCY; import static io.avaje.inject.generator.ProcessingContext.asElement; +import java.lang.reflect.Type; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -69,7 +70,7 @@ final class MethodReader { prototype = PrototypePrism.isPresent(element); primary = PrimaryPrism.isPresent(element); secondary = SecondaryPrism.isPresent(element); - priority = Util.getPriority(element); + priority = Util.priority(element); lazy = LazyPrism.isPresent(element) || LazyPrism.isPresent(element.getEnclosingElement()); conditions.readAll(element); this.lazyProxyType = lazy ? Util.lazyProxy(element) : null; @@ -532,6 +533,13 @@ boolean isLazy() { return lazy; } + /** + * Use a Provider with Secondary or Priority annotation. + * + *

As a Provider, the bean won't be instantiated unless it is needed + * by being wired as the highest priority, or wired in a List/Set/Map, or + * by being accessed via {@link io.avaje.inject.BeanScope#list(Type)} etc. + */ boolean isUseProviderForSecondary() { return (secondary || priority != null) && !optionalType && !Util.isProvider(returnTypeRaw); } diff --git a/inject-generator/src/main/java/io/avaje/inject/generator/Util.java b/inject-generator/src/main/java/io/avaje/inject/generator/Util.java index 9a981fdde..bc1f46b57 100644 --- a/inject-generator/src/main/java/io/avaje/inject/generator/Util.java +++ b/inject-generator/src/main/java/io/avaje/inject/generator/Util.java @@ -433,11 +433,9 @@ public static String shortNameLazyProxy(TypeElement lazyProxyType) { return shortName(lazyProxyType.getQualifiedName().toString()).replace(".", "_"); } - static Integer getPriority(Element element) { - + static Integer priority(Element element) { for (final var mirror : element.getAnnotationMirrors()) { - if (mirror.getAnnotationType().asElement().getSimpleName().toString().contains("Priority") - && mirror.getElementValues().size() == 1) { + if (isPriorityAnnotation(mirror) && mirror.getElementValues().size() == 1) { var value = mirror.getElementValues().values().iterator().next().getValue(); if (value instanceof Integer) { var val = (Integer) value; @@ -447,4 +445,8 @@ static Integer getPriority(Element element) { } return null; } + + private static boolean isPriorityAnnotation(AnnotationMirror mirror) { + return mirror.getAnnotationType().asElement().getSimpleName().toString().contains("Priority"); + } }