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-2019 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
*
* http://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.*;
import io.micronaut.context.event.*;
import io.micronaut.context.exceptions.*;
import io.micronaut.context.processor.ExecutableMethodProcessor;
import io.micronaut.context.scope.CustomScope;
import io.micronaut.context.scope.CustomScopeRegistry;
import io.micronaut.core.annotation.*;
import io.micronaut.core.async.subscriber.Completable;
import io.micronaut.core.convert.ConversionService;
import io.micronaut.core.convert.TypeConverter;
import io.micronaut.core.convert.TypeConverterRegistrar;
import io.micronaut.core.io.scan.ClassPathResourceLoader;
import io.micronaut.core.io.service.ServiceDefinition;
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.ClassLoadingReporter;
import io.micronaut.core.reflect.ClassUtils;
import io.micronaut.core.reflect.GenericTypeUtils;
import io.micronaut.core.reflect.ReflectionUtils;
import io.micronaut.core.type.Argument;
import io.micronaut.core.type.ReturnType;
import io.micronaut.core.util.*;
import io.micronaut.core.util.clhm.ConcurrentLinkedHashMap;
import io.micronaut.inject.*;
import io.micronaut.inject.qualifiers.Qualified;
import io.micronaut.inject.qualifiers.Qualifiers;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.inject.Provider;
import javax.inject.Scope;
import javax.inject.Singleton;
import java.io.Closeable;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Function;
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 BeanContext {
protected static final Logger LOG = LoggerFactory.getLogger(DefaultBeanContext.class);
private static final Logger EVENT_LOGGER = LoggerFactory.getLogger(ApplicationEventPublisher.class);
private static final Qualifier PROXY_TARGET_QUALIFIER = Qualifiers.byType(ProxyTarget.class);
private static final String SCOPED_PROXY_ANN = "io.micronaut.runtime.context.scope.ScopedProxy";
private static final String AROUND_TYPE = "io.micronaut.aop.Around";
private static final String INTRODUCTION_TYPE = "io.micronaut.aop.Introduction";
private static final String NAMED_MEMBER = "named";
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 scopedProxies = new ConcurrentHashMap<>(20);
Collection> beanInitializedEventListeners;
private final Collection beanDefinitionsClasses = new ConcurrentLinkedQueue<>();
private final Map beanConfigurations = new ConcurrentHashMap<>(4);
private final Map containsBeanCache = new ConcurrentHashMap<>(30);
private final Map> initializedObjectsByType = new ConcurrentLinkedHashMap.Builder>().maximumWeightedCapacity(30).build();
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 ConcurrentLinkedHashMap.Builder>().maximumWeightedCapacity(10).build();
private final ClassLoader classLoader;
private final Set thisInterfaces = ReflectionUtils.getAllInterfaces(getClass());
private final Set indexedTypes = CollectionUtils.setOf(
TypeConverter.class,
TypeConverterRegistrar.class,
ApplicationEventListener.class,
BeanCreatedEventListener.class,
BeanInitializedEventListener.class
);
private final CustomScopeRegistry customScopeRegistry;
private Collection> beanCreationEventListeners;
/**
* 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 = new DefaultCustomScopeRegistry(this, classLoader);
// startup optimization.. index Jackson modules
ClassUtils.forName("com.fasterxml.jackson.databind.Module", classLoader).ifPresent(indexedTypes::add);
}
@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");
}
readAllBeanConfigurations();
readAllBeanDefinitionClasses();
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));
}
// start thread for parallel beans
processParallelBeans();
running.set(true);
initializing.set(false);
}
return this;
}
/**
* The close method will shut down the context calling {@link javax.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));
// 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;
}
processed.add(sysId);
if (def instanceof DisposableBeanDefinition) {
try {
//noinspection unchecked
((DisposableBeanDefinition) def).dispose(this, bean);
} catch (Throwable e) {
if (LOG.isErrorEnabled()) {
LOG.error("Error disposing of bean registration [" + def.getName() + "]: " + e.getMessage(), e);
}
}
}
if (def instanceof Closeable) {
try {
((Closeable) def).close();
} catch (Throwable e) {
if (LOG.isErrorEnabled()) {
LOG.error("Error disposing of bean registration [" + def.getName() + "]: " + e.getMessage(), e);
}
}
}
if (bean instanceof LifeCycle) {
((LifeCycle) bean).stop();
}
}
terminating.set(false);
running.set(false);
ClassLoadingReporter.finish();
}
return this;
}
@Override
@Nonnull
public AnnotationMetadata resolveMetadata(Class> type) {
if (type == null) {
return AnnotationMetadata.EMPTY_METADATA;
} else {
Optional extends BeanDefinition>> candidate = findConcreteCandidate(type, null, false, 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 (Collection>) 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 (Collection>) result;
}
@Override
public Collection> getBeanRegistrations(Class beanType) {
if (beanType == null) {
return Collections.emptyList();
}
// initialize the beans
getBeansOfType(beanType);
return getActiveBeanRegistrations(beanType);
}
@Override
public Optional> findBeanRegistration(T bean) {
for (BeanRegistration beanRegistration : singletonObjects.values()) {
if (bean == beanRegistration.getBean()) {
return Optional.of(beanRegistration);
}
}
Collection scopes = getBeansOfType(CustomScope.class);
for (CustomScope> scope : scopes) {
Optional> beanRegistration = scope.findBeanRegistration(bean);
if (beanRegistration.isPresent()) {
return beanRegistration;
}
}
return Optional.empty();
}
@SuppressWarnings("unchecked")
@Override
public Optional> findExecutionHandle(Class beanType, String method, Class... arguments) {
return findExecutionHandle(beanType, null, method, arguments);
}
@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(Class type, T singleton, Qualifier qualifier, boolean inject) {
if (singleton == null) {
throw new IllegalArgumentException("Passed singleton cannot be null");
}
BeanKey beanKey = new BeanKey<>(type, qualifier);
synchronized (singletonObjects) {
initializedObjectsByType.clear();
BeanDefinition beanDefinition = inject ? findBeanCandidatesForInstance(singleton).stream().findFirst().orElse(null) : null;
if (beanDefinition != null && beanDefinition.getBeanType().isInstance(singleton)) {
doInject(new DefaultBeanResolutionContext(this, beanDefinition), singleton, beanDefinition);
singletonObjects.put(beanKey, new BeanRegistration<>(beanKey, beanDefinition, singleton));
} else {
NoInjectionBeanDefinition dynamicRegistration = new NoInjectionBeanDefinition<>(type);
beanDefinition = dynamicRegistration;
beanDefinitionsClasses.add(dynamicRegistration);
singletonObjects.put(beanKey, new BeanRegistration<>(beanKey, dynamicRegistration, singleton));
}
final Optional indexedType = indexedTypes.stream().filter(t -> t.isAssignableFrom(type) || t == type).findFirst();
if (indexedType.isPresent()) {
final Collection indexed = resolveTypeIndex(indexedType.get());
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;
}
});
}
}
return this;
}
@Override
public ClassLoader getClassLoader() {
return classLoader;
}
@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 Optional> findBeanDefinition(Class beanType, Qualifier qualifier) {
if (Object.class == beanType) {
// 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(beanType));
if (qualifier != null) {
beanCandidates = qualifier.reduce(beanType, beanCandidates.stream()).collect(Collectors.toList());
}
filterProxiedTypes(beanCandidates, true, true);
if (beanCandidates.isEmpty()) {
return Optional.empty();
} else {
if (beanCandidates.size() == 1) {
return Optional.of(beanCandidates.iterator().next());
} else {
return findConcreteCandidate(beanType, null, false, true);
}
}
}
@Override
public Collection> getBeanDefinitions(Class beanType) {
Collection> candidates = findBeanCandidatesInternal(beanType);
return Collections.unmodifiableCollection(candidates);
}
@Override
public Collection> getBeanDefinitions(Class beanType, Qualifier qualifier) {
Collection> candidates = findBeanCandidatesInternal(beanType);
if (qualifier != null) {
candidates = qualifier.reduce(beanType, new ArrayList<>(candidates).stream()).collect(Collectors.toList());
}
return Collections.unmodifiableCollection(candidates);
}
@Override
public boolean containsBean(Class beanType, Qualifier qualifier) {
BeanKey beanKey = new BeanKey<>(beanType, qualifier);
if (containsBeanCache.containsKey(beanKey)) {
return containsBeanCache.get(beanKey);
} else {
boolean result = singletonObjects.containsKey(beanKey) ||
isCandidatePresent(beanType, qualifier);
containsBeanCache.put(beanKey, result);
return result;
}
}
@Override
public T getBean(Class beanType, Qualifier qualifier) {
return getBeanInternal(null, beanType, qualifier, true, true);
}
@Override
public T getBean(Class beanType) {
return getBeanInternal(null, beanType, null, true, true);
}
@Override
public Optional findBean(Class beanType, Qualifier qualifier) {
return findBean(null, beanType, qualifier);
}
@Override
public Collection getBeansOfType(Class beanType) {
return getBeansOfType(null, beanType);
}
@Override
public Collection getBeansOfType(Class beanType, Qualifier qualifier) {
return getBeansOfTypeInternal(null, beanType, qualifier);
}
@Override
public Stream streamOfType(Class 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 getBeansOfTypeInternal(resolutionContext, beanType, qualifier).stream();
}
@Override
public T inject(T instance) {
Objects.requireNonNull(instance, "Instance cannot be null");
Collection candidates = findBeanCandidatesForInstance(instance);
if (candidates.size() == 1) {
BeanDefinition beanDefinition = candidates.stream().findFirst().get();
final DefaultBeanResolutionContext resolutionContext = new DefaultBeanResolutionContext(this, beanDefinition);
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 T createBean(Class beanType, Qualifier qualifier) {
return createBean(null, beanType, qualifier);
}
@Override
public T createBean(Class beanType, Qualifier qualifier, Map argumentValues) {
Optional> candidate = findConcreteCandidate(beanType, qualifier, true, false);
if (candidate.isPresent()) {
T createdBean = doCreateBean(new DefaultBeanResolutionContext(this, candidate.get()), candidate.get(), qualifier, false, argumentValues);
if (createdBean == null) {
throw new NoSuchBeanException(beanType);
}
return createdBean;
}
throw new NoSuchBeanException(beanType);
}
@Override
public T createBean(Class beanType, Qualifier qualifier, Object... args) {
Optional> candidate = findConcreteCandidate(beanType, qualifier, true, false);
if (candidate.isPresent()) {
BeanDefinition definition = candidate.get();
DefaultBeanResolutionContext resolutionContext = new DefaultBeanResolutionContext(this, definition);
return doCreateBean(resolutionContext, definition, 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 T doCreateBean(BeanResolutionContext resolutionContext, BeanDefinition definition, Class beanType, Qualifier qualifier, 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 path = resolutionContext.getPath();
for (int i = 0; i < requiredArguments.length; i++) {
Argument> requiredArgument = requiredArguments[i];
try {
path.pushConstructorResolve(
definition, requiredArgument
);
if (args.length > i) {
Object val = args[i];
if (val != null) {
argumentValues.put(requiredArgument.getName(), ConversionService.SHARED.convert(val, requiredArgument).orElseThrow(() ->
new BeanInstantiationException(resolutionContext, "Invalid bean @Argument [" + requiredArgument + "]. Cannot convert object [" + val + "] to required type: " + requiredArgument.getType())
));
} else {
if (!requiredArgument.getAnnotationMetadata().hasDeclaredAnnotation(Nullable.class)) {
throw new BeanInstantiationException(resolutionContext, "Invalid bean @Argument [" + requiredArgument + "]. Argument cannot be null");
}
}
} else {
// attempt resolve from context
Optional> existingBean = findBean(resolutionContext, requiredArgument.getType(), null);
if (existingBean.isPresent()) {
argumentValues.put(requiredArgument.getName(), existingBean.get());
} else {
if (!requiredArgument.getAnnotationMetadata().hasDeclaredAnnotation(Nullable.class)) {
throw new BeanInstantiationException(resolutionContext, "Invalid bean @Argument [" + requiredArgument + "]. No bean found for type: " + requiredArgument.getType());
}
}
}
} finally {
path.pop();
}
}
} else {
argumentValues = Collections.emptyMap();
}
if (LOG.isTraceEnabled()) {
LOG.trace("Computed bean argument values: {}", argumentValues);
}
T createdBean = doCreateBean(resolutionContext, definition, qualifier, false, argumentValues);
if (createdBean == null) {
throw new NoSuchBeanException(beanType);
}
return createdBean;
}
@Override
public T destroyBean(Class beanType) {
T bean = null;
BeanKey beanKey = new BeanKey<>(beanType, null);
synchronized (singletonObjects) {
if (singletonObjects.containsKey(beanKey)) {
@SuppressWarnings("unchecked") BeanRegistration beanRegistration = singletonObjects.get(beanKey);
bean = beanRegistration.bean;
if (bean != null) {
singletonObjects.remove(beanKey);
BeanKey> concreteKey = new BeanKey<>(bean.getClass(), null);
singletonObjects.remove(concreteKey);
}
}
}
if (bean != null) {
Optional> concreteCandidate = findConcreteCandidate(beanType, null, false, true);
T finalBean = bean;
concreteCandidate.ifPresent(definition -> {
if (definition instanceof DisposableBeanDefinition) {
((DisposableBeanDefinition) definition).dispose(this, finalBean);
}
}
);
}
return bean;
}
/**
* 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 T createBean(BeanResolutionContext resolutionContext, Class beanType, Qualifier qualifier) {
Optional> concreteCandidate = findConcreteCandidate(beanType, qualifier, true, false);
if (concreteCandidate.isPresent()) {
BeanDefinition candidate = concreteCandidate.get();
if (resolutionContext == null) {
resolutionContext = new DefaultBeanResolutionContext(this, candidate);
}
T createBean = doCreateBean(resolutionContext, candidate, qualifier, 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 T inject(BeanResolutionContext resolutionContext, BeanDefinition requestingBeanDefinition, T instance) {
@SuppressWarnings("unchecked") Class beanType = (Class) instance.getClass();
Optional> concreteCandidate = findConcreteCandidate(beanType, null, false, true);
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 Collection getBeansOfType(BeanResolutionContext resolutionContext, Class beanType) {
return getBeansOfTypeInternal(resolutionContext, beanType, null);
}
/**
* Get all beans of the given type and qualifier.
*
* @param resolutionContext The bean resolution context
* @param beanType The bean type
* @param qualifier The qualifier
* @param The bean type parameter
* @return The found beans
*/
protected Collection getBeansOfType(BeanResolutionContext resolutionContext, Class beanType, Qualifier qualifier) {
return getBeansOfTypeInternal(resolutionContext, beanType, qualifier);
}
/**
* Get provided 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 Provider getBeanProvider(BeanResolutionContext resolutionContext, Class beanType) {
return getBeanProvider(resolutionContext, beanType, null);
}
@SuppressWarnings("unchecked")
@Override
public T getProxyTargetBean(Class beanType, Qualifier qualifier) {
Qualifier proxyQualifier = qualifier != null ? Qualifiers.byQualifiers(qualifier, PROXY_TARGET_QUALIFIER) : PROXY_TARGET_QUALIFIER;
BeanDefinition definition = getProxyTargetBeanDefinition(beanType, qualifier);
return getBeanForDefinition(new DefaultBeanResolutionContext(this, definition), beanType, proxyQualifier, true, definition);
}
@Override
public Optional> findProxyTargetMethod(Class beanType, String method, Class[] arguments) {
BeanDefinition definition = getProxyTargetBeanDefinition(beanType, null);
return definition.findMethod(method, arguments);
}
@Override
@SuppressWarnings("unchecked")
public Optional> findProxyTargetBeanDefinition(Class beanType, Qualifier qualifier) {
Qualifier proxyQualifier = qualifier != null ? Qualifiers.byQualifiers(qualifier, PROXY_TARGET_QUALIFIER) : PROXY_TARGET_QUALIFIER;
BeanKey key = new BeanKey(beanType, proxyQualifier);
return (Optional) beanConcreteCandidateCache.computeIfAbsent(key, beanKey -> {
BeanRegistration beanRegistration = singletonObjects.get(beanKey);
if (beanRegistration != null) {
if (LOG.isDebugEnabled()) {
LOG.debug("Resolved existing bean [{}] for type [{}] and qualifier [{}]", beanRegistration.bean, beanType, qualifier);
}
return Optional.of(beanRegistration.beanDefinition);
}
return findConcreteCandidateNoCache((Class) beanType, qualifier, true, false, false);
});
}
@SuppressWarnings("unchecked")
@Override
public Collection> getBeanDefinitions(Qualifier qualifier) {
if (qualifier == null) {
return Collections.emptyList();
}
if (LOG.isDebugEnabled()) {
LOG.debug("Finding candidate beans for qualifier: {}", qualifier);
}
// first traverse component definition classes and load candidates
Collection candidates;
if (!beanDefinitionsClasses.isEmpty()) {
Stream reduced = qualifier.reduce(Object.class, beanDefinitionsClasses.stream());
Stream candidateStream = qualifier.reduce(Object.class,
reduced
.map(ref -> ref.load(this))
.filter(candidate -> candidate.isEnabled(this))
);
candidates = candidateStream.collect(Collectors.toList());
} else {
return (Collection>) Collections.EMPTY_LIST;
}
if (CollectionUtils.isNotEmpty(candidates)) {
filterProxiedTypes(candidates, true, true);
filterReplacedBeans(candidates);
}
return candidates;
}
@SuppressWarnings("unchecked")
@Override
public Collection> getAllBeanDefinitions() {
if (LOG.isDebugEnabled()) {
LOG.debug("Finding all bean definitions");
}
if (!beanDefinitionsClasses.isEmpty()) {
List collection = beanDefinitionsClasses
.stream()
.map(ref -> ref.load(this))
.filter(candidate -> candidate.isEnabled(this))
.collect(Collectors.toList());
return (Collection>) collection;
}
return (Collection>) Collections.EMPTY_MAP;
}
@SuppressWarnings("unchecked")
@Override
public Collection> getBeanDefinitionReferences() {
if (!beanDefinitionsClasses.isEmpty()) {
final List refs = beanDefinitionsClasses.stream().filter(ref -> ref.isEnabled(this))
.collect(Collectors.toList());
return (Collection>) Collections.unmodifiableList(refs);
}
return Collections.emptyList();
}
/**
* Get a bean of the given type.
*
* @param resolutionContext The bean context resolution
* @param beanType The bean type
* @param The bean type parameter
* @return The found bean
*/
@UsedByGeneratedCode
public T getBean(BeanResolutionContext resolutionContext, Class beanType) {
return getBeanInternal(resolutionContext, beanType, null, true, true);
}
/**
* Get a bean of the given type and qualifier.
*
* @param resolutionContext The bean context resolution
* @param beanType The bean type
* @param qualifier The qualifier
* @param The bean type parameter
* @return The found bean
*/
public T getBean(BeanResolutionContext resolutionContext, Class beanType, Qualifier qualifier) {
return getBeanInternal(resolutionContext, beanType, qualifier, true, true);
}
/**
* Find an optional bean of the given type and qualifier.
*
* @param resolutionContext The bean context resolution
* @param beanType The bean type
* @param qualifier The qualifier
* @param The bean type parameter
* @return The found bean wrapped as an {@link Optional}
*/
public Optional findBean(BeanResolutionContext resolutionContext, Class beanType, Qualifier qualifier) {
// allow injection the bean context
if (thisInterfaces.contains(beanType)) {
return Optional.of((T) this);
}
T bean = getBeanInternal(resolutionContext, beanType, qualifier, true, false);
if (bean == null) {
return Optional.empty();
} else {
return Optional.of(bean);
}
}
@SuppressWarnings("unchecked")
@Override
public void publishEvent(Object event) {
if (event != null) {
if (EVENT_LOGGER.isDebugEnabled()) {
EVENT_LOGGER.debug("Publishing event: {}", event);
}
Collection eventListeners = getBeansOfType(ApplicationEventListener.class, Qualifiers.byTypeArguments(event.getClass()));
eventListeners = eventListeners.stream().sorted(OrderUtil.COMPARATOR).collect(Collectors.toList());
if (!eventListeners.isEmpty()) {
if (EVENT_LOGGER.isTraceEnabled()) {
EVENT_LOGGER.trace("Established event listeners {} for event: {}", eventListeners, event);
}
eventListeners
.forEach(listener -> {
if (listener.supports(event)) {
try {
if (EVENT_LOGGER.isTraceEnabled()) {
EVENT_LOGGER.trace("Invoking event listener [{}] for event: {}", listener, event);
}
listener.onApplicationEvent(event);
} catch (ClassCastException ex) {
String msg = ex.getMessage();
if (msg == null || msg.startsWith(event.getClass().getName())) {
if (EVENT_LOGGER.isDebugEnabled()) {
EVENT_LOGGER.debug("Incompatible listener for event: " + listener, ex);
}
} else {
throw ex;
}
}
}
}
);
}
}
}
/**
* Invalidates the bean caches.
*/
protected void invalidateCaches() {
beanCandidateCache.clear();
initializedObjectsByType.clear();
}
/**
* Get a bean provider.
*
* @param resolutionContext The bean resolution context
* @param beanType The bean type
* @param qualifier The qualifier
* @param The bean type parameter
* @return The bean provider
*/
protected Provider getBeanProvider(BeanResolutionContext resolutionContext, Class beanType, Qualifier qualifier) {
@SuppressWarnings("unchecked") BeanRegistration beanRegistration = singletonObjects.get(new BeanKey(beanType, qualifier));
if (beanRegistration != null) {
return new ResolvedProvider<>(beanRegistration.bean);
}
Optional> concreteCandidate = findConcreteCandidate(beanType, qualifier, true, false);
if (concreteCandidate.isPresent()) {
return new UnresolvedProvider<>(beanType, qualifier, this);
} else {
throw new NoSuchBeanException(beanType);
}
}
/**
* Resolves the {@link BeanDefinitionReference} class instances. Default implementation uses ServiceLoader pattern.
*
* @return The bean definition classes
*/
protected List resolveBeanDefinitionReferences() {
final SoftServiceLoader definitions = SoftServiceLoader.load(BeanDefinitionReference.class, classLoader);
List list = new ArrayList<>(300);
for (ServiceDefinition definition : definitions) {
if (definition.isPresent()) {
final BeanDefinitionReference ref = definition.load();
list.add(ref);
}
}
return list;
}
/**
* Resolves the {@link BeanConfiguration} class instances. Default implementation uses ServiceLoader pattern.
*
* @return The bean definition classes
*/
protected Iterable resolveBeanConfigurations() {
final SoftServiceLoader definitions = SoftServiceLoader.load(BeanConfiguration.class, classLoader);
List list = new ArrayList<>(20);
for (ServiceDefinition definition : definitions) {
if (definition.isPresent()) {
list.add(definition.load());
}
}
return list;
}
/**
* Initialize the event listeners.
*/
protected void initializeEventListeners() {
this.beanCreationEventListeners = getBeanRegistrations(BeanCreatedEventListener.class);
this.beanInitializedEventListeners = getBeanRegistrations(BeanInitializedEventListener.class);
}
/**
* Initialize the context with the given {@link io.micronaut.context.annotation.Context} scope beans.
*
* @param contextScopeBeans The context scope beans
* @param processedBeans The beans that require {@link ExecutableMethodProcessor} handling
*/
protected void initializeContext(
List contextScopeBeans,
List processedBeans) {
if (CollectionUtils.isNotEmpty(contextScopeBeans)) {
filterReplacedBeans((Collection) contextScopeBeans);
for (BeanDefinitionReference contextScopeBean : contextScopeBeans) {
try {
loadContextScopeBean(contextScopeBean);
} catch (Throwable e) {
throw new BeanInstantiationException("Bean definition [" + contextScopeBean.getName() + "] could not be loaded: " + e.getMessage(), e);
}
}
}
if (!processedBeans.isEmpty()) {
@SuppressWarnings("unchecked") Stream> methodStream = processedBeans
.stream()
// is the bean reference enabled
.filter(ref -> ref.isEnabled(this))
// ok - continue and load it
.map((Function>) reference -> {
try {
return reference.load(this);
} catch (Exception e) {
throw new BeanInstantiationException("Bean definition [" + reference.getName() + "] could not be loaded: " + e.getMessage(), e);
}
})
// is the bean itself enabled
.filter(bean -> bean.isEnabled(this))
// ok continue and get all of the ExecutableMethod references
.flatMap(beanDefinition ->
beanDefinition.getExecutableMethods()
.parallelStream()
.map((Function, BeanDefinitionMethodReference, ?>>) executableMethod ->
BeanDefinitionMethodReference.of((BeanDefinition) beanDefinition, executableMethod)
)
);
// group the method references by annotation type such that we have a map of Annotation -> MethodReference
// ie. Class -> @Scheduled void someAnnotation()
Map, List>> byAnnotation = methodStream
.collect(
Collectors.groupingBy((Function, Class extends Annotation>>) executableMethod ->
executableMethod.getAnnotationTypeByStereotype(Executable.class)
.orElseThrow(() ->
new IllegalStateException("BeanDefinition.requiresMethodProcessing() returned true but method has no @Executable definition. This should never happen. Please report an issue.")
)));
// Find ExecutableMethodProcessor for each annotation and process the BeanDefinitionMethodReference
for (Map.Entry, List>> entry : byAnnotation.entrySet()) {
Class extends Annotation> annotationType = entry.getKey();
streamOfType(ExecutableMethodProcessor.class, Qualifiers.byTypeArguments(annotationType))
.forEach(processor -> {
for (BeanDefinitionMethodReference, ?> method : entry.getValue()) {
BeanDefinition> beanDefinition = method.getBeanDefinition();
// Only process the method if the the annotation is not declared at the class level
// If declared at the class level it will already have been processed by AnnotationProcessorListener
if (!beanDefinition.hasStereotype(annotationType)) {
//noinspection unchecked
if (method.hasDeclaredStereotype(Parallel.class)) {
ForkJoinPool.commonPool().execute(() -> {
try {
processor.process(beanDefinition, method);
} catch (Throwable e) {
if (LOG.isErrorEnabled()) {
LOG.error("Error processing bean method " + beanDefinition + "." + method + " with processor (" + processor + "): " + e.getMessage(), e);
}
Boolean shutdownOnError = method.getValue(Parallel.class, "shutdownOnError", Boolean.class).orElse(true);
if (shutdownOnError) {
stop();
}
}
});
} else {
processor.process(beanDefinition, method);
}
}
}
if (processor instanceof Completable) {
((Completable) processor).onComplete();
}
});
}
}
final Runnable runnable = () ->
beanDefinitionsClasses.removeIf((BeanDefinitionReference beanDefinitionReference) ->
!beanDefinitionReference.isEnabled(this));
if (ClassLoadingReporter.isReportingEnabled()) {
// do this in a blocking manner so that reporting is immediately aware of missing classes
runnable.run();
} else {
// proactively remove bean definitions that are not enabled
new Thread(runnable).start();
}
}
/**
* Find bean candidates for the given type.
*
* @param beanType The bean type
* @param filter A bean definition to filter out
* @param The bean generic type
* @return The candidates
*/
@SuppressWarnings("unchecked")
protected Collection> findBeanCandidates(Class beanType, BeanDefinition> filter) {
if (LOG.isDebugEnabled()) {
LOG.debug("Finding candidate beans for type: {}", beanType);
}
// first traverse component definition classes and load candidates
Collection beanDefinitionsClasses;
if (indexedTypes.contains(beanType)) {
beanDefinitionsClasses = beanIndex.get(beanType);
if (beanDefinitionsClasses == null) {
beanDefinitionsClasses = Collections.emptyList();
}
} else {
beanDefinitionsClasses = this.beanDefinitionsClasses;
}
if (!beanDefinitionsClasses.isEmpty()) {
Stream> candidateStream = beanDefinitionsClasses
.stream()
.filter(reference -> {
if (reference.isPresent()) {
Class> candidateType = reference.getBeanType();
final boolean isCandidate = candidateType != null && (beanType.isAssignableFrom(candidateType) || beanType == candidateType);
return isCandidate && reference.isEnabled(this);
}
return false;
})
.map(ref -> {
BeanDefinition loadedBean;
try {
loadedBean = ref.load(this);
} catch (Throwable e) {
throw new BeanContextException("Error loading bean [" + ref.getName() + "]: " + e.getMessage(), e);
}
return loadedBean;
});
if (filter != null) {
candidateStream = candidateStream.filter(candidate -> !candidate.equals(filter));
}
List> candidates = candidateStream
.filter(candidate -> candidate.isEnabled(this))
.collect(Collectors.toList());
if (!candidates.isEmpty()) {
filterReplacedBeans(candidates);
}
if (LOG.isDebugEnabled()) {
LOG.debug("Resolved bean candidates {} for type: {}", candidates, beanType);
}
return candidates;
} else {
if (LOG.isDebugEnabled()) {
LOG.debug("No bean candidates found for type: {}", beanType);
}
return Collections.emptySet();
}
}
/**
* Find bean candidates for the given type.
*
* @param instance The bean instance
* @param The bean generic type
* @return The candidates
*/
protected Collection findBeanCandidatesForInstance(T instance) {
if (LOG.isDebugEnabled()) {
LOG.debug("Finding candidate beans for instance: {}", instance);
}
Collection beanDefinitionsClasses = this.beanDefinitionsClasses;
return beanCandidateCache.computeIfAbsent(instance.getClass(), aClass -> {
// first traverse component definition classes and load candidates
if (!beanDefinitionsClasses.isEmpty()) {
List candidates = beanDefinitionsClasses
.stream()
.filter(reference -> {
if (reference.isEnabled(this)) {
Class> candidateType = reference.getBeanType();
return candidateType != null && candidateType.isInstance(instance);
} else {
return false;
}
})
.map(ref -> ref.load(this))
.filter(candidate -> candidate.isEnabled(this))
.collect(Collectors.toList());
if (candidates.size() > 1) {
// try narrow to exact type
candidates = candidates
.stream()
.filter(candidate ->
!(candidate instanceof NoInjectionBeanDefinition) &&
candidate.getBeanType() == instance.getClass()
)
.collect(Collectors.toList());
return candidates;
}
if (LOG.isDebugEnabled()) {
LOG.debug("Resolved bean candidates {} for instance: {}", candidates, instance);
}
return candidates;
} else {
if (LOG.isDebugEnabled()) {
LOG.debug("No bean candidates found for instance: {}", instance);
}
return Collections.emptySet();
}
});
}
/**
* Registers an active configuration.
*
* @param configuration The configuration to register
*/
protected void registerConfiguration(BeanConfiguration configuration) {
beanConfigurations.put(configuration.getName(), configuration);
ClassLoadingReporter.reportPresent(configuration.getClass());
}
/**
* Execution the creation of a bean.
*
* @param resolutionContext The {@link BeanResolutionContext}
* @param beanDefinition The {@link BeanDefinition}
* @param qualifier The {@link Qualifier}
* @param isSingleton Whether the bean is a singleton
* @param argumentValues Any argument values passed to create the bean
* @param The bean generic type
* @return The created bean
*/
protected T doCreateBean(@Nonnull BeanResolutionContext resolutionContext,
BeanDefinition beanDefinition,
Qualifier qualifier,
boolean isSingleton,
Map argumentValues) {
BeanRegistration beanRegistration = isSingleton && !beanDefinition.isIterable() ? singletonObjects.get(new BeanKey(beanDefinition, qualifier)) : null;
T bean;
if (beanRegistration != null) {
return beanRegistration.bean;
}
if (resolutionContext == null) {
resolutionContext = new DefaultBeanResolutionContext(this, beanDefinition);
}
if (beanDefinition instanceof BeanFactory) {
BeanFactory beanFactory = (BeanFactory) beanDefinition;
try {
if (beanFactory instanceof ParametrizedBeanFactory) {
ParametrizedBeanFactory parametrizedBeanFactory = (ParametrizedBeanFactory) beanFactory;
Argument>[] requiredArguments = parametrizedBeanFactory.getRequiredArguments();
if (argumentValues == null) {
throw new BeanInstantiationException(resolutionContext, "Missing bean arguments for type: " + beanDefinition.getBeanType().getName() + ". Requires arguments: " + ArrayUtils.toString(requiredArguments));
}
Map convertedValues = new LinkedHashMap<>(argumentValues);
for (Argument> requiredArgument : requiredArguments) {
Object val = argumentValues.get(requiredArgument.getName());
if (val == null && !requiredArgument.getAnnotationMetadata().hasDeclaredAnnotation(Nullable.class)) {
throw new BeanInstantiationException(resolutionContext, "Missing bean argument [" + requiredArgument + "].");
}
BeanResolutionContext finalResolutionContext = resolutionContext;
Object convertedValue = null;
if (val != null) {
convertedValue = ConversionService.SHARED.convert(val, requiredArgument).orElseThrow(() ->
new BeanInstantiationException(finalResolutionContext, "Invalid bean argument [" + requiredArgument + "]. Cannot convert object [" + val + "] to required type: " + requiredArgument.getType())
);
}
convertedValues.put(requiredArgument.getName(), convertedValue);
}
bean = parametrizedBeanFactory.build(
resolutionContext,
this,
beanDefinition,
convertedValues
);
} else {
bean = beanFactory.build(resolutionContext, this, beanDefinition);
if (bean == null) {
throw new BeanInstantiationException(resolutionContext, "Bean Factory [" + beanFactory + "] returned null");
}
}
} catch (Throwable e) {
if (e instanceof DependencyInjectionException) {
throw e;
}
if (e instanceof BeanInstantiationException) {
throw e;
} else {
if (!resolutionContext.getPath().isEmpty()) {
throw new BeanInstantiationException(resolutionContext, e);
} else {
throw new BeanInstantiationException(beanDefinition, e);
}
}
}
} else {
ConstructorInjectionPoint constructor = beanDefinition.getConstructor();
Argument[] requiredConstructorArguments = constructor.getArguments();
if (requiredConstructorArguments.length == 0) {
bean = constructor.invoke();
} else {
Object[] constructorArgs = new Object[requiredConstructorArguments.length];
for (int i = 0; i < requiredConstructorArguments.length; i++) {
Class argument = requiredConstructorArguments[i].getType();
constructorArgs[i] = getBean(resolutionContext, argument);
}
bean = constructor.invoke(constructorArgs);
}
inject(resolutionContext, null, bean);
}
if (!BeanCreatedEventListener.class.isInstance(bean)) {
if (CollectionUtils.isNotEmpty(beanCreationEventListeners)) {
BeanKey beanKey = new BeanKey(beanDefinition, qualifier);
for (BeanRegistration registration : beanCreationEventListeners) {
BeanDefinition definition = registration.getBeanDefinition();
List> typeArguments = definition.getTypeArguments(BeanCreatedEventListener.class);
if (CollectionUtils.isEmpty(typeArguments) || typeArguments.get(0).getType().isAssignableFrom(beanDefinition.getBeanType())) {
BeanCreatedEventListener listener = registration.getBean();
bean = (T) listener.onCreated(new BeanCreatedEvent(this, beanDefinition, beanKey, bean));
if (bean == null) {
throw new BeanInstantiationException(resolutionContext, "Listener [" + listener + "] returned null from onCreated event");
}
}
}
}
}
if (beanDefinition instanceof ValidatedBeanDefinition) {
bean = ((ValidatedBeanDefinition) beanDefinition).validate(resolutionContext, bean);
}
if (LOG.isDebugEnabled()) {
LOG.debug("Created bean [{}] from definition [{}] with qualifier [{}]", bean, beanDefinition, qualifier);
}
return bean;
}
/**
* Fall back method to attempt to find a candidate for the given definitions.
*
* @param beanType The bean type
* @param qualifier The qualifier
* @param candidates The candidates
* @param