Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
io.micronaut.context.DefaultBeanContext Maven / Gradle / Ivy
/*
* Copyright 2017-2022 original authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.micronaut.context;
import io.micronaut.context.annotation.ConfigurationReader;
import io.micronaut.context.annotation.Context;
import io.micronaut.context.annotation.DefaultImplementation;
import io.micronaut.context.annotation.Executable;
import io.micronaut.context.annotation.Infrastructure;
import io.micronaut.context.annotation.Parallel;
import io.micronaut.context.annotation.Primary;
import io.micronaut.context.annotation.Prototype;
import io.micronaut.context.annotation.Replaces;
import io.micronaut.context.annotation.Secondary;
import io.micronaut.context.condition.ConditionContext;
import io.micronaut.context.condition.Failure;
import io.micronaut.context.env.CachedEnvironment;
import io.micronaut.context.env.PropertyPlaceholderResolver;
import io.micronaut.context.event.ApplicationEventListener;
import io.micronaut.context.event.ApplicationEventPublisher;
import io.micronaut.context.event.BeanCreatedEvent;
import io.micronaut.context.event.BeanCreatedEventListener;
import io.micronaut.context.event.BeanDestroyedEvent;
import io.micronaut.context.event.BeanDestroyedEventListener;
import io.micronaut.context.event.BeanInitializedEventListener;
import io.micronaut.context.event.BeanPreDestroyEvent;
import io.micronaut.context.event.BeanPreDestroyEventListener;
import io.micronaut.context.event.ShutdownEvent;
import io.micronaut.context.event.StartupEvent;
import io.micronaut.context.exceptions.BeanContextException;
import io.micronaut.context.exceptions.BeanCreationException;
import io.micronaut.context.exceptions.BeanDestructionException;
import io.micronaut.context.exceptions.BeanInstantiationException;
import io.micronaut.context.exceptions.ConfigurationException;
import io.micronaut.context.exceptions.DependencyInjectionException;
import io.micronaut.context.exceptions.DisabledBeanException;
import io.micronaut.context.exceptions.NoSuchBeanException;
import io.micronaut.context.exceptions.NonUniqueBeanException;
import io.micronaut.context.processor.AnnotationProcessor;
import io.micronaut.context.processor.ExecutableMethodProcessor;
import io.micronaut.context.scope.BeanCreationContext;
import io.micronaut.context.scope.CreatedBean;
import io.micronaut.context.scope.CustomScope;
import io.micronaut.context.scope.CustomScopeRegistry;
import io.micronaut.core.annotation.AnnotationClassValue;
import io.micronaut.core.annotation.AnnotationMetadata;
import io.micronaut.core.annotation.AnnotationMetadataProvider;
import io.micronaut.core.annotation.AnnotationMetadataResolver;
import io.micronaut.core.annotation.AnnotationUtil;
import io.micronaut.core.annotation.AnnotationValue;
import io.micronaut.core.annotation.Indexes;
import io.micronaut.core.annotation.Internal;
import io.micronaut.core.annotation.NonNull;
import io.micronaut.core.annotation.Nullable;
import io.micronaut.core.annotation.UsedByGeneratedCode;
import io.micronaut.core.convert.MutableConversionService;
import io.micronaut.core.convert.TypeConverter;
import io.micronaut.core.convert.TypeConverterRegistrar;
import io.micronaut.core.convert.value.MutableConvertibleValues;
import io.micronaut.core.io.ResourceLoader;
import io.micronaut.core.io.scan.ClassPathResourceLoader;
import io.micronaut.core.io.service.SoftServiceLoader;
import io.micronaut.core.naming.NameResolver;
import io.micronaut.core.naming.NameUtils;
import io.micronaut.core.naming.Named;
import io.micronaut.core.order.OrderUtil;
import io.micronaut.core.reflect.ClassUtils;
import io.micronaut.core.type.Argument;
import io.micronaut.core.type.ReturnType;
import io.micronaut.core.type.UnsafeExecutable;
import io.micronaut.core.util.ArgumentUtils;
import io.micronaut.core.util.ArrayUtils;
import io.micronaut.core.util.CollectionUtils;
import io.micronaut.core.util.StringUtils;
import io.micronaut.core.util.clhm.ConcurrentLinkedHashMap;
import io.micronaut.core.value.PropertyResolver;
import io.micronaut.core.value.ValueResolver;
import io.micronaut.inject.AdvisedBeanType;
import io.micronaut.inject.BeanConfiguration;
import io.micronaut.inject.BeanDefinition;
import io.micronaut.inject.BeanDefinitionMethodReference;
import io.micronaut.inject.BeanDefinitionReference;
import io.micronaut.inject.BeanIdentifier;
import io.micronaut.inject.BeanType;
import io.micronaut.inject.DisposableBeanDefinition;
import io.micronaut.inject.ExecutableMethod;
import io.micronaut.inject.InitializingBeanDefinition;
import io.micronaut.inject.InjectableBeanDefinition;
import io.micronaut.inject.InjectionPoint;
import io.micronaut.inject.InstantiatableBeanDefinition;
import io.micronaut.inject.MethodExecutionHandle;
import io.micronaut.inject.ParametrizedInstantiatableBeanDefinition;
import io.micronaut.inject.ProxyBeanDefinition;
import io.micronaut.inject.QualifiedBeanType;
import io.micronaut.inject.UnsafeExecutionHandle;
import io.micronaut.inject.ValidatedBeanDefinition;
import io.micronaut.inject.provider.AbstractProviderDefinition;
import io.micronaut.inject.proxy.InterceptedBeanProxy;
import io.micronaut.inject.qualifiers.AnyQualifier;
import io.micronaut.inject.qualifiers.FilteringQualifier;
import io.micronaut.inject.qualifiers.Qualified;
import io.micronaut.inject.qualifiers.Qualifiers;
import io.micronaut.inject.validation.BeanDefinitionValidator;
import jakarta.inject.Singleton;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.EventListener;
import java.util.HashMap;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static io.micronaut.core.util.StringUtils.EMPTY_STRING_ARRAY;
/**
* The default context implementations.
*
* @author Graeme Rocher
* @since 1.0
*/
@SuppressWarnings("MagicNumber")
public class DefaultBeanContext implements InitializableBeanContext, ConfigurableBeanContext {
protected static final Logger LOG = LoggerFactory.getLogger(DefaultBeanContext.class);
protected static final Logger LOG_LIFECYCLE = LoggerFactory.getLogger(DefaultBeanContext.class.getPackage().getName() + ".lifecycle");
private static final String SCOPED_PROXY_ANN = "io.micronaut.runtime.context.scope.ScopedProxy";
private static final String INTRODUCTION_TYPE = "io.micronaut.aop.Introduction";
private static final String ADAPTER_TYPE = "io.micronaut.aop.Adapter";
private static final String PARALLEL_TYPE = Parallel.class.getName();
private static final String INDEXES_TYPE = Indexes.class.getName();
private static final String REPLACES_ANN = Replaces.class.getName();
private static final String MSG_COULD_NOT_BE_LOADED = "] could not be loaded: ";
public static final String MSG_BEAN_DEFINITION = "Bean definition [";
protected final AtomicBoolean running = new AtomicBoolean(false);
protected final AtomicBoolean configured = new AtomicBoolean(false);
protected final AtomicBoolean initializing = new AtomicBoolean(false);
protected final AtomicBoolean terminating = new AtomicBoolean(false);
final Map> singlesInCreation = new ConcurrentHashMap<>(5);
protected final SingletonScope singletonScope = new SingletonScope();
private final BeanContextConfiguration beanContextConfiguration;
// The collection should be modified only when new bean definition is added
// That shouldn't happen that often, so we can use CopyOnWriteArrayList
private final Collection beanDefinitionsClasses = new CopyOnWriteArrayList<>();
private final Collection proxyTargetBeans = new CopyOnWriteArrayList<>();
private final Map, BeanDefinitionProducer> disabledBeans = new ConcurrentHashMap<>(20);
private final Map> disabledConfigurations = new ConcurrentHashMap<>(5);
private final Map beanConfigurations = new HashMap<>(10);
private final Map containsBeanCache = new ConcurrentHashMap<>(30);
private final Map attributes = Collections.synchronizedMap(new HashMap<>(5));
private final Map singletonBeanRegistrations = new ConcurrentHashMap<>(50);
private final Map> beanConcreteCandidateCache =
new ConcurrentLinkedHashMap.Builder>().maximumWeightedCapacity(30).build();
private final Map> beanProxyTargetCache =
new ConcurrentLinkedHashMap.Builder>().maximumWeightedCapacity(30).build();
private final Map> beanCandidateCache = new ConcurrentLinkedHashMap.Builder>().maximumWeightedCapacity(30).build();
private final Map, Collection> beanIndex = new ConcurrentHashMap<>(12);
private final ClassLoader classLoader;
private final Set> thisInterfaces = CollectionUtils.setOf(
BeanDefinitionRegistry.class,
BeanContext.class,
AnnotationMetadataResolver.class,
BeanLocator.class,
ExecutionHandleLocator.class,
ApplicationContext.class,
PropertyResolver.class,
ValueResolver.class,
PropertyPlaceholderResolver.class
);
private final Set> indexedTypes = CollectionUtils.setOf(
ResourceLoader.class,
TypeConverter.class,
TypeConverterRegistrar.class,
ApplicationEventListener.class,
BeanCreatedEventListener.class,
BeanInitializedEventListener.class
);
private final CustomScopeRegistry customScopeRegistry;
private final String[] eagerInitStereotypes;
private final boolean eagerInitStereotypesPresent;
private final boolean eagerInitSingletons;
private BeanDefinitionValidator beanValidator;
private List beanDefinitionReferences;
private List beanConfigurationsList;
private StartupBeans startupBeans;
List, ListenersSupplier>> beanInitializedEventListeners;
private List, ListenersSupplier>> beanCreationEventListeners;
private List, ListenersSupplier>> beanPreDestroyEventListeners;
private List, ListenersSupplier>> beanDestroyedEventListeners;
@Nullable
private MutableConversionService conversionService;
/**
* Construct a new bean context using the same classloader that loaded this DefaultBeanContext class.
*/
public DefaultBeanContext() {
this(BeanContext.class.getClassLoader());
}
/**
* Construct a new bean context with the given class loader.
*
* @param classLoader The class loader
*/
public DefaultBeanContext(@NonNull ClassLoader classLoader) {
this(new BeanContextConfiguration() {
@NonNull
@Override
public ClassLoader getClassLoader() {
ArgumentUtils.requireNonNull("classLoader", classLoader);
return classLoader;
}
});
}
/**
* Construct a new bean context with the given class loader.
*
* @param resourceLoader The resource loader
*/
public DefaultBeanContext(@NonNull ClassPathResourceLoader resourceLoader) {
this(new BeanContextConfiguration() {
@NonNull
@Override
public ClassLoader getClassLoader() {
ArgumentUtils.requireNonNull("resourceLoader", resourceLoader);
return resourceLoader.getClassLoader();
}
});
}
/**
* Creates a new bean context with the given configuration.
*
* @param contextConfiguration The context configuration
*/
public DefaultBeanContext(@NonNull BeanContextConfiguration contextConfiguration) {
ArgumentUtils.requireNonNull("contextConfiguration", contextConfiguration);
// enable classloader logging
System.setProperty(ClassUtils.PROPERTY_MICRONAUT_CLASSLOADER_LOGGING, "true");
this.classLoader = contextConfiguration.getClassLoader();
this.customScopeRegistry = Objects.requireNonNull(createCustomScopeRegistry(), "Scope registry cannot be null");
Set> eagerInitAnnotated = contextConfiguration.getEagerInitAnnotated();
List configuredEagerSingletonAnnotations = new ArrayList<>(eagerInitAnnotated.size());
for (Class extends Annotation> ann : eagerInitAnnotated) {
configuredEagerSingletonAnnotations.add(ann.getName());
}
this.eagerInitStereotypes = configuredEagerSingletonAnnotations.toArray(EMPTY_STRING_ARRAY);
this.eagerInitStereotypesPresent = !configuredEagerSingletonAnnotations.isEmpty();
this.eagerInitSingletons = eagerInitStereotypesPresent && (configuredEagerSingletonAnnotations.contains(AnnotationUtil.SINGLETON) || configuredEagerSingletonAnnotations.contains(Singleton.class.getName()));
this.beanContextConfiguration = contextConfiguration;
}
/**
* Allows customizing the custom scope registry.
*
* @return The custom scope registry to use.
* @since 3.0.0
*/
@NonNull
protected CustomScopeRegistry createCustomScopeRegistry() {
return new DefaultCustomScopeRegistry(this);
}
/**
* @return The custom scope registry
*/
@Internal
@NonNull
CustomScopeRegistry getCustomScopeRegistry() {
return customScopeRegistry;
}
@Override
public boolean isRunning() {
return running.get() && !initializing.get();
}
/**
* The start method will read all bean definition classes found on the classpath and initialize any pre-required
* state.
*/
@Override
public synchronized BeanContext start() {
if (!isRunning()) {
if (initializing.compareAndSet(false, true)) {
if (LOG.isDebugEnabled()) {
LOG.debug("Starting BeanContext");
}
registerConversionService();
configureAndStartContext();
if (LOG.isDebugEnabled()) {
String activeConfigurations = beanConfigurations
.values()
.stream()
.filter(config -> config.isEnabled(this))
.map(BeanConfiguration::getName)
.collect(Collectors.joining(","));
if (StringUtils.isNotEmpty(activeConfigurations)) {
LOG.debug("Loaded active configurations: {}", activeConfigurations);
}
}
if (LOG.isDebugEnabled()) {
LOG.debug("BeanContext Started.");
}
publishEvent(new StartupEvent(this));
}
running.set(true);
initializing.set(false);
}
return this;
}
/**
* Registers conversion service.
*/
protected void registerConversionService() {
conversionService = MutableConversionService.create();
//noinspection resource
registerSingleton(MutableConversionService.class, conversionService, null, false);
}
/**
* Tracks when a bean or configuration is disabled.
* @param conditionContext The conditional context
* @param The component type
*/
@Internal
void trackDisabledComponent(@NonNull ConditionContext conditionContext) {
C component = conditionContext.getComponent();
List reasons = conditionContext.getFailures().stream().map(Failure::getMessage).toList();
if (component instanceof QualifiedBeanType> beanType) {
try {
@SuppressWarnings("unchecked")
Argument argument = (Argument) beanType.getGenericBeanType();
@SuppressWarnings("unchecked")
Qualifier declaredQualifier = (Qualifier) beanType.getDeclaredQualifier();
this.disabledBeans.put(new BeanKey<>(argument, declaredQualifier), new BeanDefinitionProducer(new DisabledBean<>(
argument,
declaredQualifier,
reasons
)));
} catch (Exception | NoClassDefFoundError e) {
// it is theoretically possible that resolving the generic type results in an error
// in this case just ignore this as the maps built here are purely to aid error diagnosis
}
} else if (component instanceof BeanConfiguration configuration) {
this.disabledConfigurations.put(configuration.getName(), reasons);
}
}
/**
* The close method will shut down the context calling {@link jakarta.annotation.PreDestroy} hooks on loaded
* singletons.
*/
@Override
public synchronized BeanContext stop() {
if (terminating.compareAndSet(false, true) && isRunning()) {
if (LOG.isDebugEnabled()) {
LOG.debug("Stopping BeanContext");
}
publishEvent(new ShutdownEvent(this));
attributes.clear();
// need to sort registered singletons so that beans with that require other beans appear first
List objects = topologicalSort(singletonScope.getBeanRegistrations());
Map> result = objects.stream().collect(Collectors.groupingBy(br -> br.bean != null
&& (br.bean instanceof BeanPreDestroyEventListener || br.bean instanceof BeanDestroyedEventListener)));
List listeners = result.get(true);
if (listeners != null) {
// destroy all bean destroy listeners at the end
objects.clear();
objects.addAll(result.get(false));
objects.addAll(listeners);
}
Set processed = new HashSet<>();
for (BeanRegistration beanRegistration : objects) {
Object bean = beanRegistration.bean;
int sysId = System.identityHashCode(bean);
if (processed.contains(sysId)) {
continue;
}
if (LOG_LIFECYCLE.isDebugEnabled()) {
LOG_LIFECYCLE.debug("Destroying bean [{}] with identifier [{}]", bean, beanRegistration.identifier);
}
processed.add(sysId);
try {
destroyBean(beanRegistration);
} catch (BeanDestructionException e) {
if (LOG.isErrorEnabled()) {
LOG.error(e.getMessage(), e);
}
}
}
singlesInCreation.clear();
singletonBeanRegistrations.clear();
beanConcreteCandidateCache.clear();
beanCandidateCache.clear();
beanProxyTargetCache.clear();
containsBeanCache.clear();
beanConfigurations.clear();
disabledConfigurations.clear();
singletonScope.clear();
beanDefinitionsClasses.clear();
disabledBeans.clear();
proxyTargetBeans.clear();
attributes.clear();
beanIndex.clear();
beanConfigurationsList = null;
beanDefinitionReferences = null;
beanInitializedEventListeners = null;
beanCreationEventListeners = null;
beanPreDestroyEventListeners = null;
beanDestroyedEventListeners = null;
conversionService = null;
startupBeans = null;
terminating.set(false);
running.set(false);
configured.set(false);
}
return this;
}
@Override
@NonNull
public AnnotationMetadata resolveMetadata(Class> type) {
if (type == null) {
return AnnotationMetadata.EMPTY_METADATA;
}
return findBeanDefinitionInternal(Argument.of(type), null)
.map(AnnotationMetadataProvider::getAnnotationMetadata)
.orElse(AnnotationMetadata.EMPTY_METADATA);
}
@Override
public Optional refreshBean(@Nullable BeanIdentifier identifier) {
if (identifier == null) {
return Optional.empty();
}
BeanRegistration beanRegistration = singletonScope.findBeanRegistration(identifier);
if (beanRegistration != null) {
refreshBean(beanRegistration);
return Optional.of(beanRegistration.bean);
}
return Optional.empty();
}
@Override
public void refreshBean(@NonNull BeanRegistration beanRegistration) {
Objects.requireNonNull(beanRegistration, "BeanRegistration cannot be null");
T bean = beanRegistration.bean;
if (bean != null) {
BeanDefinition definition = beanRegistration.definition();
if (definition instanceof InjectableBeanDefinition injectableBeanDefinition) {
injectableBeanDefinition.inject(this, bean);
}
}
}
@Override
public Collection> getActiveBeanRegistrations(Qualifier> qualifier) {
if (qualifier == null) {
return Collections.emptyList();
}
return singletonScope.getBeanRegistrations(qualifier);
}
@Override
public Collection> getActiveBeanRegistrations(Class beanType) {
if (beanType == null) {
return Collections.emptyList();
}
return singletonScope.getBeanRegistrations(beanType);
}
@Override
public Collection> getBeanRegistrations(Class beanType) {
if (beanType == null) {
return Collections.emptyList();
}
return getBeanRegistrations(null, Argument.of(beanType), null);
}
@Override
public BeanRegistration getBeanRegistration(Class beanType, Qualifier qualifier) {
return getBeanRegistration(null, Argument.of(beanType), qualifier);
}
@Override
public Collection> getBeanRegistrations(Class beanType, Qualifier qualifier) {
if (beanType == null) {
return Collections.emptyList();
}
return getBeanRegistrations(null, Argument.of(beanType), null);
}
@Override
public Collection> getBeanRegistrations(Argument beanType, Qualifier qualifier) {
return getBeanRegistrations(
null,
Objects.requireNonNull(beanType, "Bean type cannot be null"),
qualifier
);
}
@Override
public BeanRegistration getBeanRegistration(Argument beanType, Qualifier qualifier) {
return getBeanRegistration(
null,
Objects.requireNonNull(beanType, "Bean type cannot be null"),
qualifier
);
}
@Override
public BeanRegistration getBeanRegistration(BeanDefinition beanDefinition) {
return resolveBeanRegistration(null, beanDefinition);
}
@Override
public Optional> findBeanRegistration(T bean) {
if (bean == null) {
return Optional.empty();
}
BeanRegistration beanRegistration = singletonScope.findBeanRegistration(bean);
if (beanRegistration != null) {
return Optional.of(beanRegistration);
}
return customScopeRegistry.findBeanRegistration(bean);
}
@Override
public Optional> findExecutionHandle(Class beanType, String method, Class>... arguments) {
return findExecutionHandle(beanType, null, method, arguments);
}
@Override
public MethodExecutionHandle, Object> createExecutionHandle(BeanDefinition> beanDefinition, ExecutableMethod method) {
if (method instanceof UnsafeExecutable,?>) {
return new BeanContextUnsafeExecutionHandle(method, beanDefinition, (UnsafeExecutable) method);
}
return new BeanContextExecutionHandle(method, beanDefinition);
}
@SuppressWarnings("unchecked")
@Override
public Optional> findExecutionHandle(Class beanType, Qualifier> q, String method, Class>... arguments) {
Qualifier qualifier = (Qualifier) q;
Optional> foundBean = findBeanDefinition(beanType, qualifier);
if (foundBean.isEmpty()) {
return Optional.empty();
}
BeanDefinition beanDefinition = foundBean.get();
Optional> foundMethod = beanDefinition.findMethod(method, arguments);
if (foundMethod.isEmpty()) {
foundMethod = beanDefinition.findPossibleMethods(method)
.findFirst()
.filter(m -> {
Class>[] argTypes = m.getArgumentTypes();
if (argTypes.length == arguments.length) {
for (int i = 0; i < argTypes.length; i++) {
if (!arguments[i].isAssignableFrom(argTypes[i])) {
return false;
}
}
return true;
}
return false;
});
}
return foundMethod.map(executableMethod -> new BeanExecutionHandle<>(this, beanType, qualifier, executableMethod));
}
@Override
public Optional> findExecutableMethod(Class beanType, String method, Class>[] arguments) {
if (beanType == null) {
return Optional.empty();
}
Collection> definitions = getBeanDefinitions(beanType);
if (definitions.isEmpty()) {
return Optional.empty();
}
BeanDefinition beanDefinition = definitions.iterator().next();
Optional> foundMethod = beanDefinition.findMethod(method, arguments);
if (foundMethod.isPresent()) {
return foundMethod;
}
return beanDefinition.findPossibleMethods(method).findFirst();
}
@SuppressWarnings("unchecked")
@Override
public Optional> findExecutionHandle(T bean, String method, Class>[] arguments) {
if (bean != null) {
Optional extends BeanDefinition>> foundBean = findBeanDefinition(bean.getClass());
if (foundBean.isPresent()) {
BeanDefinition> beanDefinition = foundBean.get();
Optional extends ExecutableMethod, Object>> foundMethod = beanDefinition.findMethod(method, arguments);
if (foundMethod.isPresent()) {
return foundMethod.map((ExecutableMethod executableMethod) -> new ObjectExecutionHandle<>(bean, executableMethod));
} else {
return beanDefinition.findPossibleMethods(method)
.findFirst()
.map((ExecutableMethod executableMethod) -> new ObjectExecutionHandle<>(bean, executableMethod));
}
}
}
return Optional.empty();
}
@Override
public BeanContext registerSingleton(@NonNull Class type, @NonNull T singleton, Qualifier qualifier, boolean inject) {
purgeCacheForBeanInstance(singleton);
BeanDefinition beanDefinition;
if (inject && running.get()) {
// Bean cannot be injected before the start of the context
beanDefinition = findConcreteCandidate(null, Argument.of(type), qualifier, false).orElse(null);
if (beanDefinition == null) {
// Purge cache miss
purgeCacheForBeanInstance(singleton);
}
} else {
beanDefinition = null;
}
if (beanDefinition != null && !(beanDefinition instanceof RuntimeBeanDefinition) && beanDefinition.getBeanType().isInstance(singleton)) {
try (BeanResolutionContext context = newResolutionContext(beanDefinition, null)) {
if (inject) {
doInjectAndInitialize(context, singleton, beanDefinition);
}
DefaultBeanContext.BeanKey key = new DefaultBeanContext.BeanKey<>(beanDefinition.asArgument(), qualifier);
singletonScope.registerSingletonBean(BeanRegistration.of(this, key, beanDefinition, singleton), qualifier);
}
} else {
RuntimeBeanDefinition runtimeBeanDefinition = RuntimeBeanDefinition.builder(type, () -> singleton)
.singleton(true)
.qualifier(qualifier)
.build();
var registration = BeanRegistration.of(
this,
new BeanKey<>(runtimeBeanDefinition, qualifier),
runtimeBeanDefinition,
singleton
);
singletonScope.registerSingletonBean(registration, qualifier);
registerBeanDefinition(runtimeBeanDefinition);
}
return this;
}
private void purgeCacheForBeanInstance(T singleton) {
beanCandidateCache.entrySet().removeIf(entry -> entry.getKey().isInstance(singleton));
beanConcreteCandidateCache.entrySet().removeIf(entry -> entry.getKey().beanType.isInstance(singleton));
singletonBeanRegistrations.entrySet().removeIf(entry -> entry.getKey().beanType.isInstance(singleton));
containsBeanCache.entrySet().removeIf(entry -> entry.getKey().beanType.isInstance(singleton));
}
@NonNull
final BeanResolutionContext newResolutionContext(BeanDefinition> beanDefinition, @Nullable BeanResolutionContext currentContext) {
if (currentContext == null) {
return new SingletonBeanResolutionContext(beanDefinition);
} else {
return currentContext;
}
}
@Override
public ClassLoader getClassLoader() {
return classLoader;
}
@Override
public BeanDefinitionValidator getBeanValidator() {
if (beanValidator == null) {
this.beanValidator = findBean(BeanDefinitionValidator.class).orElse(BeanDefinitionValidator.DEFAULT);
}
return beanValidator;
}
@Override
public Optional findBeanConfiguration(String configurationName) {
BeanConfiguration configuration = this.beanConfigurations.get(configurationName);
if (configuration != null) {
return Optional.of(configuration);
} else {
return Optional.empty();
}
}
@Override
public BeanDefinition getBeanDefinition(Argument beanType, Qualifier qualifier) {
return findBeanDefinition(beanType, qualifier)
.orElseThrow(() -> newNoSuchBeanException(null, beanType, qualifier, null));
}
@Override
public Optional> findBeanDefinition(Argument beanType, Qualifier qualifier) {
BeanDefinition beanDefinition = singletonScope.findCachedSingletonBeanDefinition(beanType, qualifier);
if (beanDefinition != null) {
return Optional.of(beanDefinition);
}
return findConcreteCandidate(null, beanType, qualifier, true);
}
private Optional> findBeanDefinitionInternal(Argument beanType, Qualifier qualifier) {
return findConcreteCandidate(null, beanType, qualifier, false);
}
@Override
public Optional> findBeanDefinition(Class beanType, Qualifier qualifier) {
return findBeanDefinition(Argument.of(beanType), qualifier);
}
@Override
public Collection> getBeanDefinitions(Class beanType) {
return getBeanDefinitions(Argument.of(beanType));
}
@Override
public Collection> getBeanDefinitions(Argument beanType) {
Objects.requireNonNull(beanType, "Bean type cannot be null");
Collection> candidates = findBeanCandidatesInternal(null, beanType);
return Collections.unmodifiableCollection(candidates);
}
@Override
public Collection> getBeanDefinitions(Class beanType, Qualifier qualifier) {
Objects.requireNonNull(beanType, "Bean type cannot be null");
return getBeanDefinitions(Argument.of(beanType), qualifier);
}
@Override
public Collection> getBeanDefinitions(Argument beanType, Qualifier qualifier) {
Objects.requireNonNull(beanType, "Bean type cannot be null");
Collection> candidates = findBeanCandidatesInternal(null, beanType);
if (qualifier != null) {
candidates = qualifier.filter(beanType.getType(), candidates);
}
return Collections.unmodifiableCollection(candidates);
}
@Override
public boolean containsBean(@NonNull Class beanType, Qualifier qualifier) {
return containsBean(Argument.of(beanType), qualifier);
}
@Override
public boolean containsBean(Argument beanType, Qualifier qualifier) {
ArgumentUtils.requireNonNull("beanType", beanType);
BeanKey beanKey = new BeanKey<>(beanType, qualifier);
if (containsBeanCache.containsKey(beanKey)) {
return containsBeanCache.get(beanKey);
} else {
boolean result = singletonScope.containsBean(beanType, qualifier) ||
isCandidatePresent(beanKey.beanType, qualifier);
containsBeanCache.put(beanKey, result);
return result;
}
}
@NonNull
@Override
public T getBean(@NonNull Class beanType, @Nullable Qualifier qualifier) {
Objects.requireNonNull(beanType, "Bean type cannot be null");
return getBean(Argument.of(beanType), qualifier);
}
@NonNull
@Override
public T getBean(@NonNull Class beanType) {
Objects.requireNonNull(beanType, "Bean type cannot be null");
return getBean(Argument.of(beanType), null);
}
@NonNull
@Override
public T getBean(@NonNull Argument beanType, @Nullable Qualifier qualifier) {
Objects.requireNonNull(beanType, "Bean type cannot be null");
try {
return getBean(null, beanType, qualifier);
} catch (DisabledBeanException e) {
if (AbstractBeanContextConditional.ConditionLog.LOG.isDebugEnabled()) {
AbstractBeanContextConditional.ConditionLog.LOG.debug("Bean of type [{}] disabled for reason: {}", beanType.getSimpleName(), e.getMessage(), e);
}
throw newNoSuchBeanException(
null,
beanType,
qualifier,
"Bean of type [" + beanType.getTypeString(true) + "] disabled for reason: " + e.getMessage()
);
}
}
@Override
public Optional findBean(Class beanType, Qualifier qualifier) {
return findBean(null, beanType, qualifier);
}
@Override
public Optional findBean(Argument beanType, Qualifier qualifier) {
return findBean(null, beanType, qualifier);
}
@Override
public Collection getBeansOfType(Class beanType) {
return getBeansOfType(null, Argument.of(beanType));
}
@Override
public Collection getBeansOfType(Class beanType, Qualifier qualifier) {
return getBeansOfType(Argument.of(beanType), qualifier);
}
@Override
public Collection getBeansOfType(Argument beanType) {
return getBeansOfType(null, beanType);
}
@Override
public Collection getBeansOfType(Argument beanType, Qualifier qualifier) {
return getBeansOfType(null, beanType, qualifier);
}
@Override
public Stream streamOfType(Class beanType, Qualifier qualifier) {
return streamOfType(null, beanType, qualifier);
}
@Override
public Stream streamOfType(Argument beanType, Qualifier qualifier) {
return streamOfType(null, beanType, qualifier);
}
@Override
public Map mapOfType(Argument beanType, Qualifier qualifier) {
return mapOfType(null, beanType, qualifier);
}
/**
* Obtains a stream of beans of the given type and qualifier.
*
* @param resolutionContext The bean resolution context
* @param beanType The bean type
* @param qualifier The qualifier
* @param The bean concrete type
* @return A stream
*/
protected Stream streamOfType(BeanResolutionContext resolutionContext, Class beanType, Qualifier qualifier) {
return streamOfType(resolutionContext, Argument.of(beanType), qualifier);
}
/**
* Obtains a map of beans of the given type and qualifier.
* @param resolutionContext The resolution context
* @param beanType The bean type
* @param qualifier The qualifier
* @param The bean type
* @return A map of beans, never {@code null}.
* @since 4.0.0
*/
protected @NonNull Map mapOfType(@Nullable BeanResolutionContext resolutionContext, @NonNull Argument beanType, @Nullable Qualifier qualifier) {
// try and find a bean that implements the map with the generics
Argument> mapType = Argument.mapOf(Argument.STRING, beanType);
@SuppressWarnings("unchecked") Qualifier> mapQualifier = (Qualifier>) qualifier;
BeanDefinition> existingBean = findBeanDefinitionInternal(mapType, mapQualifier).orElse(null);
if (existingBean != null) {
return getBean(existingBean);
} else {
Collection> beanRegistrations = getBeanRegistrations(resolutionContext, beanType, qualifier);
if (beanRegistrations.isEmpty()) {
return Collections.emptyMap();
} else {
try {
return beanRegistrations.stream().collect(Collectors.toUnmodifiableMap(
DefaultBeanContext::resolveKey,
reg -> reg.bean
));
} catch (IllegalStateException e) { // occurs for duplicate keys
List> beanDefinitions = beanRegistrations.stream().map(reg -> reg.beanDefinition).toList();
throw new DependencyInjectionException(
resolutionContext,
"Injecting a map of beans requires each bean to define a qualifier. Multiple beans were found missing a qualifier resulting in duplicate keys: " + e.getMessage(),
new NonUniqueBeanException(
beanType.getType(),
beanDefinitions.iterator()
)
);
}
}
}
}
@NonNull
private static String resolveKey(BeanRegistration> reg) {
BeanDefinition> definition = reg.beanDefinition;
if (definition instanceof NameResolver resolver && resolver.resolveName().isPresent()) {
return resolver.resolveName().get();
}
Qualifier> declaredQualifier = definition.getDeclaredQualifier();
if (declaredQualifier != null) {
String name = Qualifiers.findName(declaredQualifier);
if (name != null) {
return name;
}
}
// Must be the primary or a single bean
Class> candidateType = reg.beanDefinition.getBeanType();
String candidateSimpleName = candidateType.getSimpleName();
return NameUtils.decapitalize(candidateSimpleName);
}
/**
* Obtains a stream of beans of the given type and qualifier.
*
* @param resolutionContext The bean resolution context
* @param beanType The bean type
* @param qualifier The qualifier
* @param The bean concrete type
* @return A stream
*/
@Internal
public Stream streamOfType(BeanResolutionContext resolutionContext, Argument beanType, Qualifier qualifier) {
Objects.requireNonNull(beanType, "Bean type cannot be null");
return getBeanRegistrations(resolutionContext, beanType, qualifier).stream()
.map(BeanRegistration::getBean);
}
@NonNull
@Override
public T inject(@NonNull T instance) {
Objects.requireNonNull(instance, "Instance cannot be null");
Collection> candidates = findBeanCandidatesForInstance(instance);
BeanDefinition beanDefinition;
if (candidates.size() == 1) {
beanDefinition = candidates.iterator().next();
} else if (!candidates.isEmpty()) {
beanDefinition = lastChanceResolve(Argument.of((Class) instance.getClass()), null, true, candidates);
} else {
beanDefinition = null;
}
if (beanDefinition != null && !(beanDefinition instanceof RuntimeBeanDefinition)) {
try (BeanResolutionContext resolutionContext = newResolutionContext(beanDefinition, null)) {
final BeanKey beanKey = new BeanKey<>(beanDefinition.getBeanType(), null);
resolutionContext.addInFlightBean(
beanKey,
new BeanRegistration<>(beanKey, beanDefinition, instance)
);
doInjectAndInitialize(
resolutionContext,
instance,
beanDefinition
);
}
}
return instance;
}
@NonNull
@Override
public T createBean(@NonNull Class beanType, @Nullable Qualifier qualifier) {
return createBean(null, beanType, qualifier);
}
@NonNull
@Override
public T createBean(@NonNull Class beanType, @Nullable Qualifier qualifier, @Nullable Map argumentValues) {
ArgumentUtils.requireNonNull("beanType", beanType);
Optional> candidate = findBeanDefinition(Argument.of(beanType), qualifier);
if (candidate.isPresent()) {
try (BeanResolutionContext resolutionContext = newResolutionContext(candidate.get(), null)) {
return doCreateBean(resolutionContext, candidate.get(), qualifier, argumentValues);
}
}
throw newNoSuchBeanException(
null,
Argument.of(beanType),
qualifier,
null
);
}
@NonNull
@Override
public T createBean(@NonNull Class beanType, @Nullable Qualifier qualifier, @Nullable Object... args) {
ArgumentUtils.requireNonNull("beanType", beanType);
final Argument beanArg = Argument.of(beanType);
Optional> candidate = findBeanDefinition(beanArg, qualifier);
if (candidate.isPresent()) {
BeanDefinition definition = candidate.get();
try (BeanResolutionContext resolutionContext = newResolutionContext(definition, null)) {
return doCreateBean(resolutionContext, definition, qualifier, args);
}
}
throw newNoSuchBeanException(
null,
Argument.of(beanType),
qualifier,
null
);
}
@NonNull
private T doCreateBean(@NonNull BeanResolutionContext resolutionContext,
@NonNull BeanDefinition definition,
@Nullable Qualifier qualifier,
@Nullable Object... args) {
Map argumentValues = resolveArgumentValues(resolutionContext, definition, args);
if (LOG.isTraceEnabled()) {
LOG.trace("Computed bean argument values: {}", argumentValues);
}
return doCreateBean(resolutionContext, definition, qualifier, argumentValues);
}
@NonNull
private Map resolveArgumentValues(BeanResolutionContext resolutionContext, BeanDefinition definition, Object[] args) {
Argument[] requiredArguments;
if (definition instanceof ParametrizedInstantiatableBeanDefinition parametrizedInstantiatableBeanDefinition) {
requiredArguments = parametrizedInstantiatableBeanDefinition.getRequiredArguments();
} else {
return null;
}
if (LOG.isTraceEnabled()) {
LOG.trace("Creating bean for parameters: {}", ArrayUtils.toString(args));
}
MutableConversionService conversionService = getConversionService();
Map argumentValues = CollectionUtils.newLinkedHashMap(requiredArguments.length);
BeanResolutionContext.Path currentPath = resolutionContext.getPath();
for (int i = 0; i < requiredArguments.length; i++) {
Argument> requiredArgument = requiredArguments[i];
try (BeanResolutionContext.Path ignored = currentPath.pushConstructorResolve(definition, requiredArgument)) {
Class> argumentType = requiredArgument.getType();
if (args.length > i) {
Object val = args[i];
if (val != null) {
if (argumentType.isInstance(val) && !CollectionUtils.isIterableOrMap(argumentType)) {
argumentValues.put(requiredArgument.getName(), val);
} else {
argumentValues.put(requiredArgument.getName(), conversionService.convert(val, requiredArgument).orElseThrow(() ->
new BeanInstantiationException(resolutionContext, "Invalid bean @Argument [" + requiredArgument + "]. Cannot convert object [" + val + "] to required type: " + argumentType)
));
}
} else if (!requiredArgument.isDeclaredNullable()) {
throw new BeanInstantiationException(resolutionContext, "Invalid bean @Argument [" + requiredArgument + "]. Argument cannot be null");
}
} else {
// attempt resolve from context
Optional> existingBean = findBean(resolutionContext, argumentType, null);
if (existingBean.isPresent()) {
argumentValues.put(requiredArgument.getName(), existingBean.get());
} else if (!requiredArgument.isDeclaredNullable()) {
throw new BeanInstantiationException(resolutionContext, "Invalid bean @Argument [" + requiredArgument + "]. No bean found for type: " + argumentType);
}
}
}
}
return argumentValues;
}
@Nullable
@Override
public T destroyBean(@NonNull Argument beanType, Qualifier qualifier) {
ArgumentUtils.requireNonNull("beanType", beanType);
return findBeanDefinition(beanType, qualifier)
.map(this::destroyBean)
.orElse(null);
}
@Override
@NonNull
public T destroyBean(@NonNull T bean) {
ArgumentUtils.requireNonNull("bean", bean);
Optional> beanRegistration = findBeanRegistration(bean);
if (beanRegistration.isPresent()) {
destroyBean(beanRegistration.get());
} else {
Optional> beanDefinition = findBeanDefinition((Class) bean.getClass());
if (beanDefinition.isPresent()) {
BeanDefinition