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.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.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.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.reflect.GenericTypeUtils;
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.FieldInjectionPoint;
import io.micronaut.inject.InitializingBeanDefinition;
import io.micronaut.inject.InjectionPoint;
import io.micronaut.inject.MethodExecutionHandle;
import io.micronaut.inject.MethodInjectionPoint;
import io.micronaut.inject.ParametrizedBeanFactory;
import io.micronaut.inject.ProxyBeanDefinition;
import io.micronaut.inject.ValidatedBeanDefinition;
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.Provider;
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.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 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 singletonObjects = new ConcurrentHashMap<>(100);
final Map singlesInCreation = new ConcurrentHashMap<>(5);
final Map> scopedProxies = new ConcurrentHashMap<>(20);
Set>> beanInitializedEventListeners;
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 initializedObjectsByType = 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 Set>> beanCreationEventListeners;
private BeanDefinitionValidator beanValidator;
private List beanDefinitionReferences;
private List beanConfigurationsList;
/**
* 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)) {
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(singletonObjects.values());
Set processed = new HashSet<>();
for (BeanRegistration beanRegistration : objects) {
BeanDefinition def = beanRegistration.beanDefinition;
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 {
disposeBean(beanRegistration, def.asArgument(), bean, def);
} catch (BeanDestructionException e) {
if (LOG.isErrorEnabled()) {
LOG.error(e.getMessage(), e);
}
}
}
terminating.set(false);
running.set(false);
}
return this;
}
@Override
@NonNull
public AnnotationMetadata resolveMetadata(Class> type) {
if (type == null) {
return AnnotationMetadata.EMPTY_METADATA;
} else {
Optional extends BeanDefinition>> candidate = findConcreteCandidate(
null,
Argument.of(type),
null,
false
);
return candidate.map(AnnotationMetadataProvider::getAnnotationMetadata).orElse(AnnotationMetadata.EMPTY_METADATA);
}
}
@SuppressWarnings({"SuspiciousMethodCalls", "unchecked"})
@Override
public Optional refreshBean(BeanIdentifier identifier) {
if (identifier != null) {
BeanRegistration beanRegistration = singletonObjects.get(identifier);
if (beanRegistration != null) {
BeanDefinition definition = beanRegistration.getBeanDefinition();
return Optional.of((T) definition.inject(this, beanRegistration.getBean()));
}
}
return Optional.empty();
}
@SuppressWarnings("unchecked")
@Override
public Collection> getActiveBeanRegistrations(Qualifier> qualifier) {
if (qualifier == null) {
return Collections.emptyList();
}
List result = singletonObjects
.values()
.stream()
.filter(registration -> {
BeanDefinition beanDefinition = registration.beanDefinition;
return qualifier.reduce(beanDefinition.getBeanType(), Stream.of(beanDefinition)).findFirst().isPresent();
})
.collect(Collectors.toList());
return result;
}
@SuppressWarnings("unchecked")
@Override
public Collection> getActiveBeanRegistrations(Class beanType) {
if (beanType == null) {
return Collections.emptyList();
}
List result = singletonObjects
.values()
.stream()
.filter(registration -> {
BeanDefinition beanDefinition = registration.beanDefinition;
return beanType.isAssignableFrom(beanDefinition.getBeanType());
})
.collect(Collectors.toList());
return result;
}
@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 Optional> findBeanRegistration(T bean) {
if (bean == null) {
return Optional.empty();
}
for (BeanRegistration beanRegistration : singletonObjects.values()) {
if (bean == beanRegistration.getBean()) {
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();
}
@SuppressWarnings("rawtypes")
@Override
public Object getTarget() {
Object target = this.target;
if (target == null) {
synchronized (this) { // double check
target = this.target;
if (target == null) {
try (BeanResolutionContext context = newResolutionContext(beanDefinition, null)) {
BeanDefinition rawDefinition = beanDefinition;
target = getBeanForDefinition(
context,
Argument.of(rawDefinition.getBeanType()),
rawDefinition.getDeclaredQualifier(),
true,
rawDefinition
);
}
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) {
ArgumentUtils.requireNonNull("type", type);
ArgumentUtils.requireNonNull("singleton", singleton);
BeanKey beanKey = new BeanKey<>(type, qualifier);
synchronized (singletonObjects) {
initializedObjectsByType.clear();
beanCandidateCache.remove(Argument.of(type));
BeanDefinition beanDefinition = inject ? findConcreteCandidate(
null,
beanKey.beanType,
qualifier,
false
).orElse(null) : null;
if (beanDefinition != null && beanDefinition.getBeanType().isInstance(singleton)) {
try (BeanResolutionContext context = newResolutionContext(beanDefinition, null)) {
doInject(context, singleton, beanDefinition);
final List> dependentBeans = context.getAndResetDependentBeans();
if (dependentBeans.isEmpty()) {
singletonObjects.put(beanKey, new BeanDisposingRegistration(beanKey, beanDefinition, singleton));
} else {
singletonObjects.put(beanKey, new BeanDisposingRegistration(beanKey, beanDefinition, singleton, dependentBeans));
}
BeanKey concreteKey = new BeanKey(singleton.getClass(), qualifier);
singletonObjects.put(concreteKey, new BeanRegistration<>(concreteKey, beanDefinition, singleton));
}
} 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);
singletonObjects.put(beanKey, new BeanRegistration<>(beanKey, dynamicRegistration, singleton));
BeanKey concreteKey = new BeanKey(singleton.getClass(), qualifier);
singletonObjects.put(concreteKey, new BeanRegistration<>(concreteKey, dynamicRegistration, singleton));
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;
}
@NonNull
private 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 findConcreteCandidate(null, beanType, qualifier, true)
.orElseThrow(() -> new NoSuchBeanException(beanType, qualifier));
}
@Override
public Optional> findBeanDefinition(Argument beanType, Qualifier qualifier) {
if (Objects.requireNonNull(beanType, "Bean type cannot be null").equalsType(Argument.OBJECT_ARGUMENT)) {
// optimization for object resolve
return Optional.empty();
}
BeanKey beanKey = new BeanKey<>(beanType, qualifier);
@SuppressWarnings("unchecked") BeanRegistration reg = singletonObjects.get(beanKey);
if (reg != null) {
return Optional.of(reg.getBeanDefinition());
}
Collection> beanCandidates = new ArrayList<>(findBeanCandidatesInternal(null, beanKey.beanType));
if (qualifier != null) {
beanCandidates = qualifier.reduce(beanType.getType(), beanCandidates.stream()).collect(Collectors.toList());
}
filterProxiedTypes(beanCandidates, true, true, null);
if (beanCandidates.isEmpty()) {
return Optional.empty();
} else {
if (beanCandidates.size() == 1) {
return Optional.of(beanCandidates.iterator().next());
} else {
return findConcreteCandidate(null, beanKey.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) {
Collection> candidates = findBeanCandidatesInternal(
null,
Objects.requireNonNull(beanType, "Bean type cannot be null")
);
return Collections.unmodifiableCollection(candidates);
}
@Override
public Collection> getBeanDefinitions(Class beanType, Qualifier qualifier) {
return getBeanDefinitions(
Argument.of(Objects.requireNonNull(beanType, "Bean type cannot be null")),
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 = singletonObjects.containsKey(beanKey) ||
isCandidatePresent(beanKey.beanType, qualifier);
containsBeanCache.put(beanKey, result);
return result;
}
}
@Override
public @NonNull
T getBean(@NonNull Class beanType, @Nullable Qualifier qualifier) {
Objects.requireNonNull(beanType, "Bean type cannot be null");
try {
//noinspection ConstantConditions
return getBeanInternal(
null,
Argument.of(beanType),
qualifier,
true,
true
);
} 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 @NonNull
T getBean(@NonNull Class beanType) {
Objects.requireNonNull(beanType, "Bean type cannot be null");
try {
//noinspection ConstantConditions
return getBeanInternal(
null,
Argument.of(beanType),
null,
true,
true
);
} catch (DisabledBeanException e) {
if (AbstractBeanContextConditional.LOG.isDebugEnabled()) {
AbstractBeanContextConditional.LOG.debug("Bean of type [{}] disabled for reason: {}", beanType.getSimpleName(), e.getMessage());
}
throw new NoSuchBeanException(beanType, null);
}
}
@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 getBeanRegistrations(null, Argument.of(beanType), qualifier)
.stream()
.map(BeanRegistration::getBean)
.collect(Collectors.toList());
}
@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);
}
@Override
public @NonNull
T inject(@NonNull T instance) {
Objects.requireNonNull(instance, "Instance cannot be null");
Collection candidates = findBeanCandidatesForInstance(instance);
if (candidates.size() == 1) {
BeanDefinition beanDefinition = candidates.stream().findFirst().get();
try (BeanResolutionContext resolutionContext = newResolutionContext(beanDefinition, null)) {
final BeanKey beanKey = new BeanKey<>(beanDefinition.getBeanType(), null);
resolutionContext.addInFlightBean(
beanKey,
instance
);
doInject(
resolutionContext,
instance,
beanDefinition
);
}
} else if (!candidates.isEmpty()) {
final Iterator iterator = candidates.iterator();
throw new NonUniqueBeanException(instance.getClass(), iterator);
}
return instance;
}
@Override
public @NonNull
T createBean(@NonNull Class beanType, @Nullable Qualifier qualifier) {
return createBean(null, beanType, qualifier);
}
@Override
public @NonNull
T createBean(@NonNull Class beanType, @Nullable Qualifier qualifier, @Nullable Map argumentValues) {
ArgumentUtils.requireNonNull("beanType", beanType);
final Argument beanArg = Argument.of(beanType);
Optional> candidate = findConcreteCandidate(
null,
beanArg,
qualifier,
true
);
if (candidate.isPresent()) {
try (BeanResolutionContext resolutionContext = newResolutionContext(candidate.get(), null)) {
T createdBean = doCreateBean(
resolutionContext,
candidate.get(),
qualifier,
beanArg,
false,
argumentValues
);
if (createdBean == null) {
throw new NoSuchBeanException(beanType);
}
return createdBean;
}
}
throw new NoSuchBeanException(beanType);
}
@Override
public @NonNull
T createBean(@NonNull Class beanType, @Nullable Qualifier qualifier, @Nullable Object... args) {
ArgumentUtils.requireNonNull("beanType", beanType);
final Argument beanArg = Argument.of(beanType);
Optional> candidate = findConcreteCandidate(
null,
beanArg,
qualifier,
true
);
if (candidate.isPresent()) {
BeanDefinition definition = candidate.get();
try (BeanResolutionContext resolutionContext = newResolutionContext(definition, null)) {
return doCreateBean(
resolutionContext,
definition,
Argument.of(beanType),
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
*/
protected @NonNull
T doCreateBean(@NonNull BeanResolutionContext resolutionContext,
@NonNull BeanDefinition definition,
@NonNull Argument beanType,
@Nullable Qualifier qualifier,
@Nullable Object... args) {
Map argumentValues;
if (definition instanceof ParametrizedBeanFactory) {
if (LOG.isTraceEnabled()) {
LOG.trace("Creating bean for parameters: {}", ArrayUtils.toString(args));
}
Argument[] requiredArguments = ((ParametrizedBeanFactory) definition).getRequiredArguments();
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);
}
}
}
}
}
} else {
argumentValues = Collections.emptyMap();
}
if (LOG.isTraceEnabled()) {
LOG.trace("Computed bean argument values: {}", argumentValues);
}
T createdBean = doCreateBean(resolutionContext, definition, qualifier, beanType, false, argumentValues);
if (createdBean == null) {
throw new NoSuchBeanException(beanType);
}
return createdBean;
}
@Override
public T destroyBean(Argument beanType, Qualifier qualifier) {
ArgumentUtils.requireNonNull("beanType", beanType);
T bean = null;
BeanKey beanKey = new BeanKey<>(beanType, qualifier);
synchronized (singletonObjects) {
if (singletonObjects.containsKey(beanKey)) {
@SuppressWarnings("unchecked") BeanRegistration beanRegistration = singletonObjects.get(beanKey);
bean = beanRegistration.bean;
if (bean != null) {
if (LOG_LIFECYCLE.isDebugEnabled()) {
LOG_LIFECYCLE.debug("Destroying bean [{}] with identifier [{}]", bean, beanKey);
}
purgeBeanRegistrations(bean);
disposeBean(beanRegistration, beanType, bean, beanRegistration.beanDefinition);
}
} else {
final BeanDefinition candidate = findConcreteCandidate(
null,
beanKey.beanType,
qualifier,
false
).orElse(null);
if (candidate != null) {
BeanRegistration registration = findExistingCompatibleSingleton(beanType, null, qualifier, candidate);
if (registration != null) {
bean = registration.getBean();
disposeBean(registration, beanType, bean, candidate);
purgeBeanRegistrations(bean);
}
}
}
}
return bean;
}
private void purgeBeanRegistrations(T bean) {
singletonObjects.entrySet().removeIf(entry -> entry.getValue().bean == bean);
}
@SuppressWarnings({"rawtypes", "unchecked"})
@Override
@NonNull
public T destroyBean(T bean) {
Objects.requireNonNull(bean, "Bean cannot be null");
final Argument arg = Argument.of(bean.getClass());
final BeanDefinition concreteCandidate = (BeanDefinition) findConcreteCandidate(
null,
arg,
null,
false
).orElse(null);
if (concreteCandidate != null) {
disposeBean(new BeanDisposingRegistration<>(
new BeanKey<>(concreteCandidate, concreteCandidate.getDeclaredQualifier()),
concreteCandidate,
bean
), arg, bean, concreteCandidate);
}
return bean;
}
@Override
public @Nullable
T destroyBean(@NonNull Class beanType) {
return destroyBean(Argument.of(beanType), null);
}
private void disposeBean(
BeanRegistration registration,
Argument beanType,
T finalBean,
BeanDefinition definition) {
final List preDestroyEventListeners = resolveListeners(
BeanPreDestroyEventListener.class, beanType
);
T beanToDestroy = finalBean;
if (CollectionUtils.isNotEmpty(preDestroyEventListeners)) {
for (BeanPreDestroyEventListener listener : preDestroyEventListeners) {
try {
final BeanPreDestroyEvent event =
new BeanPreDestroyEvent<>(this, definition, beanToDestroy);
beanToDestroy = Objects.requireNonNull(
listener.onPreDestroy(event),
"PreDestroy event listener illegally returned null: " + listener.getClass()
);
} catch (Exception e) {
throw new BeanDestructionException(definition, e);
}
}
}
try {
registration.close();
} catch (Exception e) {
throw new BeanDestructionException(definition, e);
}
final List postDestroyListeners = resolveListeners(
BeanDestroyedEventListener.class, beanType
);
if (CollectionUtils.isNotEmpty(postDestroyListeners)) {
for (BeanDestroyedEventListener listener : postDestroyListeners) {
try {
final BeanDestroyedEvent event =
new BeanDestroyedEvent<>(this, definition, beanToDestroy);
listener.onDestroyed(event);
} catch (Exception e) {
throw new BeanDestructionException(definition, e);
}
}
}
}
@NonNull
private List resolveListeners(Class type, Argument> genericType) {
return streamOfType(Argument.of(type, genericType))
.sorted(OrderUtil.COMPARATOR)
.collect(Collectors.toList());
}
/**
* 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 singletonObjects.get(new BeanKey(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
*/
protected @NonNull
T createBean(@Nullable BeanResolutionContext resolutionContext, @NonNull Class beanType, @Nullable Qualifier qualifier) {
ArgumentUtils.requireNonNull("beanType", beanType);
final Argument beanArgument = Argument.of(beanType);
Optional> concreteCandidate = findConcreteCandidate(
resolutionContext,
beanArgument,
qualifier,
true
);
if (concreteCandidate.isPresent()) {
BeanDefinition candidate = concreteCandidate.get();
try (BeanResolutionContext context = newResolutionContext(candidate, resolutionContext)) {
T createBean = doCreateBean(
context,
candidate,
qualifier,
beanArgument,
false, null
);
if (createBean == null) {
throw new NoSuchBeanException(beanType);
}
return createBean;
}
}
throw new NoSuchBeanException(beanType);
}
/**
* 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
*/
protected @NonNull
T inject(@NonNull BeanResolutionContext resolutionContext, @Nullable BeanDefinition requestingBeanDefinition, @NonNull T instance) {
@SuppressWarnings("unchecked") Class beanType = (Class) instance.getClass();
Optional> concreteCandidate = findConcreteCandidate(
resolutionContext,
Argument.of(beanType),
null,
false
);
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;
}
doInject(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
*/
protected @NonNull
Collection getBeansOfType(@Nullable BeanResolutionContext resolutionContext, @NonNull Argument beanType) {
return getBeanRegistrations(resolutionContext, beanType, null)
.stream().map(BeanRegistration::getBean)
.collect(Collectors.toList());
}
/**
* 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
public @NonNull
Collection getBeansOfType(
@Nullable BeanResolutionContext resolutionContext,
@NonNull Argument beanType,
@Nullable Qualifier qualifier) {
return getBeanRegistrations(resolutionContext, beanType, qualifier)
.stream().map(BeanRegistration::getBean)
.collect(Collectors.toList());
}
@SuppressWarnings("unchecked")
@Override
public @NonNull
T getProxyTargetBean(@NonNull Class beanType, @Nullable Qualifier qualifier) {
ArgumentUtils.requireNonNull("beanType", beanType);
return getProxyTargetBean(Argument.of(beanType), qualifier);
}
@NonNull
@Override
public T getProxyTargetBean(@NonNull Argument beanType, @Nullable Qualifier qualifier) {
BeanDefinition definition = getProxyTargetBeanDefinition(beanType, qualifier);
@SuppressWarnings("unchecked")
Qualifier proxyQualifier = qualifier != null ? Qualifiers.byQualifiers(qualifier, PROXY_TARGET_QUALIFIER) : PROXY_TARGET_QUALIFIER;
try (BeanResolutionContext resolutionContext = newResolutionContext(definition, null)) {
return getBeanForDefinition(
resolutionContext,
beanType,
proxyQualifier,
true,
definition
);
}
}
/**
* 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