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

io.micronaut.context.SingletonScope Maven / Gradle / Ivy

/*
 * Copyright 2017-2020 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.core.annotation.Internal;
import io.micronaut.core.annotation.NonNull;
import io.micronaut.core.annotation.Nullable;
import io.micronaut.core.type.Argument;
import io.micronaut.core.util.ArgumentUtils;
import io.micronaut.core.util.ObjectUtils;
import io.micronaut.inject.BeanDefinition;
import io.micronaut.inject.BeanIdentifier;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Stream;

/**
 * The singleton scope implementation.
 *
 * @author Denis Stepanov
 * @since 3.5.0
 */
@Internal
final class SingletonScope {

    /**
     * The locks used to prevent re-creating of the same singleton.
     */
    private final Map singletonsInCreationLocks = new ConcurrentHashMap<>(5, 1);

    /**
     * The main collection storing registrations for {@link BeanDefinition}.
     */
    private final Map singletonByBeanDefinition = new ConcurrentHashMap<>(100);

    /**
     * Index collection to retrieve a registration by {@link Argument} and {@link Qualifier}.
     */
    private final Map singletonByArgumentAndQualifier = new ConcurrentHashMap<>(100);

    @NonNull
     BeanRegistration getOrCreate(@NonNull DefaultBeanContext beanContext,
                                        @Nullable BeanResolutionContext resolutionContext,
                                        @NonNull BeanDefinition definition,
                                        @NonNull Argument beanType,
                                        @Nullable Qualifier qualifier) {
        BeanRegistration beanRegistration = findBeanRegistration(definition, beanType, qualifier);
        if (beanRegistration != null) {
            return beanRegistration;
        }
        BeanDefinitionIdentity identity = BeanDefinitionIdentity.of(definition);
        BeanRegistration existingRegistration = singletonByBeanDefinition.get(identity);
        if (existingRegistration != null) {
            return existingRegistration;
        }
        Object lock = singletonsInCreationLocks.computeIfAbsent(identity, beanDefinitionIdentity -> new Object());
        synchronized (lock) {
            try {
                existingRegistration = singletonByBeanDefinition.get(identity);
                if (existingRegistration != null) {
                    return existingRegistration;
                }
                BeanRegistration newRegistration = beanContext.createRegistration(resolutionContext, beanType, qualifier, definition, false);
                registerSingletonBean(newRegistration, qualifier);
                return newRegistration;
            } finally {
                singletonsInCreationLocks.remove(identity);
            }
        }
    }

    /**
     * Register singleton.
     *
     * @param registration The bean registration
     * @param qualifier    The qualifier
     * @param           The singleton type
     * @return The new registration
     */
    @NonNull
     BeanRegistration registerSingletonBean(@NonNull BeanRegistration registration, Qualifier qualifier) {

        BeanDefinition beanDefinition = registration.beanDefinition;
        singletonByBeanDefinition.put(BeanDefinitionIdentity.of(beanDefinition), registration);
        if (!beanDefinition.isSingleton()) {
            // In some cases you can register an instance of non-singleton bean and expect it act as a singleton
            // Current test `RegisterSingletonSpec "test register singleton method"` does it because of the present @Inject annotation
            // This might be something to remove in 4.0
            DefaultBeanContext.BeanKey beanKey = new DefaultBeanContext.BeanKey<>(beanDefinition, qualifier);
            singletonByArgumentAndQualifier.put(beanKey, registration);
        }
        if (beanDefinition instanceof BeanDefinitionDelegate || beanDefinition instanceof RuntimeBeanDefinition) {
            // Special cases when custom bean definitions need to be indexed:
            // BeanDefinitionDelegate - doesn't really exist with a custom qualifier
            DefaultBeanContext.BeanKey beanKey = new DefaultBeanContext.BeanKey<>(beanDefinition, beanDefinition.getDeclaredQualifier());
            singletonByArgumentAndQualifier.put(beanKey, registration);
        }
        return registration;
    }

    /**
     * Fast check if singleton is present.
     *
     * @param beanType  The bean type
     * @param qualifier The qualifier
     * @param        The singleton type
     * @return true if contains, false doesn't mean the singleton is not present.
     */
     boolean containsBean(Argument beanType, Qualifier qualifier) {
        ArgumentUtils.requireNonNull("beanType", beanType);
        DefaultBeanContext.BeanKey beanKey = new DefaultBeanContext.BeanKey<>(beanType, qualifier);
        return singletonByArgumentAndQualifier.containsKey(beanKey);
    }

