All Downloads are FREE. Search and download functionalities are using the official Maven repository.

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 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 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> foundBean = findBeanDefinition(bean.getClass());
            if (foundBean.isPresent()) {
                BeanDefinition beanDefinition = foundBean.get();
                Optional> 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 definition = beanDefinition.get();
                BeanKey key = new BeanKey<>(definition, definition.getDeclaredQualifier());
                destroyBean(BeanRegistration.of(this, key, definition, bean));
            }
        }
        return bean;
    }

    @Override
    @Nullable
    public  T destroyBean(@NonNull Class beanType) {
        ArgumentUtils.requireNonNull("beanType", beanType);
        return destroyBean(Argument.of(beanType), null);
    }

    @Nullable
    private  T destroyBean(@NonNull BeanDefinition beanDefinition) {
        if (beanDefinition.isSingleton()) {
            BeanRegistration beanRegistration = singletonScope.findBeanRegistration(beanDefinition);
            if (beanRegistration != null) {
                destroyBean(beanRegistration);
                return beanRegistration.bean;
            }
        }
        throw new IllegalArgumentException("Cannot destroy non-singleton bean using bean definition! Use 'destroyBean(BeanRegistration)` or `destroyBean()`.");
    }

    @Override
    public  void destroyBean(@NonNull BeanRegistration registration) {
        destroyBean(registration, false);
    }

    private  void destroyBean(@NonNull BeanRegistration registration, boolean dependent) {
        if (LOG_LIFECYCLE.isDebugEnabled()) {
            LOG_LIFECYCLE.debug("Destroying bean [{}] with identifier [{}]", registration.bean, registration.identifier);
        }
        if (registration.beanDefinition instanceof ProxyBeanDefinition) {
            if (registration.bean instanceof InterceptedBeanProxy) {
                // Ignore the proxy and destroy the target
                destroyProxyTargetBean(registration, dependent);
                return;
            }
            if (dependent && registration.beanDefinition.isSingleton()) {
                return;
            }
        }
        T beanToDestroy = registration.getBean();
        BeanDefinition definition = registration.getBeanDefinition();
        if (beanToDestroy != null) {
            purgeCacheForBeanInstance(beanToDestroy);
            if (definition.isSingleton()) {
                singletonScope.purgeCacheForBeanInstance(definition, beanToDestroy);
            }
        }
        beanToDestroy = triggerPreDestroyListeners(definition, beanToDestroy);

        if (definition instanceof DisposableBeanDefinition) {
            try {
                ((DisposableBeanDefinition) definition).dispose(this, beanToDestroy);
            } catch (Exception e) {
                if (LOG.isWarnEnabled()) {
                    LOG.warn("Error disposing bean [{}]... Continuing...", beanToDestroy, e);
                }
            }
        }
        if (beanToDestroy instanceof LifeCycle cycle && !dependent) {
            destroyLifeCycleBean(cycle, definition);
        }
        if (registration instanceof BeanDisposingRegistration) {
            List> dependents = ((BeanDisposingRegistration) registration).getDependents();
            if (CollectionUtils.isNotEmpty(dependents)) {
                final ListIterator> i = dependents.listIterator(dependents.size());
                while (i.hasPrevious()) {
                    destroyBean(i.previous(), true);
                }
            }
        } else {
            try {
                registration.close();
            } catch (Exception e) {
                throw new BeanDestructionException(definition, e);
            }
        }

        triggerBeanDestroyedListeners(definition, beanToDestroy);
    }

    /**
     * Destroy a lifecycle bean.
     * @param cycle The cycle
     * @param definition The definition
     * @param  The bean type
     */
    @Internal
    protected  void destroyLifeCycleBean(LifeCycle cycle, BeanDefinition definition) {
        try {
            cycle.stop();
        } catch (Exception e) {
            throw new BeanDestructionException(definition, e);
        }
    }

    @NonNull
    private  T triggerPreDestroyListeners(@NonNull BeanDefinition beanDefinition, @NonNull T bean) {
        if (beanPreDestroyEventListeners == null) {
            beanPreDestroyEventListeners = loadListeners(BeanPreDestroyEventListener.class);
        }
        if (!beanPreDestroyEventListeners.isEmpty()) {
            Class beanType = getBeanType(beanDefinition);
            for (Map.Entry, ListenersSupplier> entry : beanPreDestroyEventListeners) {
                if (entry.getKey().isAssignableFrom(beanType)) {
                    final BeanPreDestroyEvent event = new BeanPreDestroyEvent<>(this, beanDefinition, bean);
                    for (BeanPreDestroyEventListener listener : entry.getValue().get(null)) {
                        try {
                            bean = Objects.requireNonNull(
                                    listener.onPreDestroy(event),
                                    "PreDestroy event listener illegally returned null: " + listener.getClass()
                            );
                        } catch (Exception e) {
                            throw new BeanDestructionException(beanDefinition, e);
                        }
                    }

                }
            }
        }
        return bean;
    }

    private  void destroyProxyTargetBean(@NonNull BeanRegistration registration, boolean dependent) {
        Set destroyed = Collections.emptySet();
        if (registration instanceof BeanDisposingRegistration disposingRegistration) {
            if (disposingRegistration.getDependents() != null) {
                destroyed = Collections.newSetFromMap(new IdentityHashMap<>());
                for (BeanRegistration beanRegistration : disposingRegistration.getDependents()) {
                    destroyBean(beanRegistration, true);
                    destroyed.add(beanRegistration.bean);
                }
            }
        }
        BeanDefinition proxyTargetBeanDefinition = findProxyTargetBeanDefinition(registration.beanDefinition)
                .orElseThrow(() -> new IllegalStateException("Cannot find a proxy target bean definition for: " + registration.beanDefinition));
        Optional> declaredScope = customScopeRegistry.findDeclaredScope(proxyTargetBeanDefinition);
        if (declaredScope.isEmpty()) {
            if (proxyTargetBeanDefinition.isSingleton()) {
                return;
            }
            // Scope is not present, try to get the actual target bean and destroy it
            if (registration.bean instanceof InterceptedBeanProxy) {
                InterceptedBeanProxy interceptedProxy = (InterceptedBeanProxy) registration.bean;
                if (interceptedProxy.hasCachedInterceptedTarget()) {
                    T interceptedTarget = interceptedProxy.interceptedTarget();
                    if (destroyed.contains(interceptedTarget)) {
                        return;
                    }
                    destroyBean(BeanRegistration.of(this,
                            new BeanKey<>(proxyTargetBeanDefinition, proxyTargetBeanDefinition.getDeclaredQualifier()),
                            proxyTargetBeanDefinition,
                            interceptedTarget,
                            registration instanceof BeanDisposingRegistration ? ((BeanDisposingRegistration) registration).getDependents() : null
                    ));
                }
            }
            return;
        }
        CustomScope customScope = declaredScope.get();
        if (dependent) {
            return;
        }
        Optional> targetBeanRegistration = customScope.findBeanRegistration(proxyTargetBeanDefinition);
        if (targetBeanRegistration.isPresent()) {
            BeanRegistration targetRegistration = targetBeanRegistration.get();
            customScope.remove(targetRegistration.identifier);
        }
    }

    @NonNull
    private  void triggerBeanDestroyedListeners(@NonNull BeanDefinition beanDefinition, @NonNull T bean) {
        if (beanDestroyedEventListeners == null) {
            beanDestroyedEventListeners = loadListeners(BeanDestroyedEventListener.class);
        }
        if (!beanDestroyedEventListeners.isEmpty()) {
            Class beanType = getBeanType(beanDefinition);
            for (Map.Entry, ListenersSupplier> entry : beanDestroyedEventListeners) {
                if (entry.getKey().isAssignableFrom(beanType)) {
                    final BeanDestroyedEvent event = new BeanDestroyedEvent<>(this, beanDefinition, bean);
                    for (BeanDestroyedEventListener listener : entry.getValue().get(null)) {
                        try {
                            listener.onDestroyed(event);
                        } catch (Exception e) {
                            throw new BeanDestructionException(beanDefinition, e);
                        }
                    }
                }
            }
        }
    }

    @NonNull
    private  Class getBeanType(@NonNull BeanDefinition beanDefinition) {
        if (beanDefinition instanceof ProxyBeanDefinition) {
            return ((ProxyBeanDefinition) beanDefinition).getTargetType();
        }
        return beanDefinition.getBeanType();
    }

    /**
     * Find an active singleton bean for the given definition and qualifier.
     *
     * @param beanDefinition The bean definition
     * @param qualifier      The qualifier
     * @param             The bean generic type
     * @return The bean registration
     */
    @Nullable
    protected  BeanRegistration getActiveBeanRegistration(BeanDefinition beanDefinition, Qualifier qualifier) {
        if (beanDefinition == null) {
            return null;
        }
        return singletonScope.findBeanRegistration(beanDefinition, qualifier);
    }

    /**
     * Creates a bean.
     *
     * @param resolutionContext The bean resolution context
     * @param beanType          The bean type
     * @param qualifier         The qualifier
     * @param                The bean generic type
     * @return The instance
     */
    @NonNull
    protected  T createBean(@Nullable BeanResolutionContext resolutionContext,
                               @NonNull Class beanType,
                               @Nullable Qualifier qualifier) {
        ArgumentUtils.requireNonNull("beanType", beanType);

        Optional> concreteCandidate = findBeanDefinition(beanType, qualifier);
        if (concreteCandidate.isPresent()) {
            BeanDefinition candidate = concreteCandidate.get();
            try (BeanResolutionContext context = newResolutionContext(candidate, resolutionContext)) {
                return doCreateBean(context, candidate, qualifier);
            }
        }
        throw newNoSuchBeanException(
            resolutionContext,
            Argument.of(beanType),
            qualifier,
            null
        );
    }

    /**
     * Injects a bean.
     *
     * @param resolutionContext        The bean resolution context
     * @param requestingBeanDefinition The requesting bean definition
     * @param instance                 The instance
     * @param                       The instance type
     * @return The instance
     */
    @Internal
    @NonNull
    protected  T inject(@NonNull BeanResolutionContext resolutionContext,
                           @Nullable BeanDefinition requestingBeanDefinition,
                           @NonNull T instance) {
        @SuppressWarnings("unchecked") Class beanType = (Class) instance.getClass();
        Optional> concreteCandidate = findBeanDefinition(beanType, null);
        if (concreteCandidate.isPresent()) {
            BeanDefinition definition = concreteCandidate.get();
            if (requestingBeanDefinition != null && requestingBeanDefinition.equals(definition)) {
                // bail out, don't inject for bean definition in creation
                return instance;
            }
            doInjectAndInitialize(resolutionContext, instance, definition);
        }
        return instance;
    }

    /**
     * Get all beans of the given type.
     *
     * @param resolutionContext The bean resolution context
     * @param beanType          The bean type
     * @param                The bean type parameter
     * @return The found beans
     */
    @NonNull
    protected  Collection getBeansOfType(@Nullable BeanResolutionContext resolutionContext, @NonNull Argument beanType) {
        return getBeansOfType(resolutionContext, beanType, null);
    }

    /**
     * Get all 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 type parameter
     * @return The found beans
     */
    @Internal
    @NonNull
    public  Collection getBeansOfType(@Nullable BeanResolutionContext resolutionContext,
                                            @NonNull Argument beanType,
                                            @Nullable Qualifier qualifier) {
        Collection> beanRegistrations = getBeanRegistrations(resolutionContext, beanType, qualifier);
        List list = new ArrayList<>(beanRegistrations.size());
        for (BeanRegistration beanRegistration : beanRegistrations) {
            list.add(beanRegistration.getBean());
        }
        return list;
    }

    @Override
    @NonNull
    public  T getProxyTargetBean(@NonNull Class beanType, @Nullable Qualifier qualifier) {
        ArgumentUtils.requireNonNull("beanType", beanType);
        return getProxyTargetBean(null, Argument.of(beanType), qualifier);
    }

    @NonNull
    @Override
    public  T getProxyTargetBean(@NonNull Argument beanType, @Nullable Qualifier qualifier) {
        ArgumentUtils.requireNonNull("beanType", beanType);
        return getProxyTargetBean(null, beanType, qualifier);
    }

    /**
     * Resolves the proxy target for a given bean type. If the bean has no proxy then the original bean is returned.
     *
     * @param resolutionContext The bean resolution context
     * @param beanType          The bean type
     * @param qualifier         The bean qualifier
     * @param                The generic type
     * @return The proxied instance
     * @since 3.1.0
     */
    @NonNull
    @UsedByGeneratedCode
    public  T getProxyTargetBean(@Nullable BeanResolutionContext resolutionContext,
                                    @NonNull Argument beanType,
                                    @Nullable Qualifier qualifier) {
        BeanDefinition definition = getProxyTargetBeanDefinition(beanType, qualifier);
        return resolveBeanRegistration(resolutionContext, definition, beanType, qualifier).bean;
    }

    /**
     * Resolves the proxy target for a given proxy bean definition. If the bean has no proxy then the original bean is returned.
     *
     * @param resolutionContext The bean resolution context
     * @param definition        The proxy bean definition
     * @param beanType          The bean type
     * @param qualifier         The bean qualifier
     * @param                The generic type
     * @return The proxied instance
     * @since 4.3.0
     */
    @Internal
    @NonNull
    @UsedByGeneratedCode
    public  T getProxyTargetBean(@Nullable BeanResolutionContext resolutionContext,
                                    @NonNull BeanDefinition definition,
                                    @NonNull Argument beanType,
                                    @Nullable Qualifier qualifier) {
        return resolveBeanRegistration(resolutionContext, definition, beanType, qualifier).bean;
    }

    @NonNull
    @Override
    public  Optional> findProxyTargetMethod(@NonNull Class beanType, @NonNull String method, @NonNull Class[] arguments) {
        ArgumentUtils.requireNonNull("beanType", beanType);
        ArgumentUtils.requireNonNull("method", method);
        BeanDefinition definition = getProxyTargetBeanDefinition(beanType, null);
        return definition.findMethod(method, arguments);
    }

    @NonNull
    @Override
    public  Optional> findProxyTargetMethod(@NonNull Class beanType, Qualifier qualifier, @NonNull String method, Class... arguments) {
        ArgumentUtils.requireNonNull("beanType", beanType);
        ArgumentUtils.requireNonNull("method", method);
        BeanDefinition definition = getProxyTargetBeanDefinition(beanType, qualifier);
        return definition.findMethod(method, arguments);
    }

    @Override
    public  Optional> findProxyTargetMethod(@NonNull Argument beanType, Qualifier qualifier, @NonNull String method, Class... arguments) {
        ArgumentUtils.requireNonNull("beanType", beanType);
        ArgumentUtils.requireNonNull("method", method);
        BeanDefinition definition = getProxyTargetBeanDefinition(beanType, qualifier);
        return definition.findMethod(method, arguments);
    }

    @NonNull
    @Override
    public  Optional> findProxyTargetBeanDefinition(@NonNull Class beanType, @Nullable Qualifier qualifier) {
        return findProxyTargetBeanDefinition(Argument.of(beanType), qualifier);
    }

    @Override
    @SuppressWarnings("java:S2789") // performance optimization
    public  Optional> findProxyTargetBeanDefinition(@NonNull Argument beanType, @Nullable Qualifier qualifier) {
        ArgumentUtils.requireNonNull("beanType", beanType);
        BeanCandidateKey key = new BeanCandidateKey<>(beanType, qualifier, true);

        Optional beanDefinition = beanProxyTargetCache.get(key);
        if (beanDefinition == null) {
            beanDefinition = findProxyTargetNoCache(null, beanType, qualifier);
            beanProxyTargetCache.put(key, beanDefinition);
        }
        return beanDefinition;
    }

    @SuppressWarnings("unchecked")
    @NonNull
    @Override
    public Collection> getBeanDefinitions(@Nullable Qualifier qualifier) {
        if (qualifier == null) {
            return Collections.emptyList();
        }
        if (LOG.isDebugEnabled()) {
            LOG.debug("Finding candidate beans for qualifier: {}", qualifier);
        }
        // first traverse component definition classes and load candidates
        if (beanDefinitionsClasses.isEmpty()) {
            return Collections.emptyList();
        }
        Collection> candidates;
        if (qualifier instanceof FilteringQualifier filteringQualifier) {
            List> bdCandidates = new ArrayList<>(20);
            for (BeanDefinitionProducer producer : beanDefinitionsClasses) {
                if (!producer.isReferenceEnabled(this)) {
                    continue;
                }
                BeanDefinitionReference reference = producer.getReference();
                if (!filteringQualifier.doesQualify(Object.class, reference)) {
                    continue;
                }
                BeanDefinition beanDefinition = reference.load(this);
                if (!beanDefinition.isEnabled(this)) {
                    continue;
                }
                if (!filteringQualifier.doesQualify(Object.class, beanDefinition)) {
                    continue;
                }
                bdCandidates.add(beanDefinition);
            }
            candidates = bdCandidates;
        } else {
            Stream> reduced = qualifier.reduce(Object.class, beanDefinitionsClasses.stream()
                .filter(p -> p.isReferenceEnabled(this))
                .map(BeanDefinitionProducer::getReference));
            Stream> candidateStream = qualifier.reduce(Object.class,
                reduced
                    .map(ref -> ref.load(this))
                    .filter(candidate -> candidate.isEnabled(this))
            );
            candidates = candidateStream.collect(Collectors.toList());
        }

        if (!candidates.isEmpty()) {
            filterReplacedBeans(null, candidates);
        }
        return (Collection) candidates;
    }

    @SuppressWarnings("unchecked")
    @NonNull
    @Override
    public Collection> getAllBeanDefinitions() {
        if (LOG.isDebugEnabled()) {
            LOG.debug("Finding all bean definitions");
        }

        if (!beanDefinitionsClasses.isEmpty()) {
            return beanDefinitionsClasses
                    .stream()
                    .filter(p -> p.isDefinitionEnabled(this))
                    .map(p -> p.getDefinition(this))
                    .collect(Collectors.toList());
        }

        return (Collection>) Collections.emptyMap();
    }

    @Override
    public Collection> getDisabledBeans() {
        return disabledBeans.values().stream()
            .map(producer -> (DisabledBean) producer.reference)
            .filter(Objects::nonNull)
            .collect(Collectors.toList());
    }

    @SuppressWarnings("unchecked")
    @NonNull
    @Override
    public Collection> getBeanDefinitionReferences() {
        if (!beanDefinitionsClasses.isEmpty()) {
            final List refs = beanDefinitionsClasses.stream()
                    .filter(p -> p.isReferenceEnabled(this))
                    .map(BeanDefinitionProducer::getReference)
                    .toList();

            return refs;
        }
        return Collections.emptyList();
    }

    @Override
    @NonNull
    public  BeanContext registerBeanDefinition(@NonNull RuntimeBeanDefinition definition) {
        Objects.requireNonNull(definition, "Bean definition cannot be null");
        Class beanType = definition.getBeanType();
        BeanDefinitionProducer producer = new BeanDefinitionProducer(definition);
        this.beanDefinitionsClasses.add(producer);
        for (Class indexedType : indexedTypes) {
            if (indexedType == beanType || indexedType.isAssignableFrom(beanType)) {
                final Collection indexed = resolveTypeIndex(indexedType);
                indexed.add(producer);
                break;
            }
        }
        purgeCacheForBeanType(beanType);
        return this;
    }

    private  void purgeCacheForBeanType(Class beanType) {
        beanCandidateCache.entrySet().removeIf(entry -> entry.getKey().isAssignableFrom(beanType));
        beanConcreteCandidateCache.entrySet().removeIf(entry -> entry.getKey().beanType.isAssignableFrom(beanType));
        singletonBeanRegistrations.entrySet().removeIf(entry -> entry.getKey().beanType.isAssignableFrom(beanType));
        containsBeanCache.entrySet().removeIf(entry -> entry.getKey().beanType.isAssignableFrom(beanType));
    }

    /**
     * The definition to remove.
     * @param definition The definition to remove
     * @param  The bean type
     */
    @Internal
     void removeBeanDefinition(RuntimeBeanDefinition definition) {
        Class beanType = definition.getBeanType();
        for (Class indexedType : indexedTypes) {
            if (indexedType == beanType || indexedType.isAssignableFrom(beanType)) {
                resolveTypeIndex(indexedType).forEach(p -> p.disable(definition));
                break;
            }
        }
        beanDefinitionsClasses.forEach(p -> p.disable(definition));
        purgeCacheForBeanType(definition.getBeanType());
    }

    /**
     * Get a bean of the given type.
     *
     * @param resolutionContext The bean context resolution
     * @param beanType          The bean type
     * @param                The bean type parameter
     * @return The found bean
     */
    @UsedByGeneratedCode
    @NonNull
    public  T getBean(@Nullable BeanResolutionContext resolutionContext, @NonNull Class beanType) {
        ArgumentUtils.requireNonNull("beanType", beanType);
        return getBean(resolutionContext, Argument.of(beanType), null);
    }

    @NonNull
    @Override
    public  T getBean(@NonNull BeanDefinition definition) {
        ArgumentUtils.requireNonNull("definition", definition);
        return resolveBeanRegistration(null, definition).bean;
    }

    /**
     * Get a bean of the given type and qualifier.
     *
     * @param resolutionContext The bean context resolution
     * @param beanType          The bean type
     * @param qualifier         The qualifier
     * @param                The bean type parameter
     * @return The found bean
     */
    @NonNull
    public  T getBean(@Nullable BeanResolutionContext resolutionContext,
                         @NonNull Class beanType,
                         @Nullable Qualifier qualifier) {
        return getBean(resolutionContext, Argument.of(beanType), qualifier);
    }

    /**
     * Get a bean of the given type and qualifier.
     *
     * @param resolutionContext The bean context resolution
     * @param beanType          The bean type
     * @param qualifier         The qualifier
     * @param                The bean type parameter
     * @return The found bean
     * @since 3.0.0
     */
    @NonNull
    public  T getBean(@Nullable BeanResolutionContext resolutionContext,
                         @NonNull Argument beanType,
                         @Nullable Qualifier qualifier) {
        ArgumentUtils.requireNonNull("beanType", beanType);
        return resolveBeanRegistration(resolutionContext, beanType, qualifier, true).bean;
    }

    /**
     * Get a bean of the given bean definition, type and qualifier.
     *
     * @param resolutionContext The bean context resolution
     * @param beanDefinition    The bean definition
     * @param beanType          The bean type
     * @param qualifier         The qualifier
     * @param                The bean type parameter
     * @return The found bean
     * @since 3.5.0
     */
    @Internal
    @NonNull
    public  T getBean(@Nullable BeanResolutionContext resolutionContext,
                         @NonNull BeanDefinition beanDefinition,
                         @NonNull Argument beanType,
                         @Nullable Qualifier qualifier) {
        ArgumentUtils.requireNonNull("beanDefinition", beanDefinition);
        ArgumentUtils.requireNonNull("beanType", beanType);
        return resolveBeanRegistration(resolutionContext, beanDefinition, beanType, qualifier).bean;
    }

    /**
     * Find an optional bean of the given type and qualifier.
     *
     * @param resolutionContext The bean context resolution
     * @param beanType          The bean type
     * @param qualifier         The qualifier
     * @param                The bean type parameter
     * @return The found bean wrapped as an {@link Optional}
     */
    @NonNull
    public  Optional findBean(@Nullable BeanResolutionContext resolutionContext,
                                    @NonNull Class beanType,
                                    @Nullable Qualifier qualifier) {
        return findBean(resolutionContext, Argument.of(beanType), qualifier);
    }

    /**
     * Find an optional bean of the given type and qualifier.
     *
     * @param resolutionContext The bean context resolution
     * @param beanType          The bean type
     * @param qualifier         The qualifier
     * @param                The bean type parameter
     * @return The found bean wrapped as an {@link Optional}
     * @since 3.0.0
     */
    @Internal
    @NonNull
    public  Optional findBean(@Nullable BeanResolutionContext resolutionContext,
                                    @NonNull Argument beanType,
                                    @Nullable Qualifier qualifier) {
        ArgumentUtils.requireNonNull("beanType", beanType);
        // allow injection the bean context
        if (thisInterfaces.contains(beanType.getType())) {
            return Optional.of((T) this);
        }

        try {
            BeanRegistration beanRegistration = resolveBeanRegistration(resolutionContext, beanType, qualifier, false);
            if (beanRegistration == null || beanRegistration.bean == null) {
                return Optional.empty();
            } else {
                return Optional.of(beanRegistration.bean);
            }
        } catch (DisabledBeanException e) {
            if (AbstractBeanContextConditional.ConditionLog.LOG.isDebugEnabled()) {
                AbstractBeanContextConditional.ConditionLog.LOG.debug("Bean of type [{}] disabled for reason: {}", beanType.getSimpleName(), e.getMessage());
            }
            return Optional.empty();
        }
    }

    @Override
    public BeanContextConfiguration getContextConfiguration() {
        return this.beanContextConfiguration;
    }

    @SuppressWarnings("unchecked")
    @Override
    public void publishEvent(@NonNull Object event) {
        if (event != null) {
            getBean(Argument.of(ApplicationEventPublisher.class, event.getClass())).publishEvent(event);
        }
    }

    @Override
    public @NonNull
    Future publishEventAsync(@NonNull Object event) {
        Objects.requireNonNull(event, "Event cannot be null");
        return getBean(Argument.of(ApplicationEventPublisher.class, event.getClass())).publishEventAsync(event);
    }

    @NonNull
    @Override
    public  Optional> findProxyBeanDefinition(@NonNull Class beanType, @Nullable Qualifier qualifier) {
        ArgumentUtils.requireNonNull("beanType", beanType);
        return findProxyBeanDefinition(Argument.of(beanType), qualifier);
    }

    @NonNull
    @Override
    public  Optional> findProxyBeanDefinition(@NonNull Argument beanType, @Nullable Qualifier qualifier) {
        ArgumentUtils.requireNonNull("beanType", beanType);
        for (BeanDefinition beanDefinition : getBeanDefinitions(beanType, qualifier)) {
            if (beanDefinition.isProxy()) {
                return Optional.of(beanDefinition);
            }
        }
        return Optional.empty();
    }

    /**
     * Invalidates the bean caches. For testing only.
     */
    @Internal
    protected void invalidateCaches() {
        beanCandidateCache.clear();
        beanConcreteCandidateCache.clear();
        singletonBeanRegistrations.clear();
    }

    /**
     * Resolves the {@link BeanDefinitionReference} class instances. Default implementation uses ServiceLoader pattern.
     *
     * @return The bean definition classes
     */
    @NonNull
    protected List resolveBeanDefinitionReferences() {
        if (beanDefinitionReferences == null) {
            final SoftServiceLoader definitions = SoftServiceLoader.load(BeanDefinitionReference.class, classLoader);
            beanDefinitionReferences = new ArrayList<>(300);
            definitions.collectAll(beanDefinitionReferences, BeanDefinitionReference::isPresent);
        }
        return beanDefinitionReferences;
    }

    /**
     * Resolves the {@link BeanDefinitionReference} class instances. Default implementation uses ServiceLoader pattern.
     *
     * @param predicate The filter predicate, can be null
     * @return The bean definition classes
     */
    @NonNull
    @Deprecated
    protected List resolveBeanDefinitionReferences(@Nullable Predicate predicate) {
        if (predicate != null) {
            List allRefs = resolveBeanDefinitionReferences();
            List newRefs = new ArrayList<>(allRefs.size());
            for (BeanDefinitionReference reference : allRefs) {
                if (predicate.test(reference)) {
                    newRefs.add(reference);
                }
            }
            return newRefs;
        }
        return resolveBeanDefinitionReferences();
    }

    /**
     * Resolves the {@link BeanConfiguration} class instances. Default implementation uses ServiceLoader pattern.
     *
     * @return The bean definition classes
     */
    @NonNull
    protected Iterable resolveBeanConfigurations() {
        if (beanConfigurationsList == null) {
            final SoftServiceLoader definitions = SoftServiceLoader.load(BeanConfiguration.class, classLoader);
            beanConfigurationsList = new ArrayList<>(300);
            definitions.collectAll(beanConfigurationsList, null);
        }
        return beanConfigurationsList;
    }

    /**
     * Initialize the event listeners.
     */
    protected void initializeEventListeners() {
        this.beanCreationEventListeners = loadListeners(BeanCreatedEventListener.class);
        this.beanCreationEventListeners.add(new AbstractMap.SimpleEntry<>(AnnotationProcessor.class, new AnnotationProcessorListenersSupplier()));
        this.beanInitializedEventListeners = loadListeners(BeanInitializedEventListener.class);
    }

    @NonNull
    private  List, ListenersSupplier>> loadListeners(@NonNull Class listenerType) {
        final Map, List>> typeToListener = getTypeToListenerMap(listenerType);
        if (typeToListener.isEmpty()) {
            return new ArrayList<>(1);
        }
        List, ListenersSupplier>> eventToListeners = new ArrayList<>(typeToListener.size());
        for (Map.Entry, List>> e : typeToListener.entrySet()) {
            eventToListeners.add(new AbstractMap.SimpleEntry<>(e.getKey(), new EventListenerListenersSupplier<>(e.getValue())));
        }
        return eventToListeners;
    }

    @NonNull
    private  Map, List>> getTypeToListenerMap(@NonNull Class listenerType) {
        final Collection> beanDefinitions = getBeanDefinitions(listenerType);
        if (beanDefinitions.isEmpty()) {
            return Collections.emptyMap();
        }
        final HashMap, List>> typeToListener = CollectionUtils.newHashMap(beanDefinitions.size());
        for (BeanDefinition beanCreatedDefinition : beanDefinitions) {
            List> typeArguments = beanCreatedDefinition.getTypeArguments(listenerType);
            Argument argument = CollectionUtils.last(typeArguments);
            if (argument == null) {
                argument = Argument.OBJECT_ARGUMENT;
            }
            typeToListener.computeIfAbsent(argument.getType(), aClass -> new ArrayList<>(10))
                    .add(beanCreatedDefinition);
        }
        return typeToListener;
    }

    /**
     * Initialize the context with the given {@link io.micronaut.context.annotation.Context} scope beans.
     *
     * @param eagerInitBeans The context scope beans
     * @param processedBeans    The beans that require {@link ExecutableMethodProcessor} handling
     * @param parallelBeans     The parallel bean definitions
     */
    @Internal
    protected void initializeContext(
            @NonNull List eagerInitBeans,
            @NonNull List processedBeans,
            @NonNull List parallelBeans) {

        if (CollectionUtils.isNotEmpty(eagerInitBeans)) {
            final List> eagerInit = new ArrayList<>(eagerInitBeans.size());
            for (BeanDefinitionProducer contextScopeBean : eagerInitBeans) {
                try {
                    loadEagerBeans(contextScopeBean, eagerInit);
                } catch (Throwable e) {
                    throw new BeanInstantiationException(MSG_BEAN_DEFINITION + contextScopeBean.getReference().getName() + MSG_COULD_NOT_BE_LOADED + e.getMessage(), e);
                }
            }
            filterReplacedBeans(null, eagerInit);
            OrderUtil.sortOrdered(eagerInit);
            for (BeanDefinition eagerInitDefinition : eagerInit) {
                try {
                    initializeEagerBean(eagerInitDefinition);
                } catch (DisabledBeanException e) {
                    if (AbstractBeanContextConditional.ConditionLog.LOG.isDebugEnabled()) {
                        AbstractBeanContextConditional.ConditionLog.LOG.debug("Bean of type [{}] disabled for reason: {}", eagerInitDefinition.getBeanType().getSimpleName(), e.getMessage());
                    }
                } catch (Throwable e) {
                    throw new BeanInstantiationException(MSG_BEAN_DEFINITION + eagerInitDefinition.getName() + MSG_COULD_NOT_BE_LOADED + e.getMessage(), e);
                }
            }
        }

        if (!processedBeans.isEmpty()) {
            List> methodsToProcess = new ArrayList<>();
            for (BeanDefinitionProducer processedBeanProducer : processedBeans) {
                if (!processedBeanProducer.isDefinitionEnabled(this)) {
                    continue;
                }
                BeanDefinition definition = processedBeanProducer.getDefinition(this);
                for (ExecutableMethod method : definition.getExecutableMethods()) {
                    if (method.hasStereotype(Executable.class)) {
                        methodsToProcess.add(BeanDefinitionMethodReference.of(definition, (ExecutableMethod) method));
                    }
                }
            }

            Map, List>> byAnnotation = CollectionUtils.newHashMap(methodsToProcess.size());
            // group the method references by annotation type such that we have a map of Annotation -> MethodReference
            // ie. Class -> @Scheduled void someAnnotation()
            for (BeanDefinitionMethodReference executableMethod : methodsToProcess) {
                List> annotations = executableMethod.getAnnotationTypesByStereotype(Executable.class);
                for (Class annotation : annotations) {
                    List> references = byAnnotation.get(annotation);
                    if (references == null) {
                        references = new ArrayList<>(10);
                        byAnnotation.put(annotation, references);
                    }
                    references.add(executableMethod);
                }
            }

            // Find ExecutableMethodProcessor for each annotation and process the BeanDefinitionMethodReference
            for (Map.Entry, List>> entry : byAnnotation.entrySet()) {
                Class annotationType = entry.getKey();
                List> methods = entry.getValue();
                streamOfType(ExecutableMethodProcessor.class, Qualifiers.byTypeArguments(annotationType))
                    .forEach(processor -> {
                        if (processor instanceof LifeCycle cycle) {
                            cycle.start();
                        }
                        for (BeanDefinitionMethodReference method : methods) {

                            BeanDefinition beanDefinition = method.getBeanDefinition();

                            // Only process the method if the annotation is not declared at the class level
                            // If declared at the class level it will already have been processed by AnnotationProcessorListener
                            if (!beanDefinition.hasStereotype(annotationType)) {
                                if (method.hasDeclaredStereotype(Parallel.class)) {
                                    ForkJoinPool.commonPool().execute(() -> {
                                        try {
                                            processor.process(beanDefinition, method);
                                        } catch (Throwable e) {
                                            if (LOG.isErrorEnabled()) {
                                                LOG.error("Error processing bean method {}.{} with processor ({}): {}", beanDefinition, method, processor, e.getMessage(), e);
                                            }
                                            Boolean shutdownOnError = method.booleanValue(Parallel.class, "shutdownOnError").orElse(true);
                                            if (shutdownOnError) {
                                                stop();
                                            }
                                        }
                                    });
                                } else {
                                    processor.process(beanDefinition, method);
                                }
                            }
                        }

                        if (processor instanceof LifeCycle cycle) {
                            cycle.stop();
                        }

                    });
            }
        }

        if (CollectionUtils.isNotEmpty(parallelBeans)) {
            processParallelBeans(parallelBeans);
        }
        ForkJoinPool.commonPool().execute(() -> beanDefinitionsClasses.forEach(p -> p.isReferenceEnabled(this)));
    }

    /**
     * Find bean candidates for the given type.
     *
     * @param                The bean generic type
     * @param resolutionContext The current resolution context
     * @param beanType          The bean type
     * @param filter            A bean definition to filter out
     * @return The candidates
     */
    @NonNull
    protected  Collection> findBeanCandidates(@Nullable BeanResolutionContext resolutionContext,
                                                                   @NonNull Argument beanType,
                                                                   @Nullable BeanDefinition filter) {
        Predicate> predicate = filter == null ? null : definition -> !definition.equals(filter);
        return findBeanCandidates(resolutionContext, beanType, true, predicate);
    }

    /**
     * Find bean candidates for the given type.
     *
     * @param                The bean generic type
     * @param resolutionContext The current resolution context
     * @param beanType          The bean type
     * @param collectIterables  Whether iterables should be collected
     * @param predicate         The predicate to filter candidates
     * @return The candidates
     */
    @NonNull
    protected  Collection> findBeanCandidates(@Nullable BeanResolutionContext resolutionContext,
                                                                   @NonNull Argument beanType,
                                                                   boolean collectIterables,
                                                                   Predicate> predicate) {
        ArgumentUtils.requireNonNull("beanType", beanType);
        final Class beanClass = beanType.getType();
        if (LOG.isDebugEnabled()) {
            LOG.debug("Finding candidate beans for type: {}", beanType);
        }
        // first traverse component definition classes and load candidates

        Collection beanDefinitionsClasses;

        if (indexedTypes.contains(beanClass)) {
            beanDefinitionsClasses = beanIndex.get(beanClass);
            if (beanDefinitionsClasses == null) {
                beanDefinitionsClasses = Collections.emptyList();
            }
        } else {
            beanDefinitionsClasses = this.beanDefinitionsClasses;
        }

        return collectBeanCandidates(
            resolutionContext,
            beanType,
            collectIterables,
            predicate,
            beanDefinitionsClasses
        );
    }

    @NonNull
    private  Set> collectBeanCandidates(
        BeanResolutionContext resolutionContext,
        Argument beanType,
        boolean collectIterables,
        @Nullable
        Predicate> predicate,
        Collection beanDefinitionProducers) {
        Set> candidates;
        if (!beanDefinitionProducers.isEmpty()) {

            candidates = new HashSet<>();
            for (BeanDefinitionProducer producer : beanDefinitionProducers) {
                if (producer.isDisabled() || !producer.isReferenceCandidateBean(beanType) || !producer.isReferenceEnabled(this, resolutionContext)) {
                    continue;
                }
                BeanDefinition loadedBean = producer.getDefinition(this);
                if (!loadedBean.isCandidateBean(beanType)) {
                    continue;
                }
                if (predicate != null && !predicate.test(loadedBean)) {
                    continue;
                }
                if (!producer.isDefinitionEnabled(this, resolutionContext)) {
                    continue;
                }

                if (collectIterables && loadedBean.isConfigurationProperties()) {
                    collectIterableBeans(resolutionContext, loadedBean, candidates);
                } else {
                    candidates.add(loadedBean);
                }
            }

            if (!candidates.isEmpty()) {
                filterReplacedBeans(resolutionContext, candidates);
            }
        } else {
            candidates = Collections.emptySet();
        }

        if (LOG.isDebugEnabled()) {
            if (candidates.isEmpty()) {
                LOG.debug("No bean candidates found for type: {}", beanType);
            } else {
                for (BeanDefinition candidate : candidates) {
                    LOG.debug("  {} {} {}", candidate.getBeanType(), candidate.getDeclaredQualifier(), candidate);
                }
            }
        }
        return candidates;
    }

    /**
     * Collects iterable beans from a given iterable.
     * @param resolutionContext The resolution context
     * @param iterableBean The iterable
     * @param targetSet The target set
     * @param  The bean type
     */
    protected  void collectIterableBeans(@Nullable BeanResolutionContext resolutionContext, @NonNull BeanDefinition iterableBean, Set> targetSet) {
        // no-op
    }

    /**
     * Find bean candidates for the given type.
     *
     * @param instance The bean instance
     * @param       The bean generic type
     * @return The candidates
     */
    @NonNull
    protected  Collection> findBeanCandidatesForInstance(@NonNull T instance) {
        ArgumentUtils.requireNonNull("instance", instance);
        if (LOG.isDebugEnabled()) {
            LOG.debug("Finding candidate beans for instance: {}", instance);
        }
        Collection beanProducers = this.beanDefinitionsClasses;
        final Class beanClass = instance.getClass();
        Argument beanType = Argument.of(beanClass);
        Collection> beanDefinitions = (Collection>) ((Map) beanCandidateCache).get(beanType);
        if (beanDefinitions != null) {
            return beanDefinitions;
        }
        // first traverse component definition classes and load candidates
        if (!beanDefinitionsClasses.isEmpty()) {
            List> candidates = new ArrayList<>();
            for (BeanDefinitionProducer producer : beanProducers) {
                if (producer.isDisabled() || !producer.isReferenceEnabled(this)) {
                    continue;
                }
                BeanDefinitionReference reference = producer.getReference();
                Class candidateType = reference.getBeanType();
                if (candidateType == null || !candidateType.isInstance(instance)) {
                    continue;
                }
                BeanDefinition candidate = reference.load(this);
                if (!candidate.isEnabled(this)) {
                    continue;
                }
                candidates.add(candidate);
            }

            if (candidates.size() > 1) {
                // try narrow to exact type
                List> list = new ArrayList<>(2);
                for (BeanDefinition candidate : candidates) {
                    if (candidate.getBeanType() == beanClass) {
                        list.add(candidate);
                    }
                }
                candidates = list;
            }
            if (LOG.isDebugEnabled()) {
                LOG.debug("Resolved bean candidates {} for instance: {}", candidates, instance);
            }
            beanDefinitions = candidates;
        } else {
            if (LOG.isDebugEnabled()) {
                LOG.debug("No bean candidates found for instance: {}", instance);
            }
            beanDefinitions = Collections.emptySet();
        }
        beanCandidateCache.put(beanType, (Collection) beanDefinitions);
        return beanDefinitions;
    }

    /**
     * Registers an active configuration.
     *
     * @param configuration The configuration to register
     */
    protected synchronized void registerConfiguration(@NonNull BeanConfiguration configuration) {
        ArgumentUtils.requireNonNull("configuration", configuration);
        beanConfigurations.put(configuration.getName(), configuration);
    }

    /**
     * Execution the creation of a bean. The returned value can be null if a
     * factory method returned null.
     *
     * @param resolutionContext The {@link BeanResolutionContext}
     * @param beanDefinition    The {@link BeanDefinition}
     * @param qualifier         The {@link Qualifier}
     * @param argumentValues    Any argument values passed to create the bean
     * @param                The bean generic type
     * @return The created bean
     */
    @Internal
    @NonNull
    private  T doCreateBean(@NonNull BeanResolutionContext resolutionContext,
                               @NonNull BeanDefinition beanDefinition,
                               @Nullable Qualifier qualifier,
                               @Nullable Map argumentValues) {
        T bean;
        if (beanDefinition instanceof InstantiatableBeanDefinition instantiatableBeanDefinition) {
            bean = resolveByBeanFactory(resolutionContext, instantiatableBeanDefinition, qualifier, argumentValues);
        } else {
            throw new BeanInstantiationException("BeanDefinition doesn't support creating a new instance of the bean");
        }
        return postBeanCreated(resolutionContext, beanDefinition, qualifier, bean);
    }

    @Internal
    @NonNull
    private  T doCreateBean(@NonNull BeanResolutionContext resolutionContext,
                               @NonNull BeanDefinition beanDefinition,
                               @Nullable Qualifier qualifier) {
        return doCreateBean(resolutionContext, beanDefinition, qualifier, Collections.emptyMap());
    }

    @NonNull
    private  T resolveByBeanFactory(@NonNull BeanResolutionContext resolutionContext,
                                       @NonNull BeanDefinition beanDefinition,
                                       @Nullable Qualifier qualifier,
                                       @Nullable Map argumentValues) {
        Qualifier declaredQualifier = beanDefinition.getDeclaredQualifier();
        Qualifier prevQualifier = resolutionContext.getCurrentQualifier();
        try {
            resolutionContext.setCurrentQualifier(declaredQualifier != null && !AnyQualifier.INSTANCE.equals(declaredQualifier) ? declaredQualifier : qualifier);
            T bean;
            if (beanDefinition instanceof ParametrizedInstantiatableBeanDefinition parametrizedInstantiatableBeanDefinition) {
                Argument[] requiredArguments = parametrizedInstantiatableBeanDefinition.getRequiredArguments();
                Map convertedValues = getRequiredArgumentValues(resolutionContext, requiredArguments, argumentValues, beanDefinition);
                bean = parametrizedInstantiatableBeanDefinition.instantiate(resolutionContext, this, convertedValues);
            } else if (beanDefinition instanceof InstantiatableBeanDefinition instantiatableBeanDefinition) {
                bean = instantiatableBeanDefinition.instantiate(resolutionContext, this);
            } else {
                throw new BeanInstantiationException(resolutionContext, "Expected InstantiatableBeanDefinition [" + beanDefinition + "]");
            }
            if (bean == null) {
                throw new BeanInstantiationException(resolutionContext, "InstantiatableBeanDefinition [" + beanDefinition + "] returned null");
            }
            if (bean instanceof Qualified qualified) {
                qualified.$withBeanQualifier(declaredQualifier);
            }
            return bean;
        } catch (DependencyInjectionException | DisabledBeanException | BeanInstantiationException e) {
            throw e;
        } catch (Throwable e) {
            if (!resolutionContext.getPath().isEmpty()) {
                throw new BeanInstantiationException(resolutionContext, e);
            }
            throw new BeanInstantiationException(beanDefinition, e);
        } finally {
            resolutionContext.setCurrentQualifier(prevQualifier);
        }
    }

    @NonNull
    private  T postBeanCreated(@NonNull BeanResolutionContext resolutionContext,
                                  @NonNull BeanDefinition beanDefinition,
                                  @Nullable Qualifier qualifier,
                                  @NonNull T bean) {
        Qualifier finalQualifier = qualifier != null ? qualifier : beanDefinition.getDeclaredQualifier();

        bean = triggerBeanCreatedEventListener(resolutionContext, beanDefinition, bean, finalQualifier);

        if (beanDefinition instanceof ValidatedBeanDefinition validatedBeanDefinition) {
            bean = validatedBeanDefinition.validate(resolutionContext, bean);
        }
        if (LOG_LIFECYCLE.isDebugEnabled()) {
            LOG_LIFECYCLE.debug("Created bean [{}] from definition [{}] with qualifier [{}]", bean, beanDefinition, finalQualifier);
        }
        return bean;
    }

    @NonNull
    private  T triggerBeanCreatedEventListener(@NonNull BeanResolutionContext resolutionContext,
                                                  @NonNull BeanDefinition beanDefinition,
                                                  @NonNull T bean,
                                                  @Nullable Qualifier finalQualifier) {
        if (!(beanDefinition instanceof AbstractProviderDefinition)) {
            Class beanType = beanDefinition.getBeanType();
            if (!(bean instanceof BeanCreatedEventListener) && CollectionUtils.isNotEmpty(beanCreationEventListeners)) {
                for (Map.Entry, ListenersSupplier> entry : beanCreationEventListeners) {
                    if (entry.getKey().isAssignableFrom(beanType)) {
                        BeanKey beanKey = new BeanKey<>(beanDefinition, finalQualifier);
                        for (BeanCreatedEventListener listener : entry.getValue().get(resolutionContext)) {
                            bean = (T) listener.onCreated(new BeanCreatedEvent(this, beanDefinition, beanKey, bean));
                            if (bean == null) {
                                throw new BeanInstantiationException(resolutionContext, "Listener [" + listener + "] returned null from onCreated event");
                            }
                        }
                    }
                }
            }
        }
        return bean;
    }

    @NonNull
    private  Map getRequiredArgumentValues(@NonNull BeanResolutionContext resolutionContext,
                                                              @NonNull Argument[] requiredArguments,
                                                              @Nullable Map argumentValues,
                                                              @NonNull BeanDefinition beanDefinition) {
        Map convertedValues;
        if (argumentValues == null) {
            convertedValues = requiredArguments.length == 0 ? null : CollectionUtils.newLinkedHashMap(requiredArguments.length);
            argumentValues = Collections.emptyMap();
        } else {
            convertedValues = CollectionUtils.newLinkedHashMap(requiredArguments.length);
        }
        if (convertedValues == null) {
            return Collections.emptyMap();
        }
        MutableConversionService conversionService = getConversionService();
        for (Argument requiredArgument : requiredArguments) {
            String argumentName = requiredArgument.getName();
            Object val = argumentValues.get(argumentName);
            if (val == null) {
                if (!requiredArgument.isDeclaredNullable()) {
                    throw new BeanInstantiationException(resolutionContext, "Missing bean argument [" + requiredArgument + "] for type: " + beanDefinition.getBeanType().getName() + ". Required arguments: " + ArrayUtils.toString(requiredArguments));
                }
            } else {
                Object convertedValue;
                if (requiredArgument.getType().isInstance(val)) {
                    convertedValue = val;
                } else {
                    convertedValue = conversionService.convert(val, requiredArgument).orElseThrow(() ->
                            new BeanInstantiationException(resolutionContext, "Invalid bean argument [" + requiredArgument + "]. Cannot convert object [" + val + "] to required type: " + requiredArgument.getType())
                    );
                }
                convertedValues.put(argumentName, convertedValue);
            }
        }
        return convertedValues;
    }

    /**
     * Fall back method to attempt to find a candidate for the given definitions.
     *
     * @param beanType   The bean type
     * @param qualifier  The qualifier
     * @param candidates The candidates, always more than 1
     * @param         The generic time
     * @return The concrete bean definition
     */
    @NonNull
    protected  BeanDefinition findConcreteCandidate(@NonNull Class beanType,
                                                          @Nullable Qualifier qualifier,
                                                          @NonNull Collection> candidates) {
        if (qualifier instanceof AnyQualifier) {
            return candidates.iterator().next();
        } else {
            throw new NonUniqueBeanException(beanType, candidates.iterator());
        }
    }

    /**
     * Processes parallel bean definitions.
     *
     * @param parallelBeans The parallel beans
     */
    @Internal
    protected void processParallelBeans(List parallelBeans) {
        if (!parallelBeans.isEmpty()) {
            List finalParallelBeans = parallelBeans.stream()
                    .filter(p -> p.isReferenceEnabled(this))
                    .toList();
            if (!finalParallelBeans.isEmpty()) {
                new Thread(() -> {
                    Collection> parallelDefinitions = new ArrayList<>();
                    finalParallelBeans.forEach(producer -> {
                        try {
                            loadEagerBeans(producer, parallelDefinitions);
                        } catch (Throwable e) {
                            BeanDefinitionReference beanDefinitionReference = producer.getReference();
                            LOG.error("Parallel Bean definition [{}{}{}]", beanDefinitionReference.getName(), MSG_COULD_NOT_BE_LOADED, e.getMessage(), e);
                            Boolean shutdownOnError = beanDefinitionReference.getAnnotationMetadata().booleanValue(Parallel.class, "shutdownOnError").orElse(true);
                            if (shutdownOnError) {
                                stop();
                            }
                        }
                    });

                    filterReplacedBeans(null, parallelDefinitions);

                    parallelDefinitions.forEach(beanDefinition -> ForkJoinPool.commonPool().execute(() -> {
                        try {
                            initializeEagerBean(beanDefinition);
                        } catch (Throwable e) {
                            LOG.error("Parallel Bean definition [{}{}{}]", beanDefinition.getName(), MSG_COULD_NOT_BE_LOADED, e.getMessage(), e);
                            Boolean shutdownOnError = beanDefinition.getAnnotationMetadata().booleanValue(Parallel.class, "shutdownOnError").orElse(true);
                            if (shutdownOnError) {
                                stop();
                            }
                        }
                    }));
                    parallelDefinitions.clear();

                }).start();
            }
        }
    }

    private  void filterReplacedBeans(BeanResolutionContext resolutionContext, Collection> candidates) {
        if (candidates.size() > 1) {
            List> replacementTypes = new ArrayList<>(2);
            for (BeanDefinition candidate : candidates) {
                if (candidate.getAnnotationMetadata().hasStereotype(REPLACES_ANN)) {
                    replacementTypes.add(candidate);
                }
            }
            if (!replacementTypes.isEmpty()) {
                candidates.removeIf(definition -> checkIfReplacementExists(resolutionContext, replacementTypes, definition));
            }
        }
    }

    private  boolean checkIfReplacementExists(BeanResolutionContext resolutionContext,
                                                 List> replacementTypes,
                                                 BeanDefinition definitionToBeReplaced) {
        if (!definitionToBeReplaced.isEnabled(this, resolutionContext)) {
            return true;
        }
        final AnnotationMetadata annotationMetadata = definitionToBeReplaced.getAnnotationMetadata();
        if (annotationMetadata.hasDeclaredStereotype(Infrastructure.class)) {
            return false;
        }
        for (BeanDefinition replacementType : replacementTypes) {
            if (isNotTheSameDefinition(replacementType, definitionToBeReplaced) &&
                    isNotProxy(replacementType, definitionToBeReplaced) &&
                    checkIfReplaces(replacementType, definitionToBeReplaced, annotationMetadata)) {
                return true;
            }
        }
        return false;
    }

    private  boolean isNotTheSameDefinition(BeanDefinition replacingCandidate, BeanDefinition definitionToBeReplaced) {
        if (replacingCandidate instanceof BeanDefinitionDelegate beanDefinitionDelegate) {
            replacingCandidate = beanDefinitionDelegate.getDelegate();
        }
        if (definitionToBeReplaced instanceof BeanDefinitionDelegate beanDefinitionDelegate) {
            definitionToBeReplaced = beanDefinitionDelegate.getDelegate();
        }
        return replacingCandidate != definitionToBeReplaced;
    }

    private  boolean isNotProxy(BeanDefinition replacingCandidate, BeanDefinition definitionToBeReplaced) {
        return !(replacingCandidate instanceof ProxyBeanDefinition &&
                ((ProxyBeanDefinition) replacingCandidate).getTargetDefinitionType() == definitionToBeReplaced.getClass());
    }

    private  boolean checkIfReplaces(BeanDefinition replacingCandidate, BeanDefinition definitionToBeReplaced, AnnotationMetadata annotationMetadata) {
        final AnnotationValue replacesAnnotation = replacingCandidate.getAnnotation(Replaces.class);
        final Class replacedBeanType = replacesAnnotation.classValue(Replaces.MEMBER_BEAN).orElse(getCanonicalBeanType(replacingCandidate));
        final Optional named = replacesAnnotation.stringValue(Replaces.MEMBER_NAMED);
        final Optional> qualifier = replacesAnnotation.annotationClassValue(Replaces.MEMBER_QUALIFIER);

        if (named.isPresent() && qualifier.isPresent()) {
            throw new ConfigurationException("Both \"named\" and \"qualifier\" should not be present: " + replacesAnnotation);
        }

        if (named.isPresent()) {
            final String name = named.get();
            if (qualifiedByNamed(definitionToBeReplaced, replacedBeanType, name)) {
                if (LOG.isDebugEnabled()) {
                    LOG.debug("Bean [{}] replaces existing bean of type [{}] qualified by name [{}]",
                            replacingCandidate.getBeanType(), definitionToBeReplaced.getBeanType(), name);
                }
                return true;
            }
            return false;
        }

        if (qualifier.isPresent()) {
            final AnnotationClassValue qualifierClassValue = qualifier.get();
            if (qualifiedByQualifier(definitionToBeReplaced, replacedBeanType, qualifierClassValue)) {
                if (LOG.isDebugEnabled()) {
                    LOG.debug("Bean [{}] replaces existing bean of type [{}] qualified by qualifier [{}]",
                            replacingCandidate.getBeanType(), definitionToBeReplaced.getBeanType(), qualifierClassValue);
                }
                return true;
            }
            return false;
        }

        Optional> factory = replacesAnnotation.classValue(Replaces.MEMBER_FACTORY);
        if (factory.isPresent()) {
            Optional> declaringType = definitionToBeReplaced.getDeclaringType();
            if (declaringType.isPresent()) {
                Class factoryClass = factory.get();
                final boolean factoryReplaces = factoryClass == declaringType.get() &&
                        checkIfTypeMatches(definitionToBeReplaced, annotationMetadata, replacedBeanType);
                if (factoryReplaces) {
                    if (LOG.isDebugEnabled()) {
                        LOG.debug("Bean [{}] replaces existing bean of type [{}] in factory type [{}]",
                                replacingCandidate.getBeanType(), replacedBeanType, factoryClass);
                    }
                    return true;
                }
            }
            return false;
        }

        final boolean isTypeMatches = checkIfTypeMatches(definitionToBeReplaced, annotationMetadata, replacedBeanType);
        if (isTypeMatches && LOG.isDebugEnabled()) {
            LOG.debug("Bean [{}] replaces existing bean of type [{}]", replacingCandidate.getBeanType(), replacedBeanType);
        }
        return isTypeMatches;
    }

    private  boolean qualifiedByQualifier(BeanDefinition definitionToBeReplaced,
                                             Class replacedBeanType,
                                             AnnotationClassValue qualifier) {
        @SuppressWarnings("unchecked") final Class qualifierClass =
                (Class) qualifier.getType().orElse(null);
        if (qualifierClass != null && !qualifierClass.isAssignableFrom(Annotation.class)) {
            return Qualifiers.byStereotype(qualifierClass).doesQualify(replacedBeanType, definitionToBeReplaced);
        } else {
            throw new ConfigurationException("Default qualifier value was used while replacing %s".formatted(replacedBeanType));
        }
    }

    private  boolean qualifiedByNamed(BeanType definitionToBeReplaced, Class replacedBeanType, String named) {
        return Qualifiers.byName(named).doesQualify(replacedBeanType, definitionToBeReplaced);
    }

    private  Class getCanonicalBeanType(BeanDefinition beanDefinition) {
        if (beanDefinition instanceof BeanDefinitionDelegate beanDefinitionDelegate) {
            beanDefinition = beanDefinitionDelegate.getDelegate();
        }
        if (beanDefinition instanceof AdvisedBeanType advisedBeanType) {
            return (Class) advisedBeanType.getInterceptedType();
        }
        if (beanDefinition instanceof ProxyBeanDefinition proxyBeanDefinition) {
            return proxyBeanDefinition.getTargetType();
        }
        return beanDefinition.getBeanType();
    }

    private  boolean checkIfTypeMatches(BeanDefinition definitionToBeReplaced,
                                           AnnotationMetadata annotationMetadata,
                                           Class replacingCandidate) {
        Class bt = getCanonicalBeanType(definitionToBeReplaced);
        if (annotationMetadata.hasAnnotation(DefaultImplementation.class)) {
            Optional defaultImpl = annotationMetadata.classValue(DefaultImplementation.class);
            if (defaultImpl.isEmpty()) {
                defaultImpl = annotationMetadata.classValue(DefaultImplementation.class, "name");
            }
            if (defaultImpl.filter(impl -> impl == bt).isPresent()) {
                return replacingCandidate.isAssignableFrom(bt);
            } else {
                return replacingCandidate == bt;
            }
        }
        return replacingCandidate != Object.class && replacingCandidate.isAssignableFrom(bt);
    }

    private  void doInjectAndInitialize(BeanResolutionContext resolutionContext, T instance, BeanDefinition beanDefinition) {
        if (beanDefinition instanceof InjectableBeanDefinition injectableBeanDefinition) {
            injectableBeanDefinition.inject(resolutionContext, this, instance);
            if (beanDefinition instanceof InitializingBeanDefinition initializingBeanDefinition) {
                initializingBeanDefinition.initialize(resolutionContext, this, instance);
            }
        } else {
            throw new BeanContextException(MSG_BEAN_DEFINITION + beanDefinition + "] doesn't support injection!");
        }
    }

    private void loadEagerBeans(BeanDefinitionProducer producer, Collection> collector) {
        if (producer.isReferenceEnabled(this)) {
            BeanDefinitionReference reference = producer.getReference();
            BeanDefinition beanDefinition = reference.load(this);
            try (BeanResolutionContext resolutionContext = newResolutionContext(beanDefinition, null)) {
                if (beanDefinition.isEnabled(this, resolutionContext)) {
                    collector.add(beanDefinition);
                }
            }
        }
    }

    private void initializeEagerBean(BeanDefinition beanDefinition) {
        if (beanDefinition.isIterable() || beanDefinition.hasStereotype(ConfigurationReader.class.getName())) {
            Set> beanCandidates = new HashSet<>(5);

            collectIterableBeans(
                null,
                beanDefinition,
                beanCandidates
            );
            for (BeanDefinition beanCandidate : beanCandidates) {
                findOrCreateSingletonBeanRegistration(
                        null,
                        beanCandidate,
                        beanCandidate.asArgument(),
                        beanCandidate.hasAnnotation(Context.class) ? null : beanDefinition.getDeclaredQualifier()
                );
            }

        } else {
            findOrCreateSingletonBeanRegistration(null, beanDefinition, beanDefinition.asArgument(), null);
        }
    }

    /**
     * Resolve the {@link BeanRegistration} by an argument and a qualifier.
     *
     * @param resolutionContext The resolution context
     * @param beanType          The bean type
     * @param qualifier         The qualifier
     * @param throwNoSuchBean   Throw if it doesn't exist
     * @param                The type
     * @return The bean registration
     */
    @Nullable
    private  BeanRegistration resolveBeanRegistration(@Nullable BeanResolutionContext resolutionContext,
                                                            @NonNull Argument beanType,
                                                            @Nullable Qualifier qualifier,
                                                            boolean throwNoSuchBean) {
        // allow injection the bean context
        final Class beanClass = beanType.getType();
        if (thisInterfaces.contains(beanClass)) {
            return new BeanRegistration<>(BeanIdentifier.of(beanClass.getName()), null, (T) this);
        }
        if (InjectionPoint.class.isAssignableFrom(beanClass)) {
            return provideInjectionPoint(resolutionContext, beanType, qualifier, throwNoSuchBean);
        }
        BeanKey beanKey = new BeanKey<>(beanType, qualifier);

        if (LOG.isTraceEnabled()) {
            LOG.trace("Looking up existing bean for key: {}", beanKey);
        }

        BeanRegistration inFlightBeanRegistration = resolutionContext != null ? resolutionContext.getInFlightBean(beanKey) : null;
        if (inFlightBeanRegistration != null) {
            return inFlightBeanRegistration;
        }

        // Fast singleton lookup
        BeanRegistration beanRegistration = singletonScope.findCachedSingletonBeanRegistration(beanType, qualifier);
        if (beanRegistration != null) {
            return beanRegistration;
        }

        Optional> concreteCandidate = findBeanDefinition(resolutionContext, beanType, qualifier);

        BeanRegistration registration;

        if (concreteCandidate.isPresent()) {
            BeanDefinition definition = concreteCandidate.get();

            if (definition.isContainerType() && beanClass != definition.getBeanType()) {
                throw new NonUniqueBeanException(beanClass, Collections.singletonList(definition).iterator());
            }
            registration = resolveBeanRegistration(resolutionContext, definition, beanType, qualifier);
        } else {
            registration = null;
        }
        if ((registration == null || registration.bean == null) && throwNoSuchBean) {
            throw newNoSuchBeanException(resolutionContext, beanType, qualifier, null);
        }
        return registration;
    }

    private void assertContextState() {
        if (!this.running.get() && !this.initializing.get()) {
            throw new BeanContextException("Cannot resolve beans until the context is running");
        }
    }

    private  Optional> findBeanDefinition(BeanResolutionContext resolutionContext, Argument beanType, Qualifier qualifier) {
        BeanDefinition beanDefinition = singletonScope.findCachedSingletonBeanDefinition(beanType, qualifier);
        if (beanDefinition != null) {
            return Optional.of(beanDefinition);
        }
        return findConcreteCandidate(resolutionContext, beanType, qualifier, true);
    }

    /**
     * Trigger a no such bean exception. Subclasses can improve the exception with downstream diagnosis as necessary.
     *
     * @param                The type of the bean
     * @param resolutionContext The resolution context
     * @param beanType          The bean type
     * @param qualifier         The qualifier
     * @param message           A message to use
     * @return A no such bean exception
     */
    @Internal
    @NonNull
    protected  NoSuchBeanException newNoSuchBeanException(
        @Nullable BeanResolutionContext resolutionContext,
        @NonNull Argument beanType,
        @NonNull Qualifier qualifier,
        @Nullable String message) {
        if (message != null) {
            return new NoSuchBeanException(beanType, qualifier, message);
        } else {
            String disabledMessage = resolveDisabledBeanMessage(resolutionContext, beanType, qualifier);

            if (disabledMessage != null) {
                return new NoSuchBeanException(beanType, qualifier, disabledMessage);
            } else {
                return new NoSuchBeanException(beanType, qualifier);
            }
        }
    }

    /**
     * Resolves the message to use for a disabled bean.
     * @param resolutionContext The resolution context
     * @param beanType The bean type
     * @param qualifier The qualifier
     * @return The message or null if none exists
     * @param  The bean type
     */
    @Nullable
    protected  String resolveDisabledBeanMessage(BeanResolutionContext resolutionContext, Argument beanType, Qualifier qualifier) {
        StringBuilder stringBuilder = new StringBuilder();
        resolveDisabledBeanMessage("", stringBuilder, CachedEnvironment.getProperty("line.separator"), resolutionContext, beanType, qualifier);
        return stringBuilder.isEmpty() ? null : stringBuilder.toString();
    }

    @Internal
    final  void resolveDisabledBeanMessage(String linePrefix,
                                              StringBuilder messageBuilder,
                                              String lineSeparator,
                                              @Nullable BeanResolutionContext resolutionContext,
                                              Argument beanType,
                                              @Nullable Qualifier qualifier) {
        if (linePrefix.length() == 10) {
            // Break possible cyclic dependencies
            return;
        }

        for (Map.Entry> entry : disabledConfigurations.entrySet()) {
            String pkg = entry.getKey();
            if (beanType.getTypeName().startsWith(pkg + ".")) {
                messageBuilder.append(lineSeparator)
                    .append(linePrefix)
                    .append("* [")
                    .append(beanType.getTypeString(true))
                    .append("] is disabled because it is within the package [")
                    .append(pkg)
                    .append("] which is disabled due to bean requirements: ")
                    .append(lineSeparator);
                for (String failure : entry.getValue()) {
                    messageBuilder
                        .append(linePrefix)
                        .append(" - ")
                        .append(failure)
                        .append(lineSeparator);
                }
                messageBuilder.setLength(messageBuilder.length() - lineSeparator.length());
                return;
            }
        }

        Collection> beanDefinitions = collectBeanCandidates(
            resolutionContext,
            beanType,
            false,
            null,
            disabledBeans.values()
        ).stream()
            .sorted(Comparator.comparing(BeanDefinition::getName))
            .toList();
        if (qualifier != null) {
            beanDefinitions = qualifier.filter(beanType.getType(), beanDefinitions);
        }

        if (!beanDefinitions.isEmpty()) {
            for (BeanDefinition beanDefinition : beanDefinitions) {
                messageBuilder
                    .append(lineSeparator)
                    .append(linePrefix)
                    .append("* [").append(beanDefinition.asArgument().getTypeString(true));
                if (!beanDefinition.getBeanType().equals(beanType.getType())) {
                    messageBuilder.append("] a candidate of [")
                        .append(beanType.getTypeString(true));
                }
                messageBuilder.append("] is disabled because:")
                    .append(lineSeparator);
                if (beanDefinition instanceof DisabledBean disabledBean) {
                    for (String failure : disabledBean.reasons()) {
                        messageBuilder
                            .append(linePrefix)
                            .append(" - ")
                            .append(failure)
                            .append(lineSeparator);
                        String prefix = "No bean of type [";
                        if (failure.startsWith(prefix)) {
                            ClassUtils.forName(failure.substring(prefix.length(), failure.indexOf("]")), classLoader)
                                .ifPresent(beanClass -> {
                                    messageBuilder.setLength(messageBuilder.length() - lineSeparator.length());
                                    resolveDisabledBeanMessage(linePrefix + " ",
                                            messageBuilder,
                                            lineSeparator,
                                            resolutionContext,
                                            Argument.of(beanClass),
                                            null);
                                    messageBuilder.append(lineSeparator);
                                });
                        }
                    }
                    messageBuilder.setLength(messageBuilder.length() - lineSeparator.length());
                }
            }
        }
    }

    @Nullable
    private  BeanRegistration provideInjectionPoint(BeanResolutionContext resolutionContext,
                                                          Argument beanType,
                                                          Qualifier qualifier,
                                                          boolean throwNoSuchBean) {
        final BeanResolutionContext.Path path = resolutionContext != null ? resolutionContext.getPath() : null;
        BeanResolutionContext.Segment injectionPointSegment = null;
        if (path != null) {
            final Iterator> i = path.iterator();
            if (i.hasNext()) {
                injectionPointSegment = i.next();
                BeanResolutionContext.Segment segment = null;
                if (i.hasNext()) {
                    segment = i.next();
                    if (segment.getDeclaringType().hasStereotype(INTRODUCTION_TYPE)) {
                        segment = i.hasNext() ? i.next() : null;
                    }
                }
                if (segment != null) {
                    T ip = (T) segment.getInjectionPoint();
                    if (ip != null && beanType.isInstance(ip)) {
                        return new BeanRegistration<>(BeanIdentifier.of(InjectionPoint.class.getName()), null, ip);
                    }
                }
            }
        }
        if (injectionPointSegment == null || !injectionPointSegment.getArgument().isNullable()) {
            throw new BeanContextException("Failed to obtain injection point. No valid injection path present in path: " + path);
        } else if (throwNoSuchBean) {
            throw newNoSuchBeanException(
                resolutionContext,
                beanType,
                qualifier,
                null
            );
        }
        return null;
    }

    /**
     * Resolve the {@link BeanRegistration} by a {@link BeanDefinition}.
     *
     * @param resolutionContext The resolution context
     * @param definition        The bean type
     * @param                The type
     * @return The bean registration or {@link NoSuchBeanException}
     */
    @NonNull
    private  BeanRegistration resolveBeanRegistration(@Nullable BeanResolutionContext resolutionContext,
                                                            @NonNull BeanDefinition definition) {
        return resolveBeanRegistration(resolutionContext, definition, definition.asArgument(), definition.getDeclaredQualifier());
    }

    /**
     * Resolve the {@link BeanRegistration} by a {@link BeanDefinition}.
     *
     * @param resolutionContext The resolution context
     * @param definition        The bean type
     * @param beanType          The bean type
     * @param qualifier         The qualifier
     * @param                The type
     * @return The bean registration
     */
    @NonNull
    private  BeanRegistration resolveBeanRegistration(@Nullable BeanResolutionContext resolutionContext,
                                                            @NonNull BeanDefinition definition,
                                                            @NonNull Argument beanType,
                                                            @Nullable Qualifier qualifier) {
        assertContextState();
        final boolean isScopedProxyDefinition = definition.hasStereotype(SCOPED_PROXY_ANN);

        if (qualifier != null && AnyQualifier.INSTANCE.equals(definition.getDeclaredQualifier())) {
            // Any factory bean should be stored as a new bean definition with a qualifier
            definition = BeanDefinitionDelegate.create(definition, qualifier);
        }

        if (definition.isSingleton() && !isScopedProxyDefinition) {
            return findOrCreateSingletonBeanRegistration(resolutionContext, definition, beanType, qualifier);
        }

        final boolean isProxy = definition.isProxy();

        if (isProxy && isScopedProxyDefinition) {
            // AOP proxy
            Qualifier q = qualifier;
            if (q == null) {
                q = definition.getDeclaredQualifier();
            }
            BeanRegistration registration = createRegistration(resolutionContext, beanType, q, definition, true);
            T bean = registration.bean;
            if (bean instanceof Qualified) {
                ((Qualified) bean).$withBeanQualifier(q);
            }
            return registration;
        }

        CustomScope customScope = findCustomScope(resolutionContext, definition, isProxy, isScopedProxyDefinition);
        if (customScope != null) {
            if (isProxy) {
                definition = getProxyTargetBeanDefinition(beanType, qualifier);
            }
            return getOrCreateScopedRegistration(resolutionContext, customScope, qualifier, beanType, definition);
        }
        // Unknown scope, prototype scope etc
        return createRegistration(resolutionContext, beanType, qualifier, definition, true);
    }

    @NonNull
    private  BeanRegistration findOrCreateSingletonBeanRegistration(@Nullable BeanResolutionContext resolutionContext,
                                                                          @NonNull BeanDefinition definition,
                                                                          @NonNull Argument beanType,
                                                                          @Nullable Qualifier qualifier) {
        BeanRegistration beanRegistration = singletonScope.findBeanRegistration(definition, beanType, qualifier);
        if (beanRegistration != null) {
            return beanRegistration;
        }
        return singletonScope.getOrCreate(this, resolutionContext, definition, beanType, qualifier);
    }

    @Nullable
    private  CustomScope findCustomScope(@Nullable BeanResolutionContext resolutionContext,
                                               @NonNull BeanDefinition definition,
                                               boolean isProxy,
                                               boolean isScopedProxyDefinition) {
        Optional> scope = definition.getScope();
        if (scope.isPresent()) {
            Class scopeAnnotation = scope.get();
            if (scopeAnnotation == Prototype.class) {
                return null;
            }
            CustomScope customScope = customScopeRegistry.findScope(scopeAnnotation).orElse(null);
            if (customScope != null) {
                return customScope;
            }
        } else {
            Optional scopeName = definition.getScopeName();
            if (scopeName.isPresent()) {
                String scopeAnnotation = scopeName.get();
                if (Prototype.class.getName().equals(scopeAnnotation)) {
                    return null;
                }
                CustomScope customScope = customScopeRegistry.findScope(scopeAnnotation).orElse(null);
                if (customScope != null) {
                    return customScope;
                }
            }
        }

        if (resolutionContext != null) {
            BeanResolutionContext.Segment currentSegment = resolutionContext
                    .getPath()
                    .currentSegment()
                    .orElse(null);
            if (currentSegment != null) {
                Argument argument = currentSegment.getArgument();
                CustomScope customScope = customScopeRegistry.findDeclaredScope(argument).orElse(null);
                if (customScope != null) {
                    return customScope;
                }
            }
        }

        if (!isScopedProxyDefinition || !isProxy) {
            return customScopeRegistry.findDeclaredScope(definition).orElse(null);
        }
        return null;
    }

    @NonNull
    private  BeanRegistration getOrCreateScopedRegistration(@Nullable BeanResolutionContext resolutionContext,
                                                                  @NonNull CustomScope registeredScope,
                                                                  @Nullable Qualifier qualifier,
                                                                  @NonNull Argument beanType,
                                                                  @NonNull BeanDefinition definition) {
        BeanKey beanKey = new BeanKey<>(definition.asArgument(), qualifier);
        T bean = registeredScope.getOrCreate(
                new BeanCreationContext() {
                    @NonNull
                    @Override
                    public BeanDefinition definition() {
                        return definition;
                    }

                    @NonNull
                    @Override
                    public BeanIdentifier id() {
                        return beanKey;
                    }

                    @NonNull
                    @Override
                    public CreatedBean create() throws BeanCreationException {
                        return createRegistration(resolutionContext == null ? null : resolutionContext.copy(), beanKey.beanType, qualifier, definition, true);
                    }
                }
        );
        return BeanRegistration.of(this, beanKey, definition, bean);
    }

    @NonNull
    @Internal
    final  BeanRegistration createRegistration(@Nullable BeanResolutionContext resolutionContext,
                                                     @NonNull Argument beanType,
                                                     @Nullable Qualifier qualifier,
                                                     @NonNull BeanDefinition definition,
                                                     boolean dependent) {
        try (BeanResolutionContext context = newResolutionContext(definition, resolutionContext)) {
            final BeanResolutionContext.Path path = context.getPath();
            final boolean isNewPath = path.isEmpty();
            if (isNewPath) {
                path.pushBeanCreate(definition, beanType);
            }
            try {
                List> parentDependentBeans = context.popDependentBeans();
                T bean = doCreateBean(context, definition, qualifier);
                BeanRegistration dependentFactoryBean = context.getAndResetDependentFactoryBean();
                if (dependentFactoryBean != null) {
                    destroyBean(dependentFactoryBean);
                }
                BeanKey beanKey = new BeanKey<>(beanType, qualifier);
                List> dependentBeans = context.getAndResetDependentBeans();
                BeanRegistration beanRegistration = BeanRegistration.of(this, beanKey, definition, bean, dependentBeans);
                context.pushDependentBeans(parentDependentBeans);
                if (dependent) {
                    context.addDependentBean(beanRegistration);
                }
                return beanRegistration;
            } finally {
                if (isNewPath) {
                    path.pop();
                }
            }
        }
    }

    /**
     * Find a concrete candidate for the given qualifier.
     *
     * @param beanType       The bean type
     * @param qualifier      The qualifier
     * @param throwNonUnique Whether to throw an exception if the bean is not found
     * @param             The bean generic type
     * @return The concrete bean definition candidate
     */
    @SuppressWarnings({"unchecked", "rawtypes", "java:S2789"}) // performance optimization
    private  Optional> findConcreteCandidate(@Nullable BeanResolutionContext resolutionContext,
                                                                  @NonNull Argument beanType,
                                                                  @Nullable Qualifier qualifier,
                                                                  boolean throwNonUnique) {
        if (beanType.getType() == Object.class && qualifier == null) {
            return Optional.empty();
        }
        BeanCandidateKey bk = new BeanCandidateKey(beanType, qualifier, throwNonUnique);
        Optional beanDefinition = beanConcreteCandidateCache.get(bk);
        if (beanDefinition == null) {
            beanDefinition = findConcreteCandidateNoCache(
                    resolutionContext,
                    beanType,
                    qualifier,
                    throwNonUnique);
            beanConcreteCandidateCache.put(bk, beanDefinition);
        }
        return beanDefinition;
    }

    private  Optional> findConcreteCandidateNoCache(@Nullable BeanResolutionContext resolutionContext,
                                                                         @NonNull Argument beanType,
                                                                         @Nullable Qualifier qualifier,
                                                                         boolean throwNonUnique) {

        Predicate> predicate = candidate -> !candidate.isAbstract();
        Collection> candidates = findBeanCandidates(resolutionContext, beanType, true, predicate);
        return pickOneBean(beanType, qualifier, throwNonUnique, candidates);
    }

    private  Optional> findProxyTargetNoCache(@Nullable BeanResolutionContext resolutionContext,
                                                                   @NonNull Argument beanType,
                                                                   @Nullable Qualifier qualifier) {

        Collection> candidates = collectBeanCandidates(
            resolutionContext,
            beanType,
            true,
            null,
            proxyTargetBeans
        );
        return pickOneBean(beanType, qualifier, false, candidates);
    }

    @NonNull
    private  Optional> pickOneBean(Argument beanType,
                                                        Qualifier qualifier,
                                                        boolean throwNonUnique,
                                                        Collection> candidates) {
        if (candidates.isEmpty()) {
            return Optional.empty();
        }
        if (qualifier != null) {
            if (LOG.isDebugEnabled()) {
                LOG.debug("Qualifying bean [{}] for qualifier: {} ", beanType.getName(), qualifier);
            }
            candidates = qualifier.filter(beanType.getType(), candidates);
            if (candidates.isEmpty()) {
                if (LOG.isDebugEnabled()) {
                    LOG.debug("No qualifying beans of type [{}] found for qualifier: {} ", beanType.getName(), qualifier);
                }
                return Optional.empty();
            }
        }
        BeanDefinition definition;
        if (candidates.size() == 1) {
            definition = candidates.iterator().next();
        } else {
            if (LOG.isDebugEnabled()) {
                LOG.debug("Searching for @Primary for type [{}] from candidates: {} ", beanType.getName(), candidates);
            }
            definition = lastChanceResolve(beanType, qualifier, throwNonUnique, candidates);
        }
        if (LOG.isDebugEnabled() && definition != null) {
            if (qualifier != null) {
                LOG.debug("Found concrete candidate [{}] for type: {} {} ", definition, qualifier, beanType.getName());
            } else {
                LOG.debug("Found concrete candidate [{}] for type: {} ", definition, beanType.getName());
            }
        }
        return Optional.ofNullable(definition);
    }

    @Nullable
    private  BeanDefinition lastChanceResolve(Argument beanType,
                                                    Qualifier qualifier,
                                                    boolean throwNonUnique,
                                                    Collection> candidates) {
        if (candidates.size() > 1) {
            List> primary = candidates.stream()
                .filter(BeanDefinition::isPrimary)
                .toList();
            if (!primary.isEmpty()) {
                candidates = primary;
            }
        }
        if (candidates.size() == 1) {
            return candidates.iterator().next();
        }
        candidates = candidates.stream().filter(candidate -> !candidate.hasDeclaredStereotype(Secondary.class)).toList();
        if (candidates.size() == 1) {
            return candidates.iterator().next();
        }
        // pick the bean with the highest priority
        ArrayList> listCandidates = new ArrayList<>(candidates);
        listCandidates.sort(OrderUtil.ORDERED_COMPARATOR);
        Iterator> iterator = listCandidates.iterator();
        final BeanDefinition bean = iterator.next();
        final BeanDefinition next = iterator.next();
        // We should have at least two beans - no need for next checks
        // Check there are not 2 beans with the same order
        if (bean.getOrder() != next.getOrder()) {
            LOG.debug("Picked bean {} with the highest precedence for type {} and qualifier {}", bean, beanType, null);
            return bean;
        }

        // try resolve @DefaultImplementation
        BeanDefinition first = candidates.iterator().next();
        if (first.hasStereotype(DefaultImplementation.class)) {
            String n = first.stringValue(DefaultImplementation.class, "name").orElse(null);
            if (n != null) {
                for (BeanDefinition bd : candidates) {
                    if (bd.getBeanType().getName().equals(n)) {
                        return bd;
                    }
                }
            }
        }
        Collection> exactMatches = filterExactMatch(beanType.getType(), candidates);
        if (exactMatches.size() == 1) {
            return exactMatches.iterator().next();
        }
        if (throwNonUnique) {
            return findConcreteCandidate(beanType.getType(), qualifier, candidates);
        }
        return null;
    }

    private void readAllBeanConfigurations() {
        if (beanConfigurations.isEmpty()) {
            Iterable beanConfigurations = resolveBeanConfigurations();
            for (BeanConfiguration beanConfiguration : beanConfigurations) {
                registerConfiguration(beanConfiguration);
            }
        }
    }

    private  Collection> filterExactMatch(final Class beanType, Collection> candidates) {
        List> list = new ArrayList<>(candidates.size());
        for (BeanDefinition candidate : candidates) {
            if (candidate.getBeanType() == beanType) {
                list.add(candidate);
            }
        }
        return list;
    }

    private void configureAndStartContext() {
        configureContextInternal();
        initializeEventListeners();
        initializeContext(
            startupBeans.eagerInitBeans,
            startupBeans.processedBeans,
            startupBeans.parallelBeans
        );
    }

    @NonNull
    private StartupBeans readBeanDefinitionReferences() {
        if (startupBeans == null) {

            List eagerInitBeans = new ArrayList<>(20);
            List processedBeans = new ArrayList<>(10);
            List parallelBeans = new ArrayList<>(10);

            List beanDefinitionReferences = resolveBeanDefinitionReferences();

            List producers = new ArrayList<>(beanDefinitionReferences.size());
            List proxyTargetBeans = new ArrayList<>(beanDefinitionReferences.size());
            for (BeanDefinitionReference beanDefinitionReference : beanDefinitionReferences) {
                producers.add(new BeanDefinitionProducer(beanDefinitionReference));
            }
            beanDefinitionsClasses.addAll(producers);

            Collection allConfigurations = beanConfigurations.values();
            List configurationsDisabled = new ArrayList<>(allConfigurations.size());
            for (BeanConfiguration bc : allConfigurations) {
                if (!bc.isEnabled(this)) {
                    configurationsDisabled.add(bc);
                }
            }

            reference:
            for (BeanDefinitionProducer beanDefinitionProducer : producers) {
                if (beanDefinitionProducer.isDisabled()) {
                    continue;
                }
                BeanDefinitionReference beanDefinitionReference = beanDefinitionProducer.reference;
                for (BeanConfiguration disableConfiguration : configurationsDisabled) {
                    if (disableConfiguration.isWithin(beanDefinitionReference)) {
                        beanDefinitionProducer.referenceEnabled = false;
                        continue reference;
                    }
                }

                if (beanDefinitionReference.isProxiedBean()) {
                    beanDefinitionProducer.referenceEnabled = false;
                    BeanDefinitionProducer proxyBeanProducer = new BeanDefinitionProducer(beanDefinitionReference);
                    // retain only if proxy target otherwise the target is never used
                    if (beanDefinitionReference.isProxyTarget()) {
                        proxyTargetBeans.add(proxyBeanProducer);
                    }
                    continue;
                }

                final AnnotationMetadata annotationMetadata = beanDefinitionReference.getAnnotationMetadata();
                Class[] indexes = annotationMetadata.classValues(INDEXES_TYPE);
                if (indexes.length > 0) {
                    //noinspection ForLoopReplaceableByForEach
                    for (int i = 0; i < indexes.length; i++) {
                        Class indexedType = indexes[i];
                        resolveTypeIndex(indexedType).add(beanDefinitionProducer);
                    }
                } else {
                    if (annotationMetadata.hasStereotype(ADAPTER_TYPE)) {
                        final Class aClass = annotationMetadata.classValue(ADAPTER_TYPE, AnnotationMetadata.VALUE_MEMBER).orElse(null);
                        if (indexedTypes.contains(aClass)) {
                            resolveTypeIndex(aClass).add(beanDefinitionProducer);
                        }
                    }
                }
                if (isEagerInit(beanDefinitionReference)) {
                    eagerInitBeans.add(beanDefinitionProducer);
                } else if (annotationMetadata.hasDeclaredStereotype(PARALLEL_TYPE)) {
                    parallelBeans.add(beanDefinitionProducer);
                }

                if (beanDefinitionReference.requiresMethodProcessing()) {
                    processedBeans.add(beanDefinitionProducer);
                }

            }

            this.beanDefinitionReferences = null;
            this.beanConfigurationsList = null;

            this.proxyTargetBeans.addAll(proxyTargetBeans);
            startupBeans = new StartupBeans(eagerInitBeans, processedBeans, parallelBeans);
        }
        return startupBeans;
    }

    private boolean isEagerInit(BeanDefinitionReference beanDefinitionReference) {
        return beanDefinitionReference.isContextScope() ||
                (eagerInitSingletons && beanDefinitionReference.isSingleton()) ||
                (eagerInitStereotypesPresent && beanDefinitionReference.getAnnotationMetadata().hasDeclaredStereotype(eagerInitStereotypes));
    }

    @NonNull
    private Collection resolveTypeIndex(Class indexedType) {
        return beanIndex.computeIfAbsent(indexedType, aClass -> {
            indexedTypes.add(indexedType);
            return new ArrayList<>(20);
        });
    }

    @SuppressWarnings("unchecked")
    private  Collection> findBeanCandidatesInternal(BeanResolutionContext resolutionContext, Argument beanType) {
        @SuppressWarnings("rawtypes")
        Collection beanDefinitions = beanCandidateCache.get(beanType);
        if (beanDefinitions == null) {
            beanDefinitions = findBeanCandidates(resolutionContext, beanType, true, null);
            beanCandidateCache.put(beanType, beanDefinitions);
        }
        return beanDefinitions;
    }

    /**
     * Obtains the bean registration for the given type and qualifier.
     *
     * @param resolutionContext The resolution context
     * @param beanType          The bean type
     * @param qualifier         The qualifier
     * @param                The generic type
     * @return A {@link BeanRegistration}
     */
    @Internal
    public  BeanRegistration getBeanRegistration(@Nullable BeanResolutionContext resolutionContext,
                                                       @NonNull Argument beanType,
                                                       @Nullable Qualifier qualifier) {
        return resolveBeanRegistration(resolutionContext, beanType, qualifier, true);
    }

    /**
     * Obtains the bean registrations for the given type and qualifier.
     *
     * @param resolutionContext The resolution context
     * @param beanType          The bean type
     * @param qualifier         The qualifier
     * @param                The generic type
     * @return A collection of {@link BeanRegistration}
     */
    @SuppressWarnings("unchecked")
    @Internal
    public  Collection> getBeanRegistrations(@Nullable BeanResolutionContext resolutionContext,
                                                                    @NonNull Argument beanType,
                                                                    @Nullable Qualifier qualifier) {
        assertContextState();
        boolean hasQualifier = qualifier != null;
        if (LOG.isDebugEnabled()) {
            if (hasQualifier) {
                LOG.debug("Resolving beans for type: {} {} ", qualifier, beanType.getTypeName());
            } else {
                LOG.debug("Resolving beans for type: {}", beanType.getTypeName());
            }
        }

        BeanKey key = new BeanKey<>(beanType, qualifier);
        if (LOG.isTraceEnabled()) {
            LOG.trace("Looking up existing beans for key: {}", key);
        }
        CollectionHolder existing = singletonBeanRegistrations.get(key);
        if (existing != null && existing.registrations != null) {
            logResolvedExistingBeanRegistrations(beanType, qualifier, existing.registrations);
            return existing.registrations;
        }

        Collection> beanDefinitions = findBeanCandidatesInternal(resolutionContext, beanType);
        if (!beanDefinitions.isEmpty()) {
            beanDefinitions = applyBeanResolutionFilters(resolutionContext, beanDefinitions);
            if (qualifier != null) {
                beanDefinitions = qualifier.filter(beanType.getType(), beanDefinitions);
            }
        }

        Collection> beanRegistrations;
        if (beanDefinitions.isEmpty()) {
            beanRegistrations = Collections.emptySet();
        } else {
            boolean allCandidatesAreSingleton = true;
            for (BeanDefinition definition : beanDefinitions) {
                if (!definition.isSingleton()) {
                    allCandidatesAreSingleton = false;
                }
            }
            if (allCandidatesAreSingleton) {
                CollectionHolder holder = singletonBeanRegistrations.computeIfAbsent(key, beanKey -> new CollectionHolder());
                synchronized (holder) {
                    if (holder.registrations != null) {
                        logResolvedExistingBeanRegistrations(beanType, qualifier, holder.registrations);
                        return holder.registrations;
                    }
                    holder.registrations = resolveBeanRegistrations(resolutionContext, beanDefinitions, beanType, qualifier);
                    return holder.registrations;
                }
            } else {
                beanRegistrations = resolveBeanRegistrations(resolutionContext, beanDefinitions, beanType, qualifier);
            }
        }
        if (LOG.isDebugEnabled() && !beanRegistrations.isEmpty()) {
            if (hasQualifier) {
                LOG.debug("Found {} bean registrations for type [{} {}]", beanRegistrations.size(), qualifier, beanType.getName());
            } else {
                LOG.debug("Found {} bean registrations for type [{}]", beanRegistrations.size(), beanType.getName());
            }
            for (BeanRegistration beanRegistration : beanRegistrations) {
                LOG.debug("  {} {}", beanRegistration.definition(), beanRegistration.definition().getDeclaredQualifier());
            }
        }
        return beanRegistrations;
    }

    private  Collection> resolveBeanRegistrations(BeanResolutionContext resolutionContext,
                                                                         Collection> beanDefinitions,
                                                                         Argument beanType,
                                                                         Qualifier qualifier) {
        List> beansOfTypeList = new ArrayList<>(beanDefinitions.size());
        for (BeanDefinition definition : beanDefinitions) {
            addCandidateToList(resolutionContext, definition, beanType, qualifier, beansOfTypeList);
        }
        beansOfTypeList.sort(OrderUtil.ORDERED_COMPARATOR);
        return beansOfTypeList;
    }

    private  void logResolvedExistingBeanRegistrations(Argument beanType, Qualifier qualifier, Collection> existing) {
        if (LOG.isDebugEnabled()) {
            if (qualifier == null) {
                LOG.debug("Found {} existing beans for type [{}]: {} ", existing.size(), beanType.getName(), existing);
            } else {
                LOG.debug("Found {} existing beans for type [{} {}]: {} ", existing.size(), qualifier, beanType.getName(), existing);
            }
        }
    }

    private  Collection> applyBeanResolutionFilters(@Nullable BeanResolutionContext resolutionContext, Collection> candidates) {
        BeanResolutionContext.Segment segment = resolutionContext != null ? resolutionContext.getPath().peek() : null;
        BeanDefinition declaringBean = null;
        Class proxyTargetDefinitionType = null;
        if (segment instanceof AbstractBeanResolutionContext.ConstructorSegment || segment instanceof AbstractBeanResolutionContext.MethodSegment) {
            declaringBean = segment.getDeclaringType();
            // if the currently injected segment is a constructor argument and the type to be constructed is the
            // same as the candidate, then filter out the candidate to avoid a circular injection problem
            if (declaringBean instanceof ProxyBeanDefinition proxyBeanDefinition) {
                proxyTargetDefinitionType = proxyBeanDefinition.getTargetDefinitionType();
            }
        }
        candidates = new LinkedHashSet<>(candidates); // Make mutable
        for (Iterator> iterator = candidates.iterator(); iterator.hasNext(); ) {
            BeanDefinition c = iterator.next();
            if (c.isAbstract() || declaringBean != null && c.equals(declaringBean) || proxyTargetDefinitionType != null && proxyTargetDefinitionType.equals(c.getClass())) {
                iterator.remove();
            }
        }
        return candidates;
    }

    private  void addCandidateToList(@Nullable BeanResolutionContext resolutionContext,
                                        @NonNull BeanDefinition candidate,
                                        @NonNull Argument beanType,
                                        @Nullable Qualifier qualifier,
                                        @NonNull Collection> beansOfTypeList) {
        BeanRegistration beanRegistration = null;
        try {
            beanRegistration = resolveBeanRegistration(resolutionContext, candidate);
            if (LOG.isDebugEnabled()) {
                LOG.debug("Found a registration {} for candidate: {} with qualifier: {}", beanRegistration, candidate, qualifier);
            }
        } catch (DisabledBeanException e) {
            if (AbstractBeanContextConditional.ConditionLog.LOG.isDebugEnabled()) {
                AbstractBeanContextConditional.ConditionLog.LOG.debug("Bean of type [{}] disabled for reason: {}", beanType.getTypeName(), e.getMessage());
            }
        }

        if (beanRegistration != null) {
            if (candidate.isContainerType()) {
                Object container = beanRegistration.bean;
                if (container instanceof Object[] array) {
                    container = Arrays.asList(array);
                }
                if (container instanceof Iterable iterable) {
                    int i = 0;
                    for (Object o : iterable) {
                        if (o == null || !beanType.isInstance(o)) {
                            continue;
                        }
                        beansOfTypeList.add(BeanRegistration.of(
                            this,
                            new BeanKey<>(beanType, Qualifiers.byQualifiers(Qualifiers.byName(String.valueOf(i++)), qualifier)),
                            candidate,
                            (T) o
                        ));
                    }
                }
            } else {
                beansOfTypeList.add(beanRegistration);
            }
        }
    }

    private  boolean isCandidatePresent(Argument beanType, Qualifier qualifier) {
        final Collection> candidates = findBeanCandidates(null, beanType, true, null);
        if (!candidates.isEmpty()) {
            filterReplacedBeans(null, candidates);
            if (qualifier != null) {
                return qualifier.doesQualify(beanType.getType(), candidates);
            }
            return true;
        }
        return false;
    }

    private static  List nullSafe(List list) {
        if (list == null) {
            return Collections.emptyList();
        }
        return list;
    }

    private List topologicalSort(Collection beans) {
        Map> initial = beans.stream()
                .sorted(Comparator.comparing(s -> s.getBeanDefinition().getRequiredComponents().size()))
                .collect(Collectors.groupingBy(b -> b.getBeanDefinition().getRequiredComponents().isEmpty()));
        List sorted = new ArrayList<>(nullSafe(initial.get(true)));
        List unsorted = new ArrayList<>(nullSafe(initial.get(false)));
        // Optimization which knows about types which are already in the sorted list
        Set> satisfied = new HashSet<>();

        // Optimization for types which we know are already unsatisified
        // in a single iteration, allowing to skip the loop on unsorted elements
        Set> unsatisfied = new HashSet<>();

        //loop until all items have been sorted
        while (!unsorted.isEmpty()) {
            boolean acyclic = false;

            unsatisfied.clear();
            Iterator i = unsorted.iterator();
            while (i.hasNext()) {
                BeanRegistration bean = i.next();
                boolean found = false;

                //determine if any components are in the unsorted list
                Collection> components = bean.getBeanDefinition().getRequiredComponents();
                for (Class clazz : components) {
                    if (satisfied.contains(clazz)) {
                        continue;
                    }
                    if (unsatisfied.contains(clazz) || unsorted.stream()
                            .map(BeanRegistration::getBeanDefinition)
                            .map(BeanDefinition::getBeanType)
                            .anyMatch(clazz::isAssignableFrom)) {
                        found = true;
                        unsatisfied.add(clazz);
                        break;
                    }
                    satisfied.add(clazz);
                }

                //none of the required components are in the unsorted list,
                //so it can be added to the sorted list
                if (!found) {
                    acyclic = true;
                    i.remove();
                    sorted.add(0, bean);
                }
            }

            //rather than throw an exception here because there is a cyclical dependency
            //just add the first item to the list and keep trying. It may be possible to
            //see a cycle here because qualifiers are not taken into account.
            if (!acyclic) {
                sorted.add(0, unsorted.remove(0));
            }
        }

        return sorted;
    }

    @NonNull
    @Override
    public MutableConvertibleValues getAttributes() {
        return MutableConvertibleValues.of(attributes);
    }

    @NonNull
    @Override
    public Optional getAttribute(CharSequence name) {
        if (name != null) {
            return Optional.ofNullable(attributes.get(name));
        } else {
            return Optional.empty();
        }
    }

    @NonNull
    @Override
    public  Optional getAttribute(CharSequence name, Class type) {
        if (name != null) {
            final Object o = attributes.get(name);
            if (type.isInstance(o)) {
                return Optional.of((T) o);
            } else if (o != null) {
                return getConversionService().convert(o, type);
            }
        }
        return Optional.empty();
    }

    @NonNull
    @Override
    public BeanContext setAttribute(@NonNull CharSequence name, @Nullable Object value) {
        if (name != null) {
            if (value != null) {
                attributes.put(name, value);
            } else {
                attributes.remove(name);
            }
        }
        return this;
    }

    @NonNull
    @Override
    public  Optional removeAttribute(@NonNull CharSequence name, @NonNull Class type) {
        final Object o = attributes.remove(name);
        if (type.isInstance(o)) {
            return Optional.of((T) o);
        }
        return Optional.empty();
    }

    @Override
    public void finalizeConfiguration() {
        configureAndStartContext();
    }

    @Override
    public MutableConversionService getConversionService() {
        return conversionService;
    }

    @Override
    public final synchronized void configure() {
        if (this.running.get()) {
            configurationFailure("already running");
        }
        if (this.terminating.get()) {
            configurationFailure("currently terminating");
        }
        if (this.initializing.get()) {
            configurationFailure("currently initializing");
        }
        configureContextInternal();
    }

    /**
     * Configures the context reading all bean definitions.
     */
    @Internal
    void configureContextInternal() {
        if (configured.compareAndSet(false, true)) {
            readAllBeanConfigurations();
            readBeanDefinitionReferences();
        }
    }

    private static void configurationFailure(String message) {
        throw new ConfigurationException("Bean context is " + message + ". The configure() method can only be called prior to startup");
    }

    /**
     * @param  The type
     * @param  The return type
     */
    private abstract static sealed class AbstractExecutionHandle implements MethodExecutionHandle {
        protected final ExecutableMethod method;

        /**
         * @param method The method
         */
        AbstractExecutionHandle(ExecutableMethod method) {
            this.method = method;
        }

        @NonNull
        @Override
        public ExecutableMethod getExecutableMethod() {
            return method;
        }

        @Override
        public Argument[] getArguments() {
            return method.getArguments();
        }

        @Override
        public String toString() {
            return method.toString();
        }

        @Override
        public String getMethodName() {
            return this.method.getMethodName();
        }

        @Override
        public ReturnType getReturnType() {
            return method.getReturnType();
        }

        @Override
        public AnnotationMetadata getAnnotationMetadata() {
            return method.getAnnotationMetadata();
        }
    }

    /**
     * @param  The targe type
     * @param  The return type
     */
    private static final class ObjectExecutionHandle extends AbstractExecutionHandle implements UnsafeExecutionHandle {

        @Nullable
        private final UnsafeExecutable unsafeExecutable;
        private final T target;

        /**
         * @param target The target type
         * @param method The method
         */
        ObjectExecutionHandle(T target, ExecutableMethod method) {
            super(method);
            this.target = target;
            if (method instanceof UnsafeExecutable unsafeExecutable) {
                this.unsafeExecutable = unsafeExecutable;
            } else {
                this.unsafeExecutable = null;
            }
        }

        @Override
        public T getTarget() {
            return target;
        }

        @Override
        public R invoke(Object... arguments) {
            return method.invoke(target, arguments);
        }

        @Override
        public R invokeUnsafe(Object... arguments) {
            if (unsafeExecutable == null) {
                return invoke(arguments);
            }
            return unsafeExecutable.invokeUnsafe(target, arguments);
        }

        @Override
        public Method getTargetMethod() {
            return method.getTargetMethod();
        }

        @Override
        public Class getDeclaringType() {
            return target.getClass();
        }

    }

    /**
     * @param 
     * @param 
     */
    private static final class BeanExecutionHandle extends AbstractExecutionHandle {
        private final BeanContext beanContext;
        private final Class beanType;
        private final Qualifier qualifier;
        private final boolean isSingleton;

        private T target;

        /**
         * @param beanContext The bean context
         * @param beanType    The bean type
         * @param qualifier   The qualifier
         * @param method      The method
         */
        BeanExecutionHandle(BeanContext beanContext, Class beanType, Qualifier qualifier, ExecutableMethod method) {
            super(method);
            this.beanContext = beanContext;
            this.beanType = beanType;
            this.qualifier = qualifier;
            this.isSingleton = beanContext.findBeanDefinition(beanType, qualifier).map(BeanDefinition::isSingleton).orElse(false);
        }

        @Override
        public T getTarget() {
            T target = this.target;
            if (target == null) {
                synchronized (this) { // double check
                    target = this.target;
                    if (target == null) {
                        target = beanContext.getBean(beanType, qualifier);
                        this.target = target;
                    }
                }
            }
            return target;
        }

        @Override
        public Method getTargetMethod() {
            return method.getTargetMethod();
        }

        @Override
        public Class getDeclaringType() {
            return beanType;
        }

        @Override
        public R invoke(Object... arguments) {
            if (isSingleton) {
                T target = getTarget();

                return method.invoke(target, arguments);
            } else {
                return method.invoke(beanContext.getBean(beanType, qualifier), arguments);
            }
        }
    }

    /**
     * Internal supplier of listeners.
     *
     * @param  The listener type
     *
     * @author Denis Stepanov
     * @since 4.0.0
     */
    @Internal
    sealed interface ListenersSupplier {

        /**
         * Retrieved the listeners lazily.
         *
         * @param beanResolutionContext The bean resolution context
         * @return the collection of listeners
         */
        @NonNull
        Iterable get(@Nullable BeanResolutionContext beanResolutionContext);

    }

    /**
     * Class used as a bean key.
     *
     * @param  The bean type
     */
    @SuppressWarnings("java:S1948")
    static final class BeanKey implements BeanIdentifier {
        final Argument beanType;
        private final Qualifier qualifier;
        private final int hashCode;

        /**
         * A bean key for the given bean definition.
         *
         * @param definition The definition
         * @param qualifier  The qualifier
         */
        BeanKey(BeanDefinition definition, Qualifier qualifier) {
            this(definition.asArgument(), qualifier);
        }

        /**
         * A bean key for the given bean definition.
         *
         * @param argument  The argument
         * @param qualifier The qualifier
         */
        BeanKey(Argument argument, Qualifier qualifier) {
            this.beanType = argument;
            this.qualifier = qualifier;
            this.hashCode = argument.typeHashCode();
        }

        /**
         * @param beanType      The bean type
         * @param qualifier     The qualifier
         * @param typeArguments The type arguments
         */
        BeanKey(Class beanType, Qualifier qualifier, @Nullable Class... typeArguments) {
            this(Argument.of(beanType, typeArguments), qualifier);
        }

        @Override
        public int length() {
            return toString().length();
        }

        @Override
        public char charAt(int index) {
            return toString().charAt(index);
        }

        @Override
        public CharSequence subSequence(int start, int end) {
            return toString().subSequence(start, end);
        }

        @Override
        public String toString() {
            return (qualifier != null ? qualifier + " " : "") + beanType.getName();
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) {
                return true;
            }
            if (o == null || getClass() != o.getClass()) {
                return false;
            }
            BeanKey beanKey = (BeanKey) o;
            return beanType.equalsType(beanKey.beanType) &&
                    Objects.equals(qualifier, beanKey.qualifier);
        }

        @Override
        public int hashCode() {
            return hashCode;
        }

        @Override
        public String getName() {
            if (qualifier instanceof Named named) {
                return named.getName();
            }
            return Primary.SIMPLE_NAME;
        }
    }

    /**
     * Class used as a bean candidate key.
     *
     * @param  The bean candidate type
     */
    static final class BeanCandidateKey {
        private final Argument beanType;
        private final Qualifier qualifier;
        private final boolean throwNonUnique;
        private final int hashCode;

        /**
         * A bean key for the given bean definition.
         *
         * @param argument       The argument
         * @param qualifier      The qualifier
         * @param throwNonUnique The throwNonUnique
         */
        BeanCandidateKey(Argument argument, Qualifier qualifier, boolean throwNonUnique) {
            this.beanType = argument;
            this.qualifier = qualifier;
            this.hashCode = argument.typeHashCode();
            this.throwNonUnique = throwNonUnique;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) {
                return true;
            }
            if (o == null || getClass() != o.getClass()) {
                return false;
            }
            BeanCandidateKey beanKey = (BeanCandidateKey) o;
            return beanType.equalsType(beanKey.beanType) &&
                    Objects.equals(qualifier, beanKey.qualifier) && throwNonUnique == beanKey.throwNonUnique;
        }

        @Override
        public int hashCode() {
            return hashCode;
        }

    }

    private final class EventListenerListenersSupplier implements ListenersSupplier {

        private final List> listenersDefinitions;
        // The supplier can be triggered concurrently.
        // We allow for the listeners collection to be initialized multiple times.
        @SuppressWarnings("java:S3077")
        private volatile List listeners;

        public EventListenerListenersSupplier(List> listenersDefinitions) {
            this.listenersDefinitions = listenersDefinitions;
        }

        @Override
        public Iterable get(BeanResolutionContext beanResolutionContext) {
            if (listeners == null) {
                List listeners = new ArrayList<>(listenersDefinitions.size());
                for (BeanDefinition listenersDefinition : listenersDefinitions) {
                    T listener;
                    if (beanResolutionContext == null) {
                        try (BeanResolutionContext context = newResolutionContext(listenersDefinition, null)) {
                            listener = resolveBeanRegistration(context, listenersDefinition).bean;
                        }
                    } else {
                        listener = resolveBeanRegistration(beanResolutionContext, listenersDefinition).bean;
                    }
                    listeners.add(listener);
                }
                OrderUtil.sort(listeners);
                this.listeners = listeners;
            }
            return listeners;
        }
    }

    private static final class AnnotationProcessorListenersSupplier implements ListenersSupplier {

        @Override
        public Iterable get(BeanResolutionContext beanResolutionContext) {
            return Collections.singletonList(new AnnotationProcessorListener());
        }

    }

    private final class SingletonBeanResolutionContext extends AbstractBeanResolutionContext {

        public SingletonBeanResolutionContext(BeanDefinition beanDefinition) {
            super(DefaultBeanContext.this, beanDefinition);
        }

        @Override
        public BeanResolutionContext copy() {
            SingletonBeanResolutionContext copy = new SingletonBeanResolutionContext(rootDefinition);
            copy.copyStateFrom(this);
            return copy;
        }

        @Override
        public  void addInFlightBean(BeanIdentifier beanIdentifier, BeanRegistration beanRegistration) {
            singlesInCreation.put(beanIdentifier, beanRegistration);
        }

        @Override
        public void removeInFlightBean(BeanIdentifier beanIdentifier) {
            singlesInCreation.remove(beanIdentifier);
        }

        @Nullable
        @Override
        public  BeanRegistration getInFlightBean(BeanIdentifier beanIdentifier) {
            return (BeanRegistration) singlesInCreation.get(beanIdentifier);
        }
    }

    private static final class CollectionHolder {
        Collection> registrations;
    }

    /**
     * Holds references to the startup beans.
     * @param eagerInitBeans Eager init beans
     * @param processedBeans Processed beans
     * @param parallelBeans Parallel startup beans
     */
    private record StartupBeans(
        List eagerInitBeans,
        List processedBeans,
        List parallelBeans) {
    }


    /**
     * The class adds the caching of the enabled decision + the definition instance.
     * NOTE: The class can be accessed in multiple threads, we do allow for the fields to be possibly initialized concurrently - multiple times.
     *
     * @since 4.0.0
     */
    @Internal
    static final class BeanDefinitionProducer {

        @Nullable
        @SuppressWarnings("java:S3077")
        private volatile BeanDefinitionReference reference;
        @Nullable
        @SuppressWarnings("java:S3077")
        private volatile BeanDefinition definition;
        @Nullable
        private volatile Boolean referenceEnabled;
        @Nullable
        private volatile Boolean definitionEnabled;

        BeanDefinitionProducer(@NonNull BeanDefinitionReference reference) {
            this.reference = reference;
            if (reference instanceof AbstractInitializableBeanDefinitionAndReference) {
                referenceEnabled  = true; // Postpone validation check
            }
        }

        public boolean isReferenceEnabled(DefaultBeanContext context) {
            return isReferenceEnabled(context, null);
        }

        public boolean isReferenceEnabled(DefaultBeanContext context, @Nullable BeanResolutionContext resolutionContext) {
            BeanDefinitionReference ref = reference;
            // The reference needs to be assigned to a new variable as it can change between checks
            if (ref == null) {
                return false;
            }
            if (referenceEnabled == null) {
                if (ref.isEnabled(context, resolutionContext)) {
                    referenceEnabled = true;
                } else {
                    referenceEnabled = false;
                    reference = null;
                }
            }
            return referenceEnabled;
        }

        public boolean isDisabled() {
            if (reference == null) {
                return true;
            }
            Boolean refEnabled = referenceEnabled;
            // The reference needs to be assigned to a new variable as it can change between checks
            if (refEnabled != null && !refEnabled) {
                return true;
            }
            Boolean defEnabled = definitionEnabled;
            // The reference needs to be assigned to a new variable as it can change between checks
            return defEnabled != null && !defEnabled;
        }

        public boolean isDefinitionEnabled(DefaultBeanContext defaultBeanContext) {
            return isDefinitionEnabled(defaultBeanContext, null);
        }

        public boolean isDefinitionEnabled(DefaultBeanContext context, @Nullable BeanResolutionContext resolutionContext) {
            if (definitionEnabled == null) {
                if (isReferenceEnabled(context, resolutionContext)) {
                    BeanDefinition  def = getDefinition(context);
                    if (def.isEnabled(context, resolutionContext)) {
                        definition = def;
                        definitionEnabled = true;
                    } else {
                        definitionEnabled = false;
                    }
                } else {
                    definitionEnabled = false;
                }
            }
            return definitionEnabled;
        }

        public  BeanDefinitionReference getReference() {
            // The reference needs to be assigned to a new variable as it can change between checks
            Boolean refEnabled = referenceEnabled;
            if (reference == null || refEnabled == null || !refEnabled) {
                throw new IllegalStateException("The reference is not enabled");
            }
            return reference;
        }

        public  BeanDefinition getDefinition(BeanContext beanContext) {
            // The reference needs to be assigned to a new variable as it can change between checks
            Boolean defEnabled = definitionEnabled;
            if (defEnabled != null && !defEnabled) {
                throw new IllegalStateException("The definition is not enabled");
            }
            try {
                BeanDefinition def = definition;
                if (def == null) {
                    def = getReference().load(beanContext);
                    definition = def;
                }
                return def;
            } catch (Throwable e) {
                throw new BeanInstantiationException(MSG_BEAN_DEFINITION + reference.getName() + MSG_COULD_NOT_BE_LOADED + e.getMessage(), e);
            }
        }

        public  boolean isReferenceCandidateBean(Argument beanType) {
            // The reference needs to be assigned to a new variable as it can change between checks
            BeanDefinitionReference ref = reference;
            return ref != null && ref.isCandidateBean(beanType);
        }

        public void disable(BeanDefinitionReference reference) {
            // The reference needs to be assigned to a new variable as it can change between checks
            BeanDefinitionReference ref = this.reference;
            if (ref != null && ref.equals(reference)) {
                this.reference = null;
            }
        }
    }

    private final class BeanContextUnsafeExecutionHandle extends BeanContextExecutionHandle implements UnsafeExecutionHandle {

        private final UnsafeExecutable unsafeExecutionHandle;

        public BeanContextUnsafeExecutionHandle(ExecutableMethod method, BeanDefinition beanDefinition, UnsafeExecutable unsafeExecutionHandle) {
            super(method, beanDefinition);
            this.unsafeExecutionHandle = unsafeExecutionHandle;
        }

        @Override
        public Object invokeUnsafe(Object... arguments) {
            return unsafeExecutionHandle.invokeUnsafe(getTarget(), arguments);
        }

        @Override
        public String toString() {
            return unsafeExecutionHandle.toString();
        }
    }

    private sealed class BeanContextExecutionHandle implements MethodExecutionHandle {

        private final ExecutableMethod method;
        private final BeanDefinition beanDefinition;
        private Object target;

        public BeanContextExecutionHandle(ExecutableMethod method, BeanDefinition beanDefinition) {
            this.method = method;
            this.beanDefinition = beanDefinition;
        }

        @NonNull
        @Override
        public AnnotationMetadata getAnnotationMetadata() {
            return method.getAnnotationMetadata();
        }

        @Override
        public Object getTarget() {
            Object target = this.target;
            if (target == null) {
                synchronized (this) { // double check
                    target = this.target;
                    if (target == null) {
                        target = getBean(beanDefinition);
                        this.target = target;
                    }
                }
            }
            return target;
        }

        @Override
        public Class getDeclaringType() {
            return beanDefinition.getBeanType();
        }

        @Override
        public String getMethodName() {
            return method.getMethodName();
        }

        @Override
        public Argument[] getArguments() {
            return method.getArguments();
        }

        @Override
        public Method getTargetMethod() {
            return method.getTargetMethod();
        }

        @Override
        public ReturnType getReturnType() {
            return method.getReturnType();
        }

        @Override
        public Object invoke(Object... arguments) {
            return method.invoke(getTarget(), arguments);
        }

        @NonNull
        @Override
        public ExecutableMethod getExecutableMethod() {
            return (ExecutableMethod) method;
        }

        @Override
        public String toString() {
            return method.toString();
        }
    }

}