diff --git a/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/impl/ArcContainerImpl.java b/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/impl/ArcContainerImpl.java index 4d1afc751b964..db7e8c52c6173 100644 --- a/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/impl/ArcContainerImpl.java +++ b/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/impl/ArcContainerImpl.java @@ -22,6 +22,7 @@ import java.util.concurrent.ForkJoinPool; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.BiConsumer; import java.util.function.Consumer; import java.util.function.Supplier; import java.util.stream.Collectors; @@ -80,6 +81,7 @@ public class ArcContainerImpl implements ArcContainer { private final AtomicBoolean running; private final List> beans; + private final Map>> beansByRawType; private final LazyValue> removedBeans; private final List> interceptors; private final List> decorators; @@ -106,6 +108,7 @@ public ArcContainerImpl(CurrentContextFactory currentContextFactory, boolean str id = String.valueOf(ID_GENERATOR.incrementAndGet()); running = new AtomicBoolean(true); List> beans = new ArrayList<>(); + Map>> beansByRawType = new HashMap<>(); List>> removedBeans = new ArrayList<>(); List> interceptors = new ArrayList<>(); List> decorators = new ArrayList<>(); @@ -130,6 +133,7 @@ public ArcContainerImpl(CurrentContextFactory currentContextFactory, boolean str decorators.add((InjectableDecorator) bean); } else { beans.add(bean); + precomputeBeanRawTypes(beansByRawType, bean); } } removedBeans.add(c.getRemovedBeans()); @@ -141,7 +145,7 @@ public ArcContainerImpl(CurrentContextFactory currentContextFactory, boolean str } // register built-in beans - addBuiltInBeans(beans); + addBuiltInBeans(beans, beansByRawType); interceptors.sort(Comparator.comparingInt(InjectableInterceptor::getPriority)); decorators.sort(Comparator.comparingInt(InjectableDecorator::getPriority)); @@ -158,6 +162,17 @@ public ArcContainerImpl(CurrentContextFactory currentContextFactory, boolean str instance = InstanceImpl.forGlobalEntrypoint(Object.class, Collections.emptySet()); this.beans = List.copyOf(beans); + this.beansByRawType = Map.copyOf(beansByRawType); + // Trim the size of the non-singleton lists + this.beansByRawType.forEach(new BiConsumer>>() { + @Override + public void accept(String key, List> val) { + if (val.size() > 1) { + ((ArrayList>) val).trimToSize(); + } + } + }); + this.interceptors = List.copyOf(interceptors); this.decorators = List.copyOf(decorators); this.observers = List.copyOf(observers); @@ -202,6 +217,38 @@ public List get() { this.contexts = contextsBuilder.build(); } + static void precomputeBeanRawTypes(Map>> map, InjectableBean bean) { + for (Type type : bean.getTypes()) { + if (Object.class.equals(type)) { + continue; + } + Class rawType = Types.getRawType(type); + if (rawType == null) { + continue; + } + rawType = Types.boxedClass(rawType); + String key = rawType.getName(); + List> match = map.get(key); + if (match == null) { + // very often a singleton list will be used + map.put(key, List.of(bean)); + } else { + // we don't expect large lists so this should be fine performance wise + if (match.contains(bean)) { + continue; + } + if (match.size() == 1) { + List> newMatch = new ArrayList<>(); + newMatch.add(match.get(0)); + newMatch.add(bean); + map.put(key, newMatch); + } else { + match.add(bean); + } + } + } + } + public void init() { // Fire an event with qualifier @Initialized(ApplicationScoped.class) Set qualifiers = Set.of(Initialized.Literal.APPLICATION, Any.Literal.INSTANCE); @@ -462,12 +509,19 @@ private Notifier notifierOrNull(Set qualifiers) { return notifier.isEmpty() ? null : notifier; } - private static void addBuiltInBeans(List> beans) { + private static void addBuiltInBeans(List> beans, Map>> beansByRawType) { // BeanManager, Event, Instance, InjectionPoint - beans.add(new BeanManagerBean()); - beans.add(new EventBean()); + BeanManagerBean beanManagerBean = new BeanManagerBean(); + beans.add(beanManagerBean); + precomputeBeanRawTypes(beansByRawType, beanManagerBean); + EventBean eventBean = new EventBean(); + beans.add(eventBean); + precomputeBeanRawTypes(beansByRawType, eventBean); beans.add(InstanceBean.INSTANCE); - beans.add(new InjectionPointBean()); + precomputeBeanRawTypes(beansByRawType, InstanceBean.INSTANCE); + InjectionPointBean injectionPointBean = new InjectionPointBean(); + beans.add(injectionPointBean); + precomputeBeanRawTypes(beansByRawType, injectionPointBean); } private InstanceHandle instanceHandle(Type type, Annotation... qualifiers) { @@ -680,7 +734,7 @@ private static Integer getAlternativePriority(InjectableBean bean) { List> getMatchingBeans(Resolvable resolvable) { List> matching = new ArrayList<>(); - for (InjectableBean bean : beans) { + for (InjectableBean bean : potentialBeans(resolvable.requiredType)) { if (matches(bean, resolvable.requiredType, resolvable.qualifiers)) { matching.add(bean); } @@ -688,6 +742,17 @@ List> getMatchingBeans(Resolvable resolvable) { return matching; } + Iterable> potentialBeans(Type type) { + if (!Object.class.equals(type)) { + Class rawType = Types.getRawType(type); + if (rawType != null) { + List> match = beansByRawType.get(Types.boxedClass(rawType).getName()); + return match == null ? List.of() : match; + } + } + return beans; + } + List getMatchingRemovedBeans(Resolvable resolvable) { List matching = new ArrayList<>(); for (RemovedBean removedBean : removedBeans.get()) {