    /**
     * @return Active singleton registrations
     */
    @NonNull
    Collection getBeanRegistrations() {
        return singletonByBeanDefinition.values();
    }

    /**
     * @param qualifier The qualifier
     * @return Active singleton registrations by qualifier
     */
    @NonNull
    Collection> getBeanRegistrations(@NonNull Qualifier qualifier) {
        List> beanRegistrations = new ArrayList<>();
        for (BeanRegistration beanRegistration : singletonByBeanDefinition.values()) {
            BeanDefinition beanDefinition = beanRegistration.beanDefinition;
            if (qualifier.reduce(beanDefinition.getBeanType(), Stream.of(beanDefinition)).findFirst().isPresent()) {
                beanRegistrations.add(beanRegistration);
            }
        }
        return beanRegistrations;
    }

    /**
     * @param beanType The beanType
     * @param       The bean type
     * @return Active singleton registrations by beanType
     */
    @SuppressWarnings("unchecked")
    @NonNull
     Collection> getBeanRegistrations(@NonNull Class beanType) {
        List> beanRegistrations = new ArrayList<>();
        for (BeanRegistration beanRegistration : singletonByBeanDefinition.values()) {
            BeanDefinition beanDefinition = beanRegistration.beanDefinition;
            if (beanType.isAssignableFrom(beanDefinition.getBeanType())) {
                beanRegistrations.add((BeanRegistration) beanRegistration);
            }
        }
        return beanRegistrations;
    }

    /**
     * Find active bean registration by provided bean definition and qualifier.
     *
     * @param beanDefinition The beanDefinition
     * @param qualifier      The qualifier
     * @param             The bean type
     * @return found registration or null
     */
    @Nullable
     BeanRegistration findBeanRegistration(@NonNull BeanDefinition beanDefinition, @Nullable Qualifier qualifier) {
        return findBeanRegistration(beanDefinition, beanDefinition.asArgument(), qualifier);
    }

    /**
     * Find active bean registration by provided identifier.
     *
     * @param identifier The identifier
     * @param         The bean type
     * @return found registration or null
     */
    @Nullable
     BeanRegistration findBeanRegistration(@NonNull BeanIdentifier identifier) {
        for (BeanRegistration registration : singletonByBeanDefinition.values()) {
            if (registration.identifier.equals(identifier)) {
                return registration;
            }
        }
        return null;
    }

    /**
     * Find active bean registration by provided bean instance.
     *
     * @param bean The bean
     * @param   The bean type
     * @return found registration or null
     */
    @Nullable
     BeanRegistration findBeanRegistration(@Nullable T bean) {
        if (bean == null) {
            return null;
        }
        for (BeanRegistration beanRegistration : singletonByBeanDefinition.values()) {
            if (bean == beanRegistration.getBean()) {
                return beanRegistration;
            }
        }
        return null;
    }

    /**
     * Find active bean registration by provided bean definition.
     *
     * @param definition The
     * @param         The bean type
     * @return found registration or null
     */
    @Nullable
     BeanRegistration findBeanRegistration(@NonNull BeanDefinition definition) {
        return singletonByBeanDefinition.get(BeanDefinitionIdentity.of(definition));
    }

    /**
     * Find active bean registration by provided bean definition, beanType and qualifier.
     *
     * @param beanDefinition The beanDefinition
     * @param beanType       The beanType
     * @param qualifier      The qualifier
     * @param             The bean type
     * @return found registration or null
     */
    @Nullable
     BeanRegistration findBeanRegistration(@NonNull BeanDefinition beanDefinition,
                                                 @NonNull Argument beanType,
                                                 @Nullable Qualifier qualifier) {
        BeanRegistration beanRegistration = singletonByBeanDefinition.get(BeanDefinitionIdentity.of(beanDefinition));
        if (beanRegistration == null) {
            return findCachedSingletonBeanRegistration(beanType, qualifier);
        }
        return beanRegistration;
    }

