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-2020 original authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.micronaut.context;
import io.micronaut.context.annotation.*;
import io.micronaut.context.env.PropertyPlaceholderResolver;
import io.micronaut.context.event.*;
import io.micronaut.context.exceptions.*;
import io.micronaut.context.processor.AnnotationProcessor;
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.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.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.ClassUtils;
import io.micronaut.core.reflect.GenericTypeUtils;
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.core.value.PropertyResolver;
import io.micronaut.core.value.ValueResolver;
import io.micronaut.inject.*;
import io.micronaut.inject.qualifiers.Qualified;
import io.micronaut.inject.qualifiers.Qualifiers;
import io.micronaut.inject.validation.BeanDefinitionValidator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.micronaut.core.annotation.NonNull;
import io.micronaut.core.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.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Consumer;
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);
protected static final Logger LOG_LIFECYCLE = LoggerFactory.getLogger(DefaultBeanContext.class.getPackage().getName() + ".lifecycle");
private static final Logger EVENT_LOGGER = LoggerFactory.getLogger(ApplicationEventPublisher.class);
private static final Qualifier PROXY_TARGET_QUALIFIER = new Qualifier() {
@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 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,
ApplicationEventPublisher.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;
/**
* 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);
Set> eagerInitAnnotated = contextConfiguration.getEagerInitAnnotated();
this.eagerInitStereotypes = eagerInitAnnotated
.stream().map(Class::getName).toArray(String[]::new);
this.eagerInitStereotypesPresent = eagerInitStereotypes.length > 0;
this.eagerInitSingletons = eagerInitStereotypesPresent && eagerInitAnnotated.contains(Singleton.class);
}
@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));
}
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));
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);
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);
}
return this;
}
@Override
@NonNull
public AnnotationMetadata resolveMetadata(Class> type) {
if (type == null) {
return AnnotationMetadata.EMPTY_METADATA;
} else {
Optional extends BeanDefinition>> candidate = findConcreteCandidate(null, 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 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, beanType, null);
}
@Override
public BeanRegistration getBeanRegistration(Class beanType, Qualifier qualifier) {
return getBeanRegistration(null, beanType, qualifier);
}
@Override
public Collection> getBeanRegistrations(Class beanType, Qualifier qualifier) {
if (beanType == null) {
return Collections.emptyList();
}
return getBeanRegistrations(null, beanType, null);
}
@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();
}
@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,
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(type);
BeanDefinition beanDefinition = inject ? findConcreteCandidate(null, type, qualifier, false, false).orElse(null) : null;
if (beanDefinition != null && beanDefinition.getBeanType().isInstance(singleton)) {
try (BeanResolutionContext context = newResolutionContext(beanDefinition, null)) {
doInject(context, singleton, beanDefinition);
}
singletonObjects.put(beanKey, new BeanRegistration<>(beanKey, beanDefinition, singleton));
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));
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;
}
@NonNull
private BeanResolutionContext newResolutionContext(BeanDefinition> beanDefinition, @Nullable BeanResolutionContext currentContext) {
if (currentContext == null) {
return new AbstractBeanResolutionContext(this, beanDefinition) {
@Override
public void addInFlightBean(BeanIdentifier beanIdentifier, T instance) {
singlesInCreation.put(beanIdentifier, instance);
}
@Override
public void removeInFlightBean(BeanIdentifier beanIdentifier) {
singlesInCreation.remove(beanIdentifier);
}
@Nullable
@Override
public T getInFlightBean(BeanIdentifier beanIdentifier) {
return (T) singlesInCreation.get(beanIdentifier);
}
};
} 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 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(null, 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(null, beanType, null, false, true);
}
}
}
@Override
public Collection> getBeanDefinitions(Class beanType) {
Collection> candidates = findBeanCandidatesInternal(null, beanType);
return Collections.unmodifiableCollection(candidates);
}
@Override
public Collection> getBeanDefinitions(Class beanType, Qualifier qualifier) {
Collection> candidates = findBeanCandidatesInternal(null, beanType);
if (qualifier != null) {
candidates = qualifier.reduce(beanType, new ArrayList<>(candidates).stream()).collect(Collectors.toList());
}
return Collections.unmodifiableCollection(candidates);
}
@Override
public boolean containsBean(@NonNull Class 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(beanType, qualifier);
containsBeanCache.put(beanKey, result);
return result;
}
}
@Override
public T getBean(Class beanType, Qualifier qualifier) {
try {
return getBeanInternal(null, 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 T getBean(Class beanType) {
try {
return getBeanInternal(null, 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 Collection getBeansOfType(Class beanType) {
return getBeansOfType(null, beanType);
}
@Override
public Collection getBeansOfType(Class beanType, Qualifier qualifier) {
return getBeanRegistrations(null, beanType, qualifier)
.stream()
.map(BeanRegistration::getBean)
.collect(Collectors.toList());
}
@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 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);
Optional> candidate = findConcreteCandidate(null, beanType, qualifier, true, false);
if (candidate.isPresent()) {
try (BeanResolutionContext resolutionContext = newResolutionContext(candidate.get(), null)) {
T createdBean = doCreateBean(resolutionContext, candidate.get(), qualifier, beanType, 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);
Optional> candidate = findConcreteCandidate(null, beanType, qualifier, true, false);
if (candidate.isPresent()) {
BeanDefinition definition = candidate.get();
try (BeanResolutionContext resolutionContext = newResolutionContext(definition, null)) {
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 @NonNull
T doCreateBean(@NonNull BeanResolutionContext resolutionContext,
@NonNull BeanDefinition definition,
@NonNull Class 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 path = resolutionContext.getPath();
for (int i = 0; i < requiredArguments.length; i++) {
Argument> requiredArgument = requiredArguments[i];
try {
path.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);
}
}
}
} finally {
path.pop();
}
}
} 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 @Nullable
T destroyBean(@NonNull Class beanType) {
ArgumentUtils.requireNonNull("beanType", 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) {
if (LOG_LIFECYCLE.isDebugEnabled()) {
LOG_LIFECYCLE.debug("Destroying bean [{}] with identifier [{}]", bean, beanKey);
}
singletonObjects.remove(beanKey);
BeanKey> concreteKey = new BeanKey<>(bean.getClass(), null);
singletonObjects.remove(concreteKey);
}
}
}
if (bean != null) {
Optional> concreteCandidate = findConcreteCandidate(null, beanType, null, false, true);
T finalBean = bean;
concreteCandidate.ifPresent(definition -> {
if (definition instanceof DisposableBeanDefinition) {
((DisposableBeanDefinition) definition).dispose(this, finalBean);
}
}
);
}
return bean;
}
/**
* Find an active {@link javax.inject.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);
Optional> concreteCandidate = findConcreteCandidate(resolutionContext, beanType, qualifier, true, false);
if (concreteCandidate.isPresent()) {
BeanDefinition candidate = concreteCandidate.get();
try (BeanResolutionContext context = newResolutionContext(candidate, resolutionContext)) {
T createBean = doCreateBean(context, candidate, qualifier, beanType, 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, 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 @NonNull
Collection getBeansOfType(@Nullable BeanResolutionContext resolutionContext, @NonNull Class 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
*/
protected @NonNull
Collection getBeansOfType(
@Nullable BeanResolutionContext resolutionContext,
@NonNull Class beanType,
@Nullable Qualifier qualifier) {
return getBeanRegistrations(resolutionContext, beanType, qualifier)
.stream().map(BeanRegistration::getBean)
.collect(Collectors.toList());
}
/**
* 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 @NonNull
Provider getBeanProvider(@Nullable BeanResolutionContext resolutionContext, @NonNull Class beanType) {
return getBeanProvider(resolutionContext, beanType, null);
}
@SuppressWarnings("unchecked")
@Override
public @NonNull
T getProxyTargetBean(@NonNull Class beanType, @Nullable Qualifier qualifier) {
ArgumentUtils.requireNonNull("beanType", beanType);
Qualifier proxyQualifier = qualifier != null ? Qualifiers.byQualifiers(qualifier, PROXY_TARGET_QUALIFIER) : PROXY_TARGET_QUALIFIER;
BeanDefinition definition = getProxyTargetBeanDefinition(beanType, qualifier);
try (BeanResolutionContext resolutionContext = newResolutionContext(definition, null)) {
return getBeanForDefinition(
resolutionContext,
beanType,
proxyQualifier,
true,
definition
);
}
}
@Override
public @NonNull
Optional> findProxyTargetMethod(@NonNull Class beanType, @NonNull String method, @NonNull Class[] arguments) {
ArgumentUtils.requireNonNull("beanType", beanType);
ArgumentUtils.requireNonNull("method", method);
BeanDefinition definition = getProxyTargetBeanDefinition(beanType, null);
return definition.findMethod(method, arguments);
}
@Override
public Optional> findProxyTargetMethod(Class beanType, Qualifier qualifier, String method, Class... arguments) {
ArgumentUtils.requireNonNull("beanType", beanType);
ArgumentUtils.requireNonNull("method", method);
BeanDefinition definition = getProxyTargetBeanDefinition(beanType, qualifier);
return definition.findMethod(method, arguments);
}
@Override
@SuppressWarnings({"unchecked", "rawtypes"})
public @NonNull
Optional> findProxyTargetBeanDefinition(@NonNull Class beanType, @Nullable Qualifier qualifier) {
ArgumentUtils.requireNonNull("beanType", beanType);
Qualifier proxyQualifier = qualifier != null ? Qualifiers.byQualifiers(qualifier, PROXY_TARGET_QUALIFIER) : PROXY_TARGET_QUALIFIER;
BeanKey key = new BeanKey(beanType, proxyQualifier);
Optional beanDefinition = beanConcreteCandidateCache.get(key);
//noinspection OptionalAssignedToNull
if (beanDefinition == null) {
BeanRegistration beanRegistration = singletonObjects.get(key);
if (beanRegistration != null) {
if (LOG.isDebugEnabled()) {
LOG.debug("Resolved existing bean [{}] for type [{}] and qualifier [{}]", beanRegistration.bean, beanType, qualifier);
}
beanDefinition = Optional.of(beanRegistration.beanDefinition);
} else {
beanDefinition = findConcreteCandidateNoCache(
null,
(Class) beanType,
proxyQualifier,
true,
false,
false
);
}
beanConcreteCandidateCache.put(key, beanDefinition);
}
return beanDefinition;
}
@SuppressWarnings("unchecked")
@Override
public @NonNull
Collection> getBeanDefinitions(@Nullable Qualifier qualifier) {
if (qualifier == null) {
return Collections.emptyList();
}
if (LOG.isDebugEnabled()) {
LOG.debug("Finding candidate beans for qualifier: {}", qualifier);
}
// first traverse component definition classes and load candidates
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 Collections.emptyList();
}
if (CollectionUtils.isNotEmpty(candidates)) {
filterProxiedTypes(candidates, true, true);
filterReplacedBeans(null, candidates);
}
return candidates;
}
@SuppressWarnings("unchecked")
@Override
public @NonNull
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;
}
return (Collection>) Collections.emptyMap();
}
@SuppressWarnings("unchecked")
@Override
public @NonNull
Collection> getBeanDefinitionReferences() {
if (!beanDefinitionsClasses.isEmpty()) {
final List refs = beanDefinitionsClasses.stream().filter(ref -> ref.isEnabled(this))
.collect(Collectors.toList());
return 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 @NonNull
T getBean(@Nullable BeanResolutionContext resolutionContext, @NonNull Class beanType) {
ArgumentUtils.requireNonNull("beanType", beanType);
return getBeanInternal(resolutionContext, beanType, null, true, true);
}
@NonNull
@Override
public T getBean(@NonNull BeanDefinition definition) {
ArgumentUtils.requireNonNull("definition", definition);
Qualifier declaredQualifier = definition.getDeclaredQualifier();
if (definition.isSingleton()) {
BeanKey key = new BeanKey<>(definition, declaredQualifier);
BeanRegistration beanRegistration = singletonObjects.get(key);
if (beanRegistration != null) {
return (T) beanRegistration.bean;
}
}
Class beanType = definition.getBeanType();
try (BeanResolutionContext context = newResolutionContext(definition, null)) {
return getBeanForDefinition(
context,
beanType,
declaredQualifier,
true,
definition
);
}
}
/**
* 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 @NonNull
T getBean(@Nullable BeanResolutionContext resolutionContext, @NonNull Class beanType, @Nullable Qualifier qualifier) {
ArgumentUtils.requireNonNull("beanType", beanType);
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 @NonNull
Optional findBean(@Nullable BeanResolutionContext resolutionContext, @NonNull Class beanType, @Nullable Qualifier qualifier) {
ArgumentUtils.requireNonNull("beanType", beanType);
// allow injection the bean context
if (thisInterfaces.contains(beanType)) {
return Optional.of((T) this);
}
try {
T bean = getBeanInternal(resolutionContext, beanType, qualifier, true, false);
if (bean == null) {
return Optional.empty();
} else {
return Optional.of(bean);
}
} catch (DisabledBeanException e) {
if (AbstractBeanContextConditional.LOG.isDebugEnabled()) {
AbstractBeanContextConditional.LOG.debug("Bean of type [{}] disabled for reason: {}", beanType.getSimpleName(), e.getMessage());
}
return Optional.empty();
}
}
@SuppressWarnings("unchecked")
@Override
public void publishEvent(@NonNull Object event) {
//noinspection ConstantConditions
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());
notifyEventListeners(event, eventListeners);
}
}
private void notifyEventListeners(@NonNull Object event, Collection eventListeners) {
if (!eventListeners.isEmpty()) {
if (EVENT_LOGGER.isTraceEnabled()) {
EVENT_LOGGER.trace("Established event listeners {} for event: {}", eventListeners, event);
}
for (ApplicationEventListener listener : eventListeners) {
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;
}
}
}
}
}
}
@Override
public @NonNull
Future publishEventAsync(@NonNull Object event) {
Objects.requireNonNull(event, "Event cannot be null");
CompletableFuture future = new CompletableFuture<>();
Collection eventListeners = streamOfType(ApplicationEventListener.class, Qualifiers.byTypeArguments(event.getClass()))
.sorted(OrderUtil.COMPARATOR).collect(Collectors.toList());
Executor executor = findBean(Executor.class, Qualifiers.byName("scheduled"))
.orElseGet(ForkJoinPool::commonPool);
executor.execute(() -> {
try {
notifyEventListeners(event, eventListeners);
future.complete(null);
} catch (Exception e) {
future.completeExceptionally(e);
}
});
return future;
}
@Override
public @NonNull
Optional> findProxyBeanDefinition(@NonNull Class beanType, @Nullable Qualifier qualifier) {
ArgumentUtils.requireNonNull("beanType", beanType);
return getBeanDefinitions(beanType, qualifier)
.stream()
.filter(BeanDefinition::isProxy)
.findFirst();
}
/**
* 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 @NonNull
Provider getBeanProvider(@Nullable BeanResolutionContext resolutionContext, @NonNull Class beanType, @Nullable Qualifier qualifier) {
ArgumentUtils.requireNonNull("beanType", beanType);
BeanKey beanKey = new BeanKey(beanType, qualifier);
T inFlightBean = resolutionContext != null ? resolutionContext.getInFlightBean(beanKey) : null;
if (inFlightBean != null) {
return new ResolvedProvider<>(inFlightBean);
}
@SuppressWarnings("unchecked") BeanRegistration beanRegistration = singletonObjects.get(beanKey);
if (beanRegistration != null) {
return new ResolvedProvider<>(beanRegistration.bean);
}
Optional> concreteCandidate = findConcreteCandidate(resolutionContext, beanType, qualifier, true, false);
if (concreteCandidate.isPresent()) {
return new UnresolvedProvider<>(concreteCandidate.get(), this);
} else {
throw new NoSuchBeanException(beanType);
}
}
/**
* Resolves the {@link BeanDefinitionReference} class instances. Default implementation uses ServiceLoader pattern.
*
* @return The bean definition classes
*/
protected @NonNull
List resolveBeanDefinitionReferences() {
final SoftServiceLoader definitions = SoftServiceLoader.load(BeanDefinitionReference.class, classLoader);
List> list = new ArrayList<>(300);
for (ServiceDefinition definition : definitions) {
list.add(definition);
}
return list.parallelStream()
.filter(ServiceDefinition::isPresent)
.map(ServiceDefinition::load)
.filter(BeanDefinitionReference::isPresent)
.collect(Collectors.toList());
}
/**
* Resolves the {@link BeanConfiguration} class instances. Default implementation uses ServiceLoader pattern.
*
* @return The bean definition classes
*/
protected @NonNull
Iterable resolveBeanConfigurations() {
final SoftServiceLoader definitions = SoftServiceLoader.load(BeanConfiguration.class, classLoader);
List> list = new ArrayList<>(300);
for (ServiceDefinition definition : definitions) {
list.add(definition);
}
return list.parallelStream()
.filter(ServiceDefinition::isPresent)
.map(ServiceDefinition::load)
.collect(Collectors.toList());
}
/**
* Initialize the event listeners.
*/
protected void initializeEventListeners() {
final Collection> beanCreatedDefinitions = getBeanDefinitions(BeanCreatedEventListener.class);
final HashMap> beanCreatedListeners = new HashMap<>(beanCreatedDefinitions.size());
//noinspection ArraysAsListWithZeroOrOneArgument
beanCreatedListeners.put(AnnotationProcessor.class, Arrays.asList(new AnnotationProcessorListener()));
for (BeanDefinition beanCreatedDefinition : beanCreatedDefinitions) {
try (BeanResolutionContext context = newResolutionContext(beanCreatedDefinition, null)) {
final BeanCreatedEventListener listener;
final Qualifier qualifier = beanCreatedDefinition.getDeclaredQualifier();
if (beanCreatedDefinition.isSingleton()) {
listener = createAndRegisterSingleton(
context,
beanCreatedDefinition,
beanCreatedDefinition.getBeanType(),
qualifier
);
} else {
listener = doCreateBean(
context,
beanCreatedDefinition,
BeanCreatedEventListener.class,
qualifier
);
}
List