Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ArC: speed up bean resolution at runtime #33760

Merged
merged 1 commit into from
Jun 5, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -80,6 +81,7 @@ public class ArcContainerImpl implements ArcContainer {
private final AtomicBoolean running;

private final List<InjectableBean<?>> beans;
private final Map<String, List<InjectableBean<?>>> beansByRawType;
private final LazyValue<List<RemovedBean>> removedBeans;
private final List<InjectableInterceptor<?>> interceptors;
private final List<InjectableDecorator<?>> decorators;
Expand All @@ -106,6 +108,7 @@ public ArcContainerImpl(CurrentContextFactory currentContextFactory, boolean str
id = String.valueOf(ID_GENERATOR.incrementAndGet());
running = new AtomicBoolean(true);
List<InjectableBean<?>> beans = new ArrayList<>();
Map<String, List<InjectableBean<?>>> beansByRawType = new HashMap<>();
List<Supplier<Collection<RemovedBean>>> removedBeans = new ArrayList<>();
List<InjectableInterceptor<?>> interceptors = new ArrayList<>();
List<InjectableDecorator<?>> decorators = new ArrayList<>();
Expand All @@ -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());
Expand All @@ -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));
Expand All @@ -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);
mkouba marked this conversation as resolved.
Show resolved Hide resolved
// Trim the size of the non-singleton lists
this.beansByRawType.forEach(new BiConsumer<String, List<InjectableBean<?>>>() {
@Override
public void accept(String key, List<InjectableBean<?>> val) {
if (val.size() > 1) {
((ArrayList<InjectableBean<?>>) val).trimToSize();
}
}
});

this.interceptors = List.copyOf(interceptors);
this.decorators = List.copyOf(decorators);
this.observers = List.copyOf(observers);
Expand Down Expand Up @@ -202,6 +217,38 @@ public List<RemovedBean> get() {
this.contexts = contextsBuilder.build();
}

static void precomputeBeanRawTypes(Map<String, List<InjectableBean<?>>> 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<InjectableBean<?>> 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<InjectableBean<?>> newMatch = new ArrayList<>();
mkouba marked this conversation as resolved.
Show resolved Hide resolved
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<Annotation> qualifiers = Set.of(Initialized.Literal.APPLICATION, Any.Literal.INSTANCE);
Expand Down Expand Up @@ -462,12 +509,19 @@ private Notifier<Object> notifierOrNull(Set<Annotation> qualifiers) {
return notifier.isEmpty() ? null : notifier;
}

private static void addBuiltInBeans(List<InjectableBean<?>> beans) {
private static void addBuiltInBeans(List<InjectableBean<?>> beans, Map<String, List<InjectableBean<?>>> 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 <T> InstanceHandle<T> instanceHandle(Type type, Annotation... qualifiers) {
Expand Down Expand Up @@ -680,14 +734,25 @@ private static Integer getAlternativePriority(InjectableBean<?> bean) {

List<InjectableBean<?>> getMatchingBeans(Resolvable resolvable) {
List<InjectableBean<?>> matching = new ArrayList<>();
for (InjectableBean<?> bean : beans) {
for (InjectableBean<?> bean : potentialBeans(resolvable.requiredType)) {
if (matches(bean, resolvable.requiredType, resolvable.qualifiers)) {
matching.add(bean);
}
}
return matching;
}

Iterable<InjectableBean<?>> potentialBeans(Type type) {
if (!Object.class.equals(type)) {
Class<?> rawType = Types.getRawType(type);
if (rawType != null) {
List<InjectableBean<?>> match = beansByRawType.get(Types.boxedClass(rawType).getName());
return match == null ? List.of() : match;
}
}
return beans;
}

List<RemovedBean> getMatchingRemovedBeans(Resolvable resolvable) {
List<RemovedBean> matching = new ArrayList<>();
for (RemovedBean removedBean : removedBeans.get()) {
Expand Down