    /**
     * Find cached singleton registration by beanType and qualifier.
     * 

* If the result is null it doesn't mean singleton is not present. * * @param beanType The bean type * @param qualifier The qualifier * @param The type * @return found registration. */ @Nullable BeanRegistration findCachedSingletonBeanRegistration(@NonNull Argument beanType, @Nullable Qualifier qualifier) { DefaultBeanContext.BeanKey beanKey = new DefaultBeanContext.BeanKey<>(beanType, qualifier); BeanRegistration beanRegistration = singletonByArgumentAndQualifier.get(beanKey); if (beanRegistration != null && beanRegistration.bean != null) { return beanRegistration; } return null; } /** * Find cached singleton registration's bean definition by beanType and qualifier. *

* If the result is null it doesn't mean singleton is not present. * * @param beanType The bean type * @param qualifier The qualifier * @param The type * @return found registration's bean definition. */ @Nullable BeanDefinition findCachedSingletonBeanDefinition(@NonNull Argument beanType, @Nullable Qualifier qualifier) { BeanRegistration reg = findCachedSingletonBeanRegistration(beanType, qualifier); if (reg != null) { return reg.getBeanDefinition(); } return null; } /** * Purge cached instances by {@link BeanDefinition} and an instance. * * @param beanDefinition The bean definition * @param bean The bean * @param The bean type */ synchronized void purgeCacheForBeanInstance(BeanDefinition beanDefinition, T bean) { singletonByBeanDefinition.remove(BeanDefinitionIdentity.of(beanDefinition)); singletonByArgumentAndQualifier.entrySet().removeIf(entry -> entry.getKey().beanType.isInstance(bean)); } /** * Cleanup the scope. */ void clear() { singletonByBeanDefinition.clear(); singletonByArgumentAndQualifier.clear(); } /** * The bean definition identity implementation. * * @author Denis Stepanov * @since 3.5.0 */ interface BeanDefinitionIdentity { static BeanDefinitionIdentity of(BeanDefinition beanDefinition) { if (beanDefinition instanceof BeanDefinitionDelegate definitionDelegate) { return new BeanDefinitionDelegatedIdentity(definitionDelegate); } else if (beanDefinition instanceof RuntimeBeanDefinition runtimeBeanDefinition) { return new RuntimeBeanDefinitionIdentity(runtimeBeanDefinition); } return new SimpleBeanDefinitionIdentity(beanDefinition); } } /** * Simple key object that compares {@link BeanDefinitionDelegate}s by its class name and attributes. * * @since 3.5.0 */ static final class BeanDefinitionDelegatedIdentity implements BeanDefinitionIdentity { private final BeanDefinitionDelegate beanDefinitionDelegate; BeanDefinitionDelegatedIdentity(BeanDefinitionDelegate beanDefinitionDelegate) { this.beanDefinitionDelegate = beanDefinitionDelegate; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } BeanDefinitionDelegatedIdentity that = (BeanDefinitionDelegatedIdentity) o; if (beanDefinitionDelegate.definition.getClass() != that.beanDefinitionDelegate.definition.getClass()) { return false; } return Objects.equals(beanDefinitionDelegate.getDeclaredQualifier(), that.beanDefinitionDelegate.getDeclaredQualifier()); } @Override public int hashCode() { return ObjectUtils.hash(beanDefinitionDelegate.getBeanType(), beanDefinitionDelegate.getDeclaredQualifier()); } } /** * Simple key object that compares {@link BeanDefinitionDelegate}s by its class name and attributes. * * @since 3.5.0 */ static final class RuntimeBeanDefinitionIdentity implements BeanDefinitionIdentity { private final RuntimeBeanDefinition beanDefinition; RuntimeBeanDefinitionIdentity(RuntimeBeanDefinition beanDefinition) { this.beanDefinition = beanDefinition; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } RuntimeBeanDefinitionIdentity that = (RuntimeBeanDefinitionIdentity) o; if (beanDefinition.getBeanType() != that.beanDefinition.getBeanType()) { return false; } return beanDefinition.getBeanDefinitionName().equals(that.beanDefinition.getBeanDefinitionName()); } @Override public int hashCode() { return beanDefinition.getBeanDefinitionName().hashCode(); } } /** * Simple key object that compares {@link BeanDefinition}s by its class name. * It's possible we have multiple instances of the same bean definition. * * @since 3.5.0 */ static final class SimpleBeanDefinitionIdentity implements BeanDefinitionIdentity { private final Class beanDefinitionClass; SimpleBeanDefinitionIdentity(BeanDefinition beanDefinition) { this.beanDefinitionClass = beanDefinition.getClass(); } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } SimpleBeanDefinitionIdentity that = (SimpleBeanDefinitionIdentity) o; return beanDefinitionClass == that.beanDefinitionClass; } @Override public int hashCode() { return beanDefinitionClass.hashCode(); } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy