Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
io.micronaut.context.DefaultBeanContext Maven / Gradle / Ivy
/*
* Copyright 2017-2022 original authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.micronaut.context;
import io.micronaut.context.annotation.ConfigurationReader;
import io.micronaut.context.annotation.Context;
import io.micronaut.context.annotation.DefaultImplementation;
import io.micronaut.context.annotation.Executable;
import io.micronaut.context.annotation.Infrastructure;
import io.micronaut.context.annotation.Parallel;
import io.micronaut.context.annotation.Primary;
import io.micronaut.context.annotation.Prototype;
import io.micronaut.context.annotation.Replaces;
import io.micronaut.context.annotation.Secondary;
import io.micronaut.context.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.Order;
import io.micronaut.core.annotation.UsedByGeneratedCode;
import io.micronaut.core.convert.ConversionService;
import io.micronaut.core.convert.DefaultConversionService;
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.Named;
import io.micronaut.core.order.OrderUtil;
import io.micronaut.core.order.Ordered;
import io.micronaut.core.reflect.ClassUtils;
import io.micronaut.core.type.Argument;
import io.micronaut.core.type.ReturnType;
import io.micronaut.core.util.ArgumentUtils;
import io.micronaut.core.util.ArrayUtils;
import io.micronaut.core.util.CollectionUtils;
import io.micronaut.core.util.StreamUtils;
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.BeanFactory;
import io.micronaut.inject.BeanIdentifier;
import io.micronaut.inject.BeanType;
import io.micronaut.inject.ConstructorInjectionPoint;
import io.micronaut.inject.DisposableBeanDefinition;
import io.micronaut.inject.ExecutableMethod;
import io.micronaut.inject.InitializingBeanDefinition;
import io.micronaut.inject.InjectionPoint;
import io.micronaut.inject.MethodExecutionHandle;
import io.micronaut.inject.ParametrizedBeanFactory;
import io.micronaut.inject.ProxyBeanDefinition;
import io.micronaut.inject.ValidatedBeanDefinition;
import io.micronaut.inject.proxy.InterceptedBeanProxy;
import io.micronaut.inject.qualifiers.AnyQualifier;
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.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.LinkedHashMap;
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.ConcurrentLinkedQueue;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* The default context implementations.
*
* @author Graeme Rocher
* @since 1.0
*/
@SuppressWarnings("MagicNumber")
public class DefaultBeanContext implements InitializableBeanContext {
protected static final Logger LOG = LoggerFactory.getLogger(DefaultBeanContext.class);
protected static final Logger LOG_LIFECYCLE = LoggerFactory.getLogger(DefaultBeanContext.class.getPackage().getName() + ".lifecycle");
@SuppressWarnings("rawtypes")
private static final Qualifier PROXY_TARGET_QUALIFIER = new Qualifier() {
@SuppressWarnings("rawtypes")
@Override
public > Stream reduce(Class beanType, Stream candidates) {
return candidates.filter(bt -> {
if (bt instanceof BeanDefinitionDelegate) {
return !(((BeanDefinitionDelegate) bt).getDelegate() instanceof ProxyBeanDefinition);
} else {
return !(bt instanceof ProxyBeanDefinition);
}
});
}
};
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 NAMED_MEMBER = "named";
private static final String QUALIFIER_MEMBER = "qualifier";
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 Comparator> BEAN_REGISTRATION_COMPARATOR = (o1, o2) -> {
int order1 = OrderUtil.getOrder(o1.getBeanDefinition(), o1.getBean());
int order2 = OrderUtil.getOrder(o2.getBeanDefinition(), o2.getBean());
return Integer.compare(order1, order2);
};
protected final AtomicBoolean running = new AtomicBoolean(false);
protected final AtomicBoolean initializing = new AtomicBoolean(false);
protected final AtomicBoolean terminating = new AtomicBoolean(false);
final Map> singlesInCreation = new ConcurrentHashMap<>(5);
Set, List>> beanInitializedEventListeners;
private final SingletonScope singletonScope = new SingletonScope();
private final BeanContextConfiguration beanContextConfiguration;
private final Collection beanDefinitionsClasses = new ConcurrentLinkedQueue<>();
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> beanCandidateCache = new ConcurrentLinkedHashMap.Builder>().maximumWeightedCapacity(30).build();
private final Map> 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 Set, List>>> beanCreationEventListeners;
private Set, List>> beanPreDestroyEventListeners;
private Set, List>> beanDestroyedEventListeners;
/**
* 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 eagerInitStereotypes = new ArrayList<>(eagerInitAnnotated.size());
for (Class extends Annotation> ann : eagerInitAnnotated) {
eagerInitStereotypes.add(ann.getName());
}
this.eagerInitStereotypes = eagerInitStereotypes.toArray(new String[0]);
this.eagerInitStereotypesPresent = !eagerInitStereotypes.isEmpty();
this.eagerInitSingletons = eagerInitStereotypesPresent && (eagerInitStereotypes.contains(AnnotationUtil.SINGLETON) || eagerInitStereotypes.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)) {
// Reset possibly modified shared context
((DefaultConversionService) ConversionService.SHARED).reset();
if (LOG.isDebugEnabled()) {
LOG.debug("Starting BeanContext");
}
finalizeConfiguration();
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;
}
/**
* 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)) {
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);
}
}
}
singletonBeanRegistrations.clear();
beanConcreteCandidateCache.clear();
beanCandidateCache.clear();
containsBeanCache.clear();
beanConfigurations.clear();
singletonScope.clear();
beanInitializedEventListeners = null;
beanCreationEventListeners = null;
beanPreDestroyEventListeners = null;
beanDestroyedEventListeners = null;
((DefaultConversionService) ConversionService.SHARED).reset();
terminating.set(false);
running.set(false);
}
return this;
}
@Override
@NonNull
public AnnotationMetadata resolveMetadata(Class> type) {
if (type == null) {
return AnnotationMetadata.EMPTY_METADATA;
}
return findBeanDefinition(Argument.of(type), null, false)
.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");
if (beanRegistration.bean != null) {
beanRegistration.definition().inject(this, beanRegistration.bean);
}
}
@Override
public Collection> getActiveBeanRegistrations(Qualifier> qualifier) {
if (qualifier == null) {
return Collections.emptyList();
}
return singletonScope.getBeanRegistrations(qualifier);
}
@Override
public Collection> getActiveBeanRegistrations(Class beanType) {
if (beanType == null) {
return Collections.emptyList();
}
return singletonScope.getBeanRegistrations(beanType);
}
@Override
public Collection> getBeanRegistrations(Class beanType) {
if (beanType == null) {
return Collections.emptyList();
}
return getBeanRegistrations(null, Argument.of(beanType), null);
}
@Override
public BeanRegistration getBeanRegistration(Class beanType, Qualifier qualifier) {
return getBeanRegistration(null, Argument.of(beanType), qualifier);
}
@Override
public Collection> getBeanRegistrations(Class beanType, Qualifier qualifier) {
if (beanType == null) {
return Collections.emptyList();
}
return getBeanRegistrations(null, Argument.of(beanType), null);
}
@Override
public Collection> getBeanRegistrations(Argument beanType, Qualifier qualifier) {
return getBeanRegistrations(
null,
Objects.requireNonNull(beanType, "Bean type cannot be null"),
qualifier
);
}
@Override
public BeanRegistration getBeanRegistration(Argument beanType, Qualifier qualifier) {
return getBeanRegistration(
null,
Objects.requireNonNull(beanType, "Bean type cannot be null"),
qualifier
);
}
@Override
public BeanRegistration getBeanRegistration(BeanDefinition beanDefinition) {
return resolveBeanRegistration(null, beanDefinition);
}
@Override
public Optional> findBeanRegistration(T bean) {
if (bean == null) {
return Optional.empty();
}
BeanRegistration beanRegistration = singletonScope.findBeanRegistration(bean);
if (beanRegistration != null) {
return Optional.of(beanRegistration);
}
return customScopeRegistry.findBeanRegistration(bean);
}
@Override
public Optional> findExecutionHandle(Class beanType, String method, Class... arguments) {
return findExecutionHandle(beanType, null, method, arguments);
}
@Override
public MethodExecutionHandle, Object> createExecutionHandle(BeanDefinition extends Object> beanDefinition, ExecutableMethod method) {
return new MethodExecutionHandle() {
private Object target;
@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, Object> getExecutableMethod() {
return (ExecutableMethod, Object>) method;
}
};
}
@SuppressWarnings("unchecked")
@Override
public Optional> findExecutionHandle(Class beanType, Qualifier> qualifier, String method, Class... arguments) {
Optional extends BeanDefinition>> foundBean = findBeanDefinition(beanType, (Qualifier) qualifier);
if (foundBean.isPresent()) {
BeanDefinition> beanDefinition = foundBean.get();
Optional extends ExecutableMethod, Object>> foundMethod = beanDefinition.findMethod(method, arguments);
if (foundMethod.isPresent()) {
return foundMethod.map((ExecutableMethod executableMethod) ->
new BeanExecutionHandle(this, beanType, qualifier, executableMethod)
);
} else {
return 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;
})
.map((ExecutableMethod executableMethod) -> new BeanExecutionHandle(this, beanType, qualifier, executableMethod));
}
}
return Optional.empty();
}
@Override
public Optional> findExecutableMethod(Class beanType, String method, Class[] arguments) {
if (beanType != null) {
Collection> definitions = getBeanDefinitions(beanType);
if (!definitions.isEmpty()) {
BeanDefinition beanDefinition = definitions.iterator().next();
Optional> foundMethod = beanDefinition.findMethod(method, arguments);
if (foundMethod.isPresent()) {
return foundMethod;
} else {
return beanDefinition.findPossibleMethods(method)
.findFirst();
}
}
}
return Optional.empty();
}
@SuppressWarnings("unchecked")
@Override
public Optional> findExecutionHandle(T bean, String method, Class[] arguments) {
if (bean != null) {
Optional extends BeanDefinition>> foundBean = findBeanDefinition(bean.getClass());
if (foundBean.isPresent()) {
BeanDefinition> beanDefinition = foundBean.get();
Optional extends ExecutableMethod, Object>> foundMethod = beanDefinition.findMethod(method, arguments);
if (foundMethod.isPresent()) {
return foundMethod.map((ExecutableMethod executableMethod) -> new ObjectExecutionHandle<>(bean, executableMethod));
} else {
return beanDefinition.findPossibleMethods(method)
.findFirst()
.map((ExecutableMethod executableMethod) -> new ObjectExecutionHandle<>(bean, executableMethod));
}
}
}
return Optional.empty();
}
@Override
public BeanContext registerSingleton(@NonNull Class type, @NonNull T singleton, Qualifier qualifier, boolean inject) {
purgeCacheForBeanInstance(singleton);
BeanDefinition beanDefinition;
if (inject && running.get()) {
// Bean cannot be injected before the start of the context
beanDefinition = findBeanDefinition(type, qualifier).orElse(null);
if (beanDefinition == null) {
// Purge cache miss
beanCandidateCache.entrySet().removeIf(entry -> entry.getKey().isInstance(singleton));
beanConcreteCandidateCache.entrySet().removeIf(entry -> entry.getKey().beanType.isInstance(singleton));
}
} else {
beanDefinition = null;
}
if (beanDefinition != null && beanDefinition.getBeanType().isInstance(singleton)) {
try (BeanResolutionContext context = newResolutionContext(beanDefinition, null)) {
doInject(context, singleton, beanDefinition);
DefaultBeanContext.BeanKey key = new DefaultBeanContext.BeanKey<>(beanDefinition.asArgument(), qualifier);
singletonScope.registerSingletonBean(BeanRegistration.of(this, key, beanDefinition, singleton), qualifier);
}
} else {
NoInjectionBeanDefinition dynamicRegistration = new NoInjectionBeanDefinition<>(singleton.getClass(), qualifier);
if (qualifier instanceof Named) {
final BeanDefinitionDelegate delegate = BeanDefinitionDelegate.create(dynamicRegistration);
delegate.put(BeanDefinition.NAMED_ATTRIBUTE, ((Named) qualifier).getName());
beanDefinition = delegate;
} else {
beanDefinition = dynamicRegistration;
}
beanDefinitionsClasses.add(dynamicRegistration);
DefaultBeanContext.BeanKey key = new DefaultBeanContext.BeanKey<>(beanDefinition.asArgument(), qualifier);
singletonScope.registerSingletonBean(BeanRegistration.of(this, key, dynamicRegistration, singleton), qualifier);
for (Class indexedType : indexedTypes) {
if (indexedType == type || indexedType.isAssignableFrom(type)) {
final Collection indexed = resolveTypeIndex(indexedType);
BeanDefinition finalBeanDefinition = beanDefinition;
indexed.add(new AbstractBeanDefinitionReference(type.getName(), type.getName()) {
@Override
protected Class extends BeanDefinition>> getBeanDefinitionType() {
return (Class extends BeanDefinition>>) finalBeanDefinition.getClass();
}
@Override
public BeanDefinition load() {
return finalBeanDefinition;
}
@Override
public Class getBeanType() {
return type;
}
});
break;
}
}
}
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(() -> new NoSuchBeanException(beanType, qualifier));
}
@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> findBeanDefinition(Argument beanType, Qualifier qualifier, boolean throwNonUnique) {
return findConcreteCandidate(null, beanType, qualifier, throwNonUnique);
}
@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.reduce(beanType.getType(), new ArrayList<>(candidates).stream()).collect(Collectors.toList());
}
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.LOG.isDebugEnabled()) {
AbstractBeanContextConditional.LOG.debug("Bean of type [{}] disabled for reason: {}", beanType.getSimpleName(), e.getMessage());
}
throw new NoSuchBeanException(beanType, qualifier);
}
}
@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);
}
/**
* 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 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);
if (candidates.size() == 1) {
BeanDefinition beanDefinition = candidates.iterator().next();
try (BeanResolutionContext resolutionContext = newResolutionContext(beanDefinition, null)) {
final BeanKey beanKey = new BeanKey<>(beanDefinition.getBeanType(), null);
resolutionContext.addInFlightBean(
beanKey,
new BeanRegistration<>(beanKey, beanDefinition, instance)
);
doInject(
resolutionContext,
instance,
beanDefinition
);
}
} else if (!candidates.isEmpty()) {
final Iterator iterator = candidates.iterator();
throw new NonUniqueBeanException(instance.getClass(), iterator);
}
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 new NoSuchBeanException(beanType);
}
@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, beanArg, qualifier, args);
}
}
throw new NoSuchBeanException(beanType);
}
/**
* @param resolutionContext The bean resolution context
* @param definition The bean definition
* @param beanType The bean type
* @param qualifier The qualifier
* @param args The argument values
* @param the bean generic type
* @return The instance
*/
@NonNull
protected T doCreateBean(@NonNull BeanResolutionContext resolutionContext,
@NonNull BeanDefinition definition,
@NonNull Argument beanType,
@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) {
if (!(definition instanceof ParametrizedBeanFactory)) {
return Collections.emptyMap();
}
if (LOG.isTraceEnabled()) {
LOG.trace("Creating bean for parameters: {}", ArrayUtils.toString(args));
}
Argument[] requiredArguments = ((ParametrizedBeanFactory) definition).getRequiredArguments();
Map argumentValues = new LinkedHashMap<>(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.SHARED.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 [" + beanToDestroy + "]... Continuing...", e);
}
}
}
if (beanToDestroy instanceof LifeCycle) {
try {
((LifeCycle>) beanToDestroy).stop();
} catch (Exception e) {
throw new BeanDestructionException(definition, e);
}
}
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);
}
@NonNull
private T triggerPreDestroyListeners(@NonNull BeanDefinition beanDefinition, @NonNull T bean) {
if (beanPreDestroyEventListeners == null) {
beanPreDestroyEventListeners = loadListeners(BeanPreDestroyEventListener.class).entrySet();
}
if (!beanPreDestroyEventListeners.isEmpty()) {
Class beanType = getBeanType(beanDefinition);
for (Map.Entry, List> entry : beanPreDestroyEventListeners) {
if (entry.getKey().isAssignableFrom(beanType)) {
final BeanPreDestroyEvent event = new BeanPreDestroyEvent<>(this, beanDefinition, bean);
for (BeanPreDestroyEventListener listener : entry.getValue()) {
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) {
BeanDisposingRegistration> disposingRegistration = (BeanDisposingRegistration>) registration;
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.isPresent()) {
if (proxyTargetBeanDefinition.isSingleton()) {
return;
}
// Scope is not present, try to get the actual target bean and destroy it
if (registration.bean instanceof InterceptedBeanProxy) {
InterceptedBeanProxy