package io.micronaut.context;
import io.micronaut.context.annotation.*;
import io.micronaut.context.env.Environment;
import io.micronaut.context.event.BeanInitializedEventListener;
import io.micronaut.context.event.BeanInitializingEvent;
import io.micronaut.context.exceptions.*;
import io.micronaut.core.annotation.AnnotationMetadata;
import io.micronaut.core.annotation.Internal;
import io.micronaut.core.annotation.UsedByGeneratedCode;
import io.micronaut.core.bind.annotation.Bindable;
import io.micronaut.core.convert.ArgumentConversionContext;
import io.micronaut.core.convert.ConversionContext;
import io.micronaut.core.naming.Named;
import io.micronaut.core.type.Argument;
import io.micronaut.core.type.DefaultArgument;
import io.micronaut.core.util.CollectionUtils;
import io.micronaut.core.util.StringUtils;
import io.micronaut.core.value.PropertyResolver;
import io.micronaut.inject.*;
import io.micronaut.inject.annotation.AbstractEnvironmentAnnotationMetadata;
import io.micronaut.inject.qualifiers.Qualifiers;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.Nullable;
import javax.inject.Inject;
import javax.inject.Provider;
import javax.inject.Scope;
import javax.inject.Singleton;
import java.lang.annotation.Annotation;
import java.lang.reflect.Array;
import java.lang.reflect.Modifier;
import java.util.*;
import java.util.stream.Stream;
@Internal
public class AbstractBeanDefinition<T> extends AbstractBeanContextConditional implements BeanDefinition<T>, EnvironmentConfigurable {
private static final Logger LOG = LoggerFactory.getLogger(AbstractBeanDefinition.class);
private static final String NAMED_ATTRIBUTE = Named.class.getName();
@SuppressWarnings("WeakerAccess")
protected final List<MethodInjectionPoint> methodInjectionPoints = new ArrayList<>(3);
@SuppressWarnings("WeakerAccess")
protected final List<FieldInjectionPoint> fieldInjectionPoints = new ArrayList<>(3);
@SuppressWarnings("WeakerAccess")
protected List<MethodInjectionPoint> postConstructMethods;
@SuppressWarnings("WeakerAccess")
protected List<MethodInjectionPoint> preDestroyMethods;
@SuppressWarnings("WeakerAccess")
protected Map<MethodKey, ExecutableMethod<T, ?>> executableMethodMap;
private final Class<T> type;
private final boolean isAbstract;
private final boolean isConfigurationProperties;
private final Class<?> declaringType;
private final ConstructorInjectionPoint<T> constructor;
private final Collection<Class> requiredComponents = new HashSet<>(3);
private AnnotationMetadata beanAnnotationMetadata;
private Environment environment;
@SuppressWarnings({"unchecked", "WeakerAccess"})
@Internal
@UsedByGeneratedCode
protected AbstractBeanDefinition(Class<T> producedType,
Class<?> declaringType,
String methodName,
AnnotationMetadata methodMetadata,
boolean requiresReflection,
Argument... arguments) {
this.type = producedType;
this.isAbstract = false;
this.declaringType = declaringType;
if (requiresReflection) {
this.constructor = new ReflectionMethodConstructorInjectionPoint(
this,
declaringType,
methodName,
arguments,
methodMetadata
);
} else {
this.constructor = new DefaultMethodConstructorInjectionPoint(
this,
declaringType,
methodName,
arguments,
methodMetadata
);
}
this.isConfigurationProperties = hasStereotype(ConfigurationReader.class) || isIterable();
this.addRequiredComponents(arguments);
}
@Internal
@UsedByGeneratedCode
protected AbstractBeanDefinition(Class<T> type,
AnnotationMetadata constructorAnnotationMetadata,
boolean requiresReflection,
Argument... arguments) {
this.type = type;
this.isAbstract = Modifier.isAbstract(this.type.getModifiers());
this.declaringType = type;
if (requiresReflection) {
this.constructor = new ReflectionConstructorInjectionPoint<>(
this,
type,
constructorAnnotationMetadata,
arguments);
} else {
this.constructor = new DefaultConstructorInjectionPoint<>(
this,
type,
constructorAnnotationMetadata,
arguments
);
}
this.isConfigurationProperties = hasStereotype(ConfigurationReader.class) || isIterable();
this.addRequiredComponents(arguments);
}
@Override
public @NonNull List<Argument<?>> getTypeArguments(String type) {
if (type == null) {
return Collections.emptyList();
}
Map<String, Argument<?>[]> typeArguments = getTypeArgumentsMap();
Argument<?>[] arguments = typeArguments.get(type);
if (arguments != null) {
return Arrays.asList(arguments);
}
return Collections.emptyList();
}
@Override
public AnnotationMetadata getAnnotationMetadata() {
if (this.beanAnnotationMetadata == null) {
this.beanAnnotationMetadata = initializeAnnotationMetadata();
}
return this.beanAnnotationMetadata;
}
@Override
public boolean isAbstract() {
return this.isAbstract;
}
@Override
public boolean isIterable() {
return hasDeclaredStereotype(EachProperty.class) || hasDeclaredStereotype(EachBean.class);
}
@Override
public boolean isPrimary() {
return hasDeclaredStereotype(Primary.class);
}
@SuppressWarnings("unchecked")
@Override
public <R> Optional<ExecutableMethod<T, R>> findMethod(String name, Class... argumentTypes) {
if (executableMethodMap != null) {
MethodKey methodKey = new MethodKey(name, argumentTypes);
ExecutableMethod<T, R> invocableMethod = (ExecutableMethod<T, R>) executableMethodMap.get(methodKey);
if (invocableMethod != null) {
return Optional.of(invocableMethod);
}
}
return Optional.empty();
}
@Override
@SuppressWarnings({"unchecked"})
public Stream<ExecutableMethod<T, ?>> findPossibleMethods(String name) {
if (executableMethodMap != null && executableMethodMap.keySet().stream().anyMatch(methodKey -> methodKey.name.equals(name))) {
return executableMethodMap
.values()
.stream()
.filter(method -> method.getMethodName().equals(name));
}
return Stream.empty();
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
AbstractBeanDefinition<?> that = (AbstractBeanDefinition<?>) o;
return getClass().equals(that.getClass());
}
@Override
public int hashCode() {
return getClass().hashCode();
}
@Override
public String toString() {
return "Definition: " + declaringType.getName();
}
@Override
public boolean isProvided() {
return getAnnotationMetadata().hasDeclaredStereotype(Provided.class);
}
@Override
public boolean isSingleton() {
return getAnnotationMetadata().hasDeclaredStereotype(Singleton.class);
}
@Override
public Optional<Class<? extends Annotation>> getScope() {
return getAnnotationMetadata().getDeclaredAnnotationTypeByStereotype(Scope.class);
}
@Override
public final Class<T> getBeanType() {
return type;
}
@Override
public final Optional<Class<?>> getDeclaringType() {
return Optional.ofNullable(declaringType);
}
@Override
public final ConstructorInjectionPoint<T> getConstructor() {
return constructor;
}
@Override
public Collection<Class> getRequiredComponents() {
return Collections.unmodifiableCollection(requiredComponents);
}
@Override
public final Collection<MethodInjectionPoint> getInjectedMethods() {
return Collections.unmodifiableCollection(methodInjectionPoints);
}
@Override
public final Collection<FieldInjectionPoint> getInjectedFields() {
return Collections.unmodifiableCollection(fieldInjectionPoints);
}
@Override
public final Collection<MethodInjectionPoint> getPostConstructMethods() {
if (postConstructMethods != null) {
return Collections.unmodifiableCollection(postConstructMethods);
} else {
return Collections.emptyList();
}
}
@Override
public final Collection<MethodInjectionPoint> getPreDestroyMethods() {
if (preDestroyMethods != null) {
return Collections.unmodifiableCollection(preDestroyMethods);
} else {
return Collections.emptyList();
}
}
@Override
public String getName() {
return getBeanType().getName();
}
@SuppressWarnings("unchecked")
@Override
public T inject(BeanContext context, T bean) {
return (T) injectBean(new DefaultBeanResolutionContext(context, this), context, bean);
}
@SuppressWarnings("unchecked")
@Override
public T inject(BeanResolutionContext resolutionContext, BeanContext context, T bean) {
return (T) injectBean(resolutionContext, context, bean);
}
@Override
public Collection<ExecutableMethod<T, ?>> getExecutableMethods() {
if (executableMethodMap != null) {
return Collections.unmodifiableCollection(this.executableMethodMap.values());
} else {
return Collections.emptyList();
}
}
@Internal
@Override
public final void configure(Environment environment) {
if (environment != null) {
this.environment = environment;
if (constructor instanceof EnvironmentConfigurable) {
((EnvironmentConfigurable) constructor).configure(environment);
}
for (MethodInjectionPoint methodInjectionPoint : methodInjectionPoints) {
if (methodInjectionPoint instanceof EnvironmentConfigurable) {
((EnvironmentConfigurable) methodInjectionPoint).configure(environment);
}
}
if (executableMethodMap != null) {
for (ExecutableMethod<T, ?> executableMethod : executableMethodMap.values()) {
if (executableMethod instanceof EnvironmentConfigurable) {
((EnvironmentConfigurable) executableMethod).configure(environment);
}
}
}
}
}
@Internal
protected final void warn(String message) {
if (LOG.isWarnEnabled()) {
LOG.warn(message);
}
}
@SuppressWarnings("unused")
@Internal
protected final void warnMissingProperty(Class type, String method, String property) {
if (LOG.isWarnEnabled()) {
LOG.warn("Configuration property [{}] could not be set as the underlying method [{}] does not exist on builder [{}]. This usually indicates the configuration option was deprecated and has been removed by the builder implementation (potentially a third-party library).", property, method, type);
}
}
@SuppressWarnings({"unchecked", "unused"})
@Internal
protected final Object getProxiedBean(BeanContext beanContext) {
DefaultBeanContext defaultBeanContext = (DefaultBeanContext) beanContext;
Optional<String> qualifier = getAnnotationMetadata().getAnnotationNameByStereotype(javax.inject.Qualifier.class);
return defaultBeanContext.getProxyTargetBean(
getBeanType(),
(Qualifier<T>) qualifier.map(q -> Qualifiers.byAnnotation(getAnnotationMetadata(), q)).orElse(null)
);
}
@SuppressWarnings("unused")
@Internal
@UsedByGeneratedCode
protected final AbstractBeanDefinition<T> addExecutableMethod(ExecutableMethod<T, ?> executableMethod) {
MethodKey key = new MethodKey(executableMethod.getMethodName(), executableMethod.getArgumentTypes());
if (executableMethodMap == null) {
executableMethodMap = new LinkedHashMap<>(3);
}
executableMethodMap.put(key, executableMethod);
return this;
}
@SuppressWarnings({"unused", "unchecked"})
@Internal
@UsedByGeneratedCode
protected final AbstractBeanDefinition addInjectionPoint(
Class declaringType,
Class fieldType,
String field,
@Nullable AnnotationMetadata annotationMetadata,
@Nullable Argument[] typeArguments,
boolean requiresReflection) {
if (annotationMetadata != null && annotationMetadata.hasDeclaredAnnotation(Inject.class)) {
requiredComponents.add(fieldType);
}
if (requiresReflection) {
fieldInjectionPoints.add(new ReflectionFieldInjectionPoint(
this,
declaringType,
fieldType,
field,
annotationMetadata,
typeArguments
));
} else {
fieldInjectionPoints.add(new DefaultFieldInjectionPoint(
this,
declaringType,
fieldType,
field,
annotationMetadata,
typeArguments
));
}
return this;
}
@SuppressWarnings({"unchecked", "unused"})
@Internal
@UsedByGeneratedCode
protected final AbstractBeanDefinition addInjectionPoint(
Class declaringType,
String method,
@Nullable Argument[] arguments,
@Nullable AnnotationMetadata annotationMetadata,
boolean requiresReflection) {
return addInjectionPointInternal(
declaringType,
method,
arguments,
annotationMetadata,
requiresReflection,
this.methodInjectionPoints
);
}
@SuppressWarnings("unused")
@Internal
@UsedByGeneratedCode
protected final AbstractBeanDefinition addPostConstruct(Class declaringType,
String method,
@Nullable Argument[] arguments,
@Nullable AnnotationMetadata annotationMetadata,
boolean requiresReflection) {
if (postConstructMethods == null) {
postConstructMethods = new ArrayList<>(1);
}
return addInjectionPointInternal(declaringType, method, arguments, annotationMetadata, requiresReflection, this.postConstructMethods);
}
@SuppressWarnings("unused")
@Internal
@UsedByGeneratedCode
protected final AbstractBeanDefinition addPreDestroy(Class declaringType,
String method,
Argument[] arguments,
AnnotationMetadata annotationMetadata,
boolean requiresReflection) {
if (preDestroyMethods == null) {
preDestroyMethods = new ArrayList<>(1);
}
return addInjectionPointInternal(declaringType, method, arguments, annotationMetadata, requiresReflection, this.preDestroyMethods);
}
@Internal
@SuppressWarnings({"WeakerAccess", "unused"})
@UsedByGeneratedCode
protected Object injectBean(BeanResolutionContext resolutionContext, BeanContext context, Object bean) {
return bean;
}
@Internal
@SuppressWarnings({"unused"})
@UsedByGeneratedCode
protected Object injectAnother(BeanResolutionContext resolutionContext, BeanContext context, Object bean) {
if (bean == null) {
throw new BeanInstantiationException(resolutionContext, "Bean factory returned null");
}
DefaultBeanContext defaultContext = (DefaultBeanContext) context;
return defaultContext.inject(resolutionContext, this, bean);
}
@SuppressWarnings({"unused", "unchecked"})
@Internal
@UsedByGeneratedCode
protected Object postConstruct(BeanResolutionContext resolutionContext, BeanContext context, Object bean) {
boolean addInCreationHandling = isSingleton() && !CollectionUtils.isNotEmpty(postConstructMethods);
DefaultBeanContext.BeanKey key = null;
if (addInCreationHandling) {
key = new DefaultBeanContext.BeanKey(this, resolutionContext.getCurrentQualifier());
resolutionContext.addInFlightBean(key, bean);
}
final Set<Map.Entry<Class, List<BeanInitializedEventListener>>> beanInitializedEventListeners
= ((DefaultBeanContext) context).beanInitializedEventListeners;
if (CollectionUtils.isNotEmpty(beanInitializedEventListeners)) {
for (Map.Entry<Class, List<BeanInitializedEventListener>> entry : beanInitializedEventListeners) {
if (entry.getKey().isAssignableFrom(getBeanType())) {
for (BeanInitializedEventListener listener : entry.getValue()) {
bean = listener.onInitialized(new BeanInitializingEvent(context, this, bean));
if (bean == null) {
throw new BeanInstantiationException(resolutionContext, "Listener [" + listener + "] returned null from onInitialized event");
}
}
}
}
}
DefaultBeanContext defaultContext = (DefaultBeanContext) context;
for (int i = 0; i < methodInjectionPoints.size(); i++) {
MethodInjectionPoint methodInjectionPoint = methodInjectionPoints.get(i);
if (methodInjectionPoint.isPostConstructMethod() && methodInjectionPoint.requiresReflection()) {
injectBeanMethod(resolutionContext, defaultContext, i, bean);
}
}
if (bean instanceof LifeCycle) {
bean = ((LifeCycle) bean).start();
}
try {
return bean;
} finally {
if (addInCreationHandling) {
resolutionContext.removeInFlightBean(key);
}
}
}
@UsedByGeneratedCode
protected Object preDestroy(BeanResolutionContext resolutionContext, BeanContext context, Object bean) {
DefaultBeanContext defaultContext = (DefaultBeanContext) context;
for (int i = 0; i < methodInjectionPoints.size(); i++) {
MethodInjectionPoint methodInjectionPoint = methodInjectionPoints.get(i);
if (methodInjectionPoint.isPreDestroyMethod() && methodInjectionPoint.requiresReflection()) {
injectBeanMethod(resolutionContext, defaultContext, i, bean);
}
}
if (bean instanceof LifeCycle) {
bean = ((LifeCycle) bean).stop();
}
return bean;
}
@Internal
@SuppressWarnings("WeakerAccess")
protected void injectBeanMethod(BeanResolutionContext resolutionContext, DefaultBeanContext context, int methodIndex, Object bean) {
MethodInjectionPoint methodInjectionPoint = methodInjectionPoints.get(methodIndex);
Argument[] methodArgumentTypes = methodInjectionPoint.getArguments();
Object[] methodArgs = new Object[methodArgumentTypes.length];
for (int i = 0; i < methodArgumentTypes.length; i++) {
methodArgs[i] = getBeanForMethodArgument(resolutionContext, context, methodIndex, i);
}
try {
methodInjectionPoint.invoke(bean, methodArgs);
} catch (Throwable e) {
throw new BeanInstantiationException(this, e);
}
}
@SuppressWarnings("unused")
@Internal
protected final void injectBeanField(BeanResolutionContext resolutionContext, DefaultBeanContext context, int index, Object bean) {
FieldInjectionPoint fieldInjectionPoint = fieldInjectionPoints.get(index);
boolean isInject = fieldInjectionPoint.getAnnotationMetadata().hasDeclaredAnnotation(Inject.class);
try {
Object value;
if (isInject) {
instrumentAnnotationMetadata(context, fieldInjectionPoint);
value = getBeanForField(resolutionContext, context, fieldInjectionPoint);
} else {
value = getValueForField(resolutionContext, context, index);
}
if (value != null) {
fieldInjectionPoint.set(bean, value);
}
} catch (Throwable e) {
if (e instanceof BeanContextException) {
throw (BeanContextException) e;
} else {
throw new DependencyInjectionException(resolutionContext, fieldInjectionPoint, "Error setting field value: " + e.getMessage(), e);
}
}
}
@SuppressWarnings({"unused", "unchecked"})
@Internal
protected final Object getValueForMethodArgument(BeanResolutionContext resolutionContext, BeanContext context, int methodIndex, int argIndex) {
MethodInjectionPoint injectionPoint = methodInjectionPoints.get(methodIndex);
Argument argument = injectionPoint.getArguments()[argIndex];
BeanResolutionContext.Path path = resolutionContext.getPath();
path.pushMethodArgumentResolve(this, injectionPoint, argument);
if (context instanceof ApplicationContext) {
try {
String valueAnnStr = argument.getAnnotationMetadata().stringValue(Value.class).orElse(null);
Class<?> argumentType;
boolean isCollection = false;
if (Collection.class.isAssignableFrom(argument.getType())) {
argumentType = argument.getFirstTypeVariable().map(Argument::getType).orElse((Class) Object.class);
isCollection = true;
} else {
argumentType = argument.getType();
}
if (isInnerConfiguration(argumentType, context)) {
Qualifier qualifier = resolveQualifier(resolutionContext, argument, true);
if (isCollection) {
Collection beans = ((DefaultBeanContext) context).getBeansOfType(resolutionContext, argumentType, qualifier);
return coerceCollectionToCorrectType(argument.getType(), beans);
} else {
return ((DefaultBeanContext) context).getBean(resolutionContext, argumentType, qualifier);
}
} else {
String valString = resolvePropertyValueName(resolutionContext, injectionPoint.getAnnotationMetadata(), argument, valueAnnStr);
ApplicationContext applicationContext = (ApplicationContext) context;
ArgumentConversionContext conversionContext = ConversionContext.of(argument);
Optional value = resolveValue(applicationContext, conversionContext, valueAnnStr != null, valString);
if (argumentType == Optional.class) {
return resolveOptionalObject(value);
} else {
if (value.isPresent()) {
return value.get();
} else {
if (argument.isDeclaredNullable()) {
return null;
}
throw new DependencyInjectionException(resolutionContext, injectionPoint, conversionContext, valString);
}
}
}
} finally {
path.pop();
}
} else {
path.pop();
throw new DependencyInjectionException(resolutionContext, argument, "BeanContext must support property resolution");
}
}
@Internal
@UsedByGeneratedCode
protected final boolean containsValueForMethodArgument(BeanResolutionContext resolutionContext, BeanContext context, int methodIndex, int argIndex) {
if (context instanceof ApplicationContext) {
MethodInjectionPoint injectionPoint = methodInjectionPoints.get(methodIndex);
Argument argument = injectionPoint.getArguments()[argIndex];
String valueAnnStr = argument.getAnnotationMetadata().stringValue(Value.class).orElse(null);
String valString = resolvePropertyValueName(resolutionContext, injectionPoint.getAnnotationMetadata(), argument, valueAnnStr);
ApplicationContext applicationContext = (ApplicationContext) context;
Class type = argument.getType();
boolean isConfigProps = type.isAnnotationPresent(ConfigurationProperties.class);
boolean result = isConfigProps || Map.class.isAssignableFrom(type) || Collection.class.isAssignableFrom(type) ? applicationContext.containsProperties(valString) : applicationContext.containsProperty(valString);
if (!result && isConfigurationProperties()) {
String cliOption = resolveCliOption(argument.getName());
if (cliOption != null) {
result = applicationContext.containsProperty(cliOption);
}
}
if (result && injectionPoint instanceof MissingMethodInjectionPoint) {
if (LOG.isWarnEnabled()) {
LOG.warn("Bean definition for type [{}] is compiled against an older version and value [{}] can no longer be set for missing method: {}",
getBeanType(),
valString,
injectionPoint.getName());
}
result = false;
}
return result;
}
return false;
}
@Internal
@SuppressWarnings("WeakerAccess")
@UsedByGeneratedCode
protected final Object getBeanForMethodArgument(BeanResolutionContext resolutionContext, BeanContext context, int methodIndex, int argIndex) {
MethodInjectionPoint injectionPoint = methodInjectionPoints.get(methodIndex);
Argument argument = injectionPoint.getArguments()[argIndex];
if (argument instanceof DefaultArgument) {
argument = new EnvironmentAwareArgument((DefaultArgument) argument);
instrumentAnnotationMetadata(context, argument);
}
return getBeanForMethodArgument(resolutionContext, context, injectionPoint, argument);
}
@SuppressWarnings("WeakerAccess")
@Internal
protected final Collection getBeansOfTypeForMethodArgument(BeanResolutionContext resolutionContext, BeanContext context, MethodInjectionPoint injectionPoint, Argument argument) {
return resolveBeanWithGenericsFromMethodArgument(resolutionContext, injectionPoint, argument, (beanType, qualifier) -> {
boolean hasNoGenerics = !argument.getType().isArray() && argument.getTypeVariables().isEmpty();
if (hasNoGenerics) {
return ((DefaultBeanContext) context).getBean(resolutionContext, beanType, qualifier);
} else {
return ((DefaultBeanContext) context).getBeansOfType(resolutionContext, beanType, qualifier);
}
}
);
}
@SuppressWarnings("WeakerAccess")
@Internal
protected final Provider getBeanProviderForMethodArgument(BeanResolutionContext resolutionContext, BeanContext context, MethodInjectionPoint injectionPoint, Argument argument) {
return resolveBeanWithGenericsFromMethodArgument(resolutionContext, injectionPoint, argument, (beanType, qualifier) ->
((DefaultBeanContext) context).getBeanProvider(resolutionContext, beanType, qualifier)
);
}
@SuppressWarnings("WeakerAccess")
@Internal
protected final Optional findBeanForMethodArgument(BeanResolutionContext resolutionContext, BeanContext context, MethodInjectionPoint injectionPoint, Argument argument) {
return resolveBeanWithGenericsFromMethodArgument(resolutionContext, injectionPoint, argument, (beanType, qualifier) ->
((DefaultBeanContext) context).findBean(resolutionContext, beanType, qualifier)
);
}
@SuppressWarnings("WeakerAccess")
@Internal
protected final Stream streamOfTypeForMethodArgument(BeanResolutionContext resolutionContext, BeanContext context, MethodInjectionPoint injectionPoint, Argument argument) {
return resolveBeanWithGenericsFromMethodArgument(resolutionContext, injectionPoint, argument, (beanType, qualifier) ->
((DefaultBeanContext) context).streamOfType(resolutionContext, beanType, qualifier)
);
}
@SuppressWarnings("unused")
@Internal
@UsedByGeneratedCode
protected final Object getBeanForConstructorArgument(BeanResolutionContext resolutionContext, BeanContext context, int argIndex) {
ConstructorInjectionPoint<T> constructorInjectionPoint = getConstructor();
Argument<?> argument = constructorInjectionPoint.getArguments()[argIndex];
if (argument instanceof DefaultArgument) {
argument = new EnvironmentAwareArgument((DefaultArgument) argument);
instrumentAnnotationMetadata(context, argument);
}
Class argumentType = argument.getType();
if (argumentType == BeanResolutionContext.class) {
return resolutionContext;
} else if (argumentType.isArray()) {
Collection beansOfType = getBeansOfTypeForConstructorArgument(resolutionContext, context, constructorInjectionPoint, argument);
return beansOfType.toArray((Object[]) Array.newInstance(argumentType.getComponentType(), beansOfType.size()));
} else if (Collection.class.isAssignableFrom(argumentType)) {
Collection beansOfType = getBeansOfTypeForConstructorArgument(resolutionContext, context, constructorInjectionPoint, argument);
return coerceCollectionToCorrectType(argumentType, beansOfType);
} else if (Stream.class.isAssignableFrom(argumentType)) {
return streamOfTypeForConstructorArgument(resolutionContext, context, constructorInjectionPoint, argument);
} else if (Provider.class.isAssignableFrom(argumentType)) {
return getBeanProviderForConstructorArgument(resolutionContext, context, constructorInjectionPoint, argument);
} else if (Optional.class.isAssignableFrom(argumentType)) {
return findBeanForConstructorArgument(resolutionContext, context, constructorInjectionPoint, argument);
} else {
BeanResolutionContext.Path path = resolutionContext.getPath();
BeanResolutionContext.Segment current = path.peek();
boolean isNullable = argument.isDeclaredNullable();
if (isNullable && current != null && current.getArgument().equals(argument)) {
return null;
} else {
path.pushConstructorResolve(this, argument);
try {
Object bean;
Qualifier qualifier = resolveQualifier(resolutionContext, argument, isInnerConfiguration(argument.getType(), context));
if (Qualifier.class.isAssignableFrom(argumentType)) {
bean = qualifier;
} else {
bean = ((DefaultBeanContext) context).getBean(resolutionContext, argumentType, qualifier);
}
path.pop();
return bean;
} catch (DisabledBeanException e) {
if (AbstractBeanContextConditional.LOG.isDebugEnabled()) {
AbstractBeanContextConditional.LOG.debug("Bean of type [{}] disabled for reason: {}", argumentType.getSimpleName(), e.getMessage());
}
if (isIterable() && getAnnotationMetadata().hasDeclaredAnnotation(EachBean.class)) {
throw new DisabledBeanException("Bean [" + getBeanType().getSimpleName() + "] disabled by parent: " + e.getMessage());
} else {
if (isNullable) {
path.pop();
return null;
}
throw new DependencyInjectionException(resolutionContext, argument, e);
}
} catch (NoSuchBeanException e) {
if (isNullable) {
path.pop();
return null;
}
throw new DependencyInjectionException(resolutionContext, argument, e);
}
}
}
}
@SuppressWarnings("unused")
@Internal
@UsedByGeneratedCode
protected final Object getValueForConstructorArgument(BeanResolutionContext resolutionContext, BeanContext context, int argIndex) {
ConstructorInjectionPoint<T> constructorInjectionPoint = getConstructor();
BeanResolutionContext.Path path = resolutionContext.getPath();
Argument<?> argument = constructorInjectionPoint.getArguments()[argIndex];
path.pushConstructorResolve(this, argument);
try {
Object result;
if (context instanceof ApplicationContext) {
ApplicationContext propertyResolver = (ApplicationContext) context;
AnnotationMetadata argMetadata = argument.getAnnotationMetadata();
Optional<String> valAnn = argMetadata.stringValue(Value.class);
String prop = resolvePropertyValueName(resolutionContext, argMetadata, argument, valAnn.orElse(null));
ArgumentConversionContext<?> conversionContext = ConversionContext.of(argument);
Optional<?> value = resolveValue(propertyResolver, conversionContext, valAnn.isPresent(), prop);
if (argument.getType() == Optional.class) {
return resolveOptionalObject(value);
} else {
if (value.isPresent()) {
result = value.get();
} else {
if (argument.isDeclaredNullable()) {
result = null;
} else {
result = argMetadata.getValue(Bindable.class, "defaultValue", argument)
.orElseThrow(() -> new DependencyInjectionException(resolutionContext, conversionContext, prop));
}
}
}
} else {
throw new DependencyInjectionException(resolutionContext, argument, "BeanContext must support property resolution");
}
if (this instanceof ValidatedBeanDefinition) {
((ValidatedBeanDefinition) this).validateBeanArgument(
resolutionContext,
constructorInjectionPoint,
argument,
argIndex,
result
);
}
return result;
} catch (NoSuchBeanException | BeanInstantiationException e) {
throw new DependencyInjectionException(resolutionContext, argument, e);
} finally {
path.pop();
}
}
@SuppressWarnings("WeakerAccess")
@Internal
protected final Provider getBeanProviderForConstructorArgument(BeanResolutionContext resolutionContext, BeanContext context, @SuppressWarnings("unused") ConstructorInjectionPoint constructorInjectionPoint, Argument argument) {
return resolveBeanWithGenericsFromConstructorArgument(resolutionContext, argument, (beanType, qualifier) ->
((DefaultBeanContext) context).getBeanProvider(resolutionContext, beanType, qualifier)
);
}
@SuppressWarnings("WeakerAccess")
@Internal
protected final Collection getBeansOfTypeForConstructorArgument(BeanResolutionContext resolutionContext, BeanContext context, @SuppressWarnings("unused") ConstructorInjectionPoint<T> constructorInjectionPoint, Argument argument) {
return resolveBeanWithGenericsFromConstructorArgument(resolutionContext, argument, (beanType, qualifier) -> {
boolean hasNoGenerics = !argument.getType().isArray() && argument.getTypeVariables().isEmpty();
if (hasNoGenerics) {
return ((DefaultBeanContext) context).getBean(resolutionContext, beanType, qualifier);
} else {
return ((DefaultBeanContext) context).getBeansOfType(resolutionContext, beanType, qualifier);
}
}
);
}
@SuppressWarnings("WeakerAccess")
@Internal
protected final Stream streamOfTypeForConstructorArgument(BeanResolutionContext resolutionContext, BeanContext context, @SuppressWarnings("unused") ConstructorInjectionPoint<T> constructorInjectionPoint, Argument argument) {
return resolveBeanWithGenericsFromConstructorArgument(resolutionContext, argument, (beanType, qualifier) ->
((DefaultBeanContext) context).streamOfType(resolutionContext, beanType, qualifier)
);
}
@SuppressWarnings("WeakerAccess")
@Internal
protected final Optional findBeanForConstructorArgument(BeanResolutionContext resolutionContext, BeanContext context, @SuppressWarnings("unused") ConstructorInjectionPoint<T> constructorInjectionPoint, Argument argument) {
return resolveBeanWithGenericsFromConstructorArgument(resolutionContext, argument, (beanType, qualifier) ->
((DefaultBeanContext) context).findBean(resolutionContext, beanType, qualifier)
);
}
@SuppressWarnings("unused")
@Internal
@UsedByGeneratedCode
protected final Object getBeanForField(BeanResolutionContext resolutionContext, BeanContext context, int fieldIndex) {
FieldInjectionPoint injectionPoint = fieldInjectionPoints.get(fieldIndex);
instrumentAnnotationMetadata(context, injectionPoint);
return getBeanForField(resolutionContext, context, injectionPoint);
}
@SuppressWarnings("WeakerAccess")
@Internal
@UsedByGeneratedCode
protected final Object getValueForField(BeanResolutionContext resolutionContext, BeanContext context, int fieldIndex) {
FieldInjectionPoint injectionPoint = fieldInjectionPoints.get(fieldIndex);
BeanResolutionContext.Path path = resolutionContext.getPath();
path.pushFieldResolve(this, injectionPoint);
try {
if (context instanceof PropertyResolver) {
final AnnotationMetadata annotationMetadata = injectionPoint.getAnnotationMetadata();
String valueAnnVal = annotationMetadata.stringValue(Value.class).orElse(null);
Argument<?> fieldArgument = injectionPoint.asArgument();
Class<?> argumentType;
boolean isCollection = false;
if (Collection.class.isAssignableFrom(injectionPoint.getType())) {
argumentType = fieldArgument.getFirstTypeVariable().map(Argument::getType).orElse((Class) Object.class);
isCollection = true;
} else {
argumentType = fieldArgument.getType();
}
if (isInnerConfiguration(argumentType, context)) {
Qualifier qualifier = resolveQualifier(resolutionContext, fieldArgument, true);
if (isCollection) {
Collection beans = ((DefaultBeanContext) context).getBeansOfType(resolutionContext, argumentType, qualifier);
return coerceCollectionToCorrectType(fieldArgument.getType(), beans);
} else {
return ((DefaultBeanContext) context).getBean(resolutionContext, argumentType, qualifier);
}
} else {
String valString = resolvePropertyValueName(resolutionContext, injectionPoint, valueAnnVal, annotationMetadata);
ArgumentConversionContext conversionContext = ConversionContext.of(fieldArgument);
Optional value = resolveValue((ApplicationContext) context, conversionContext, valueAnnVal != null, valString);
if (argumentType == Optional.class) {
return resolveOptionalObject(value);
} else {
if (value.isPresent()) {
return value.get();
} else {
if (fieldArgument.isDeclaredNullable()) {
return null;
}
throw new DependencyInjectionException(resolutionContext, injectionPoint, "Error resolving field value [" + valString + "]. Property doesn't exist or cannot be converted");
}
}
}
} else {
throw new DependencyInjectionException(resolutionContext, injectionPoint, "@Value requires a BeanContext that implements PropertyResolver");
}
} finally {
path.pop();
}
}
@SuppressWarnings("unused")
@Internal
@UsedByGeneratedCode
protected final <T1> Optional<T1> getValueForPath(
BeanResolutionContext resolutionContext,
BeanContext context,
Argument<T1> propertyType,
String... propertyPath) {
if (context instanceof PropertyResolver) {
PropertyResolver propertyResolver = (PropertyResolver) context;
String pathString = propertyPath.length > 1 ? String.join(".", propertyPath) : propertyPath[0];
String valString = resolvePropertyPath(resolutionContext, pathString);
return propertyResolver.getProperty(valString, ConversionContext.of(propertyType));
}
return Optional.empty();
}
@SuppressWarnings("unused")
@Internal
@UsedByGeneratedCode
protected final <T1> Optional<T1> getValueForPath(
BeanResolutionContext resolutionContext,
BeanContext context,
Argument<T1> propertyType,
String propertyPath) {
if (context instanceof PropertyResolver) {
PropertyResolver propertyResolver = (PropertyResolver) context;
String valString = substituteWildCards(resolutionContext, propertyPath);
return propertyResolver.getProperty(valString, ConversionContext.of(propertyType));
}
return Optional.empty();
}
@Internal
@UsedByGeneratedCode
protected final boolean containsValueForField(BeanResolutionContext resolutionContext, BeanContext context, int fieldIndex) {
if (context instanceof ApplicationContext) {
FieldInjectionPoint injectionPoint = fieldInjectionPoints.get(fieldIndex);
final AnnotationMetadata annotationMetadata = injectionPoint.getAnnotationMetadata();
String valueAnnVal = annotationMetadata.stringValue(Value.class).orElse(null);
String valString = resolvePropertyValueName(resolutionContext, injectionPoint, valueAnnVal, annotationMetadata);
ApplicationContext applicationContext = (ApplicationContext) context;
Class fieldType = injectionPoint.getType();
boolean isConfigProps = fieldType.isAnnotationPresent(ConfigurationProperties.class);
boolean result = isConfigProps || Map.class.isAssignableFrom(fieldType) || Collection.class.isAssignableFrom(fieldType) ? applicationContext.containsProperties(valString) : applicationContext.containsProperty(valString);
if (!result && isConfigurationProperties()) {
String cliOption = resolveCliOption(injectionPoint.getName());
if (cliOption != null) {
return applicationContext.containsProperty(cliOption);
}
}
return result;
}
return false;
}
@SuppressWarnings("unused")
@Internal
@UsedByGeneratedCode
protected final boolean containsProperties(BeanResolutionContext resolutionContext, BeanContext context) {
return containsProperties(resolutionContext, context, null);
}
@SuppressWarnings({"WeakerAccess", "SameParameterValue"})
@Internal
@UsedByGeneratedCode
protected final boolean containsProperties(@SuppressWarnings("unused") BeanResolutionContext resolutionContext, BeanContext context, String subProperty) {
boolean isSubProperty = StringUtils.isNotEmpty(subProperty);
if (!isSubProperty && !requiredComponents.isEmpty()) {
return true;
}
if (isConfigurationProperties && context instanceof ApplicationContext) {
AnnotationMetadata annotationMetadata = getAnnotationMetadata();
ApplicationContext appCtx = (ApplicationContext) context;
if (annotationMetadata.getValue(ConfigurationProperties.class, "cliPrefix").isPresent()) {
return true;
} else {
String path = getConfigurationPropertiesPath(resolutionContext);
return appCtx.containsProperties(path);
}
}
return false;
}
@SuppressWarnings("WeakerAccess")
@Internal
@UsedByGeneratedCode
protected final Object getBeanForField(BeanResolutionContext resolutionContext, BeanContext context, FieldInjectionPoint injectionPoint) {
Class beanType = injectionPoint.getType();
if (beanType.isArray()) {
Collection beansOfType = getBeansOfTypeForField(resolutionContext, context, injectionPoint);
return beansOfType.toArray((Object[]) Array.newInstance(beanType.getComponentType(), beansOfType.size()));
} else if (Collection.class.isAssignableFrom(beanType)) {
Collection beansOfType = getBeansOfTypeForField(resolutionContext, context, injectionPoint);
if (beanType.isInstance(beansOfType)) {
return beansOfType;
} else {
return CollectionUtils.convertCollection(beanType, beansOfType).orElse(null);
}
} else if (Stream.class.isAssignableFrom(beanType)) {
return getStreamOfTypeForField(resolutionContext, context, injectionPoint);
} else if (Provider.class.isAssignableFrom(beanType)) {
return getBeanProviderForField(resolutionContext, context, injectionPoint);
} else if (Optional.class.isAssignableFrom(beanType)) {
return findBeanForField(resolutionContext, context, injectionPoint);
} else {
BeanResolutionContext.Path path = resolutionContext.getPath();
path.pushFieldResolve(this, injectionPoint);
try {
Qualifier qualifier = resolveQualifier(resolutionContext, injectionPoint.asArgument());
@SuppressWarnings("unchecked") Object bean = ((DefaultBeanContext) context).getBean(resolutionContext, beanType, qualifier);
path.pop();
return bean;
} catch (DisabledBeanException e) {
if (AbstractBeanContextConditional.LOG.isDebugEnabled()) {
AbstractBeanContextConditional.LOG.debug("Bean of type [{}] disabled for reason: {}", beanType.getSimpleName(), e.getMessage());
}
if (isIterable() && getAnnotationMetadata().hasDeclaredAnnotation(EachBean.class)) {
throw new DisabledBeanException("Bean [" + getBeanType().getSimpleName() + "] disabled by parent: " + e.getMessage());
} else {
if (injectionPoint.isDeclaredNullable()) {
path.pop();
return null;
}
throw new DependencyInjectionException(resolutionContext, injectionPoint, e);
}
} catch (NoSuchBeanException e) {
if (injectionPoint.isDeclaredNullable()) {
path.pop();
return null;
}
throw new DependencyInjectionException(resolutionContext, injectionPoint, e);
}
}
}
@SuppressWarnings("WeakerAccess")
@Internal
protected final Provider getBeanProviderForField(BeanResolutionContext resolutionContext, BeanContext context, FieldInjectionPoint injectionPoint) {
return resolveBeanWithGenericsForField(resolutionContext, injectionPoint, (beanType, qualifier) ->
((DefaultBeanContext) context).getBeanProvider(resolutionContext, beanType, qualifier)
);
}
@SuppressWarnings("WeakerAccess")
@Internal
protected final Optional findBeanForField(BeanResolutionContext resolutionContext, BeanContext context, FieldInjectionPoint injectionPoint) {
return resolveBeanWithGenericsForField(resolutionContext, injectionPoint, (beanType, qualifier) ->
((DefaultBeanContext) context).findBean(resolutionContext, beanType, qualifier)
);
}
@SuppressWarnings("WeakerAccess")
@Internal
protected final Collection getBeansOfTypeForField(BeanResolutionContext resolutionContext, BeanContext context, FieldInjectionPoint injectionPoint) {
return resolveBeanWithGenericsForField(resolutionContext, injectionPoint, (beanType, qualifier) -> {
boolean hasNoGenerics = !injectionPoint.getType().isArray() && injectionPoint.asArgument().getTypeVariables().isEmpty();
if (hasNoGenerics) {
return ((DefaultBeanContext) context).getBean(resolutionContext, beanType, qualifier);
} else {
return ((DefaultBeanContext) context).getBeansOfType(resolutionContext, beanType, qualifier);
}
}
);
}
@SuppressWarnings("WeakerAccess")
@Internal
protected final Stream getStreamOfTypeForField(BeanResolutionContext resolutionContext, BeanContext context, FieldInjectionPoint injectionPoint) {
return resolveBeanWithGenericsForField(resolutionContext, injectionPoint, (beanType, qualifier) ->
((DefaultBeanContext) context).streamOfType(resolutionContext, beanType, qualifier)
);
}
@Internal
protected Map<String, Argument<?>[]> getTypeArgumentsMap() {
return Collections.emptyMap();
}
protected AnnotationMetadata resolveAnnotationMetadata() {
return AnnotationMetadata.EMPTY_METADATA;
}
private AnnotationMetadata initializeAnnotationMetadata() {
AnnotationMetadata annotationMetadata = resolveAnnotationMetadata();
if (annotationMetadata != AnnotationMetadata.EMPTY_METADATA) {
return new BeanAnnotationMetadata(annotationMetadata);
} else {
return AnnotationMetadata.EMPTY_METADATA;
}
}
private AbstractBeanDefinition addInjectionPointInternal(
Class declaringType,
String method,
@Nullable Argument[] arguments,
@Nullable AnnotationMetadata annotationMetadata,
boolean requiresReflection,
List<MethodInjectionPoint> targetInjectionPoints) {
boolean isPreDestroy = targetInjectionPoints == this.preDestroyMethods;
boolean isPostConstruct = targetInjectionPoints == this.postConstructMethods;
MethodInjectionPoint injectionPoint;
if (requiresReflection) {
injectionPoint = new ReflectionMethodInjectionPoint(
this,
declaringType,
method,
arguments,
annotationMetadata
);
} else {
injectionPoint = new DefaultMethodInjectionPoint(
this,
declaringType,
method,
arguments,
annotationMetadata
);
}
targetInjectionPoints.add(injectionPoint);
if (isPostConstruct || isPreDestroy) {
this.methodInjectionPoints.add(injectionPoint);
}
addRequiredComponents(arguments);
return this;
}
private Object getBeanForMethodArgument(BeanResolutionContext resolutionContext, BeanContext context, MethodInjectionPoint injectionPoint, Argument argument) {
Class argumentType = argument.getType();
if (argumentType.isArray()) {
Collection beansOfType = getBeansOfTypeForMethodArgument(resolutionContext, context, injectionPoint, argument);
return beansOfType.toArray((Object[]) Array.newInstance(argumentType.getComponentType(), beansOfType.size()));
} else if (Collection.class.isAssignableFrom(argumentType)) {
Collection beansOfType = getBeansOfTypeForMethodArgument(resolutionContext, context, injectionPoint, argument);
return coerceCollectionToCorrectType(argumentType, beansOfType);
} else if (Stream.class.isAssignableFrom(argumentType)) {
return streamOfTypeForMethodArgument(resolutionContext, context, injectionPoint, argument);
} else if (Provider.class.isAssignableFrom(argumentType)) {
return getBeanProviderForMethodArgument(resolutionContext, context, injectionPoint, argument);
} else if (Optional.class.isAssignableFrom(argumentType)) {
return findBeanForMethodArgument(resolutionContext, context, injectionPoint, argument);
} else {
BeanResolutionContext.Path path = resolutionContext.getPath();
path.pushMethodArgumentResolve(this, injectionPoint, argument);
try {
Qualifier qualifier = resolveQualifier(resolutionContext, argument);
@SuppressWarnings("unchecked")
Object bean = ((DefaultBeanContext) context).getBean(resolutionContext, argumentType, qualifier);
path.pop();
return bean;
} catch (DisabledBeanException e) {
if (AbstractBeanContextConditional.LOG.isDebugEnabled()) {
AbstractBeanContextConditional.LOG.debug("Bean of type [{}] disabled for reason: {}", argumentType.getSimpleName(), e.getMessage());
}
if (isIterable() && getAnnotationMetadata().hasDeclaredAnnotation(EachBean.class)) {
throw new DisabledBeanException("Bean [" + getBeanType().getSimpleName() + "] disabled by parent: " + e.getMessage());
} else {
if (argument.isDeclaredNullable()) {
path.pop();
return null;
}
throw new DependencyInjectionException(resolutionContext, argument, e);
}
} catch (NoSuchBeanException e) {
if (argument.isDeclaredNullable()) {
path.pop();
return null;
}
throw new DependencyInjectionException(resolutionContext, argument, e);
}
}
}
private Optional resolveValue(
ApplicationContext context,
ArgumentConversionContext<?> argument,
boolean hasValueAnnotation,
String valString) {
if (hasValueAnnotation) {
return context.resolvePlaceholders(valString).flatMap(v ->
context.getConversionService().convert(v, argument)
);
} else {
Optional<?> value = context.getProperty(valString, argument);
if (!value.isPresent() && isConfigurationProperties()) {
String cliOption = resolveCliOption(argument.getArgument().getName());
if (cliOption != null) {
return context.getProperty(cliOption, argument);
}
}
return value;
}
}
private String resolvePropertyValueName(
BeanResolutionContext resolutionContext,
AnnotationMetadata annotationMetadata,
Argument argument,
String valueAnnStr) {
String valString;
if (valueAnnStr != null) {
valString = valueAnnStr;
} else {
valString = annotationMetadata.stringValue(Property.class, "name")
.orElseGet(() ->
argument.getAnnotationMetadata().stringValue(Property.class, "name")
.orElseThrow(() ->
new DependencyInjectionException(
resolutionContext,
argument,
"Value resolution attempted but @Value annotation is missing"
)
)
);
valString = substituteWildCards(resolutionContext, valString);
}
return valString;
}
private String resolvePropertyValueName(
BeanResolutionContext resolutionContext,
FieldInjectionPoint injectionPoint,
String valueAnn,
AnnotationMetadata annotationMetadata) {
String valString;
if (valueAnn != null) {
valString = valueAnn;
} else {
valString = annotationMetadata.stringValue(Property.class, "name")
.orElseThrow(() -> new DependencyInjectionException(resolutionContext, injectionPoint, "Value resolution attempted but @Value annotation is missing"));
valString = substituteWildCards(resolutionContext, valString);
}
return valString;
}
private String resolvePropertyPath(
BeanResolutionContext resolutionContext,
String path) {
String valString = getConfigurationPropertiesPath(resolutionContext);
return valString + "." + path;
}
private String getConfigurationPropertiesPath(BeanResolutionContext resolutionContext) {
String valString = getAnnotationMetadata()
.stringValue(ConfigurationReader.class, "prefix")
.orElseThrow(() -> new IllegalStateException("Resolve property path called for non @ConfigurationProperties bean"));
valString = substituteWildCards(
resolutionContext,
valString
);
return valString;
}
private String substituteWildCards(BeanResolutionContext resolutionContext, String valString) {
if (valString.indexOf('*') > -1) {
Optional<String> namedBean = resolutionContext.get(Named.class.getName(), ConversionContext.STRING);
if (namedBean.isPresent()) {
valString = valString.replace("*", namedBean.get());
}
}
return valString;
}
private String resolveCliOption(String name) {
String attr = "cliPrefix";
AnnotationMetadata annotationMetadata = getAnnotationMetadata();
if (annotationMetadata.isPresent(ConfigurationProperties.class, attr)) {
return annotationMetadata.stringValue(ConfigurationProperties.class, attr).map(val -> val + name).orElse(null);
}
return null;
}
private boolean isInnerConfiguration(Class<?> argumentType, BeanContext beanContext) {
return isConfigurationProperties &&
argumentType.getName().indexOf('$') > -1 &&
!argumentType.isEnum() &&
!argumentType.isPrimitive() &&
Modifier.isPublic(argumentType.getModifiers()) && Modifier.isStatic(argumentType.getModifiers()) &&
isInnerOfAnySuperclass(argumentType) &&
beanContext.findBeanDefinition(argumentType).map(bd -> bd.hasStereotype(ConfigurationReader.class) || bd.isIterable()).isPresent();
}
private boolean isInnerOfAnySuperclass(Class argumentType) {
Class beanType = getBeanType();
while (beanType != null) {
if ((beanType.getName() + "$" + argumentType.getSimpleName()).equals(argumentType.getName())) {
return true;
}
beanType = beanType.getSuperclass();
}
return false;
}
private <B, X extends RuntimeException> B resolveBeanWithGenericsFromMethodArgument(BeanResolutionContext resolutionContext, MethodInjectionPoint injectionPoint, Argument argument, BeanResolver<B> beanResolver) throws X {
BeanResolutionContext.Path path = resolutionContext.getPath();
path.pushMethodArgumentResolve(this, injectionPoint, argument);
try {
Qualifier qualifier = resolveQualifier(resolutionContext, argument);
Class argumentType = argument.getType();
Class genericType = resolveGenericType(argument, argumentType);
@SuppressWarnings("unchecked") B bean = (B) beanResolver.resolveBean(genericType != null ? genericType : argumentType, qualifier);
path.pop();
return bean;
} catch (NoSuchBeanException e) {
throw new DependencyInjectionException(resolutionContext, injectionPoint, argument, e);
}
}
private Class resolveGenericType(Argument argument, Class argumentType) {
Class genericType;
if (argumentType.isArray()) {
genericType = argumentType.getComponentType();
} else {
Map<String, Argument<?>> genericTypes = argument.getTypeVariables();
if (genericTypes.size() == 1) {
genericType = genericTypes.values().iterator().next().getType();
} else {
genericType = null;
}
}
return genericType;
}
private <B> B resolveBeanWithGenericsFromConstructorArgument(BeanResolutionContext resolutionContext, Argument argument, BeanResolver<B> beanResolver) {
BeanResolutionContext.Path path = resolutionContext.getPath();
path.pushConstructorResolve(this, argument);
try {
Class argumentType = argument.getType();
Class genericType = resolveGenericType(argument, argumentType);
Qualifier qualifier = resolveQualifier(resolutionContext, argument);
@SuppressWarnings("unchecked") B bean = (B) beanResolver.resolveBean(genericType != null ? genericType : argumentType, qualifier);
path.pop();
return bean;
} catch (NoSuchBeanException e) {
if (argument.isNullable()) {
path.pop();
return null;
}
throw new DependencyInjectionException(resolutionContext, argument, e);
}
}
private <B> B resolveBeanWithGenericsForField(BeanResolutionContext resolutionContext, FieldInjectionPoint injectionPoint, BeanResolver<B> beanResolver) {
BeanResolutionContext.Path path = resolutionContext.getPath();
path.pushFieldResolve(this, injectionPoint);
Argument argument = injectionPoint.asArgument();
try {
Optional<Class> genericType = injectionPoint.getType().isArray() ? Optional.of(injectionPoint.getType().getComponentType()) : argument.getFirstTypeVariable().map(Argument::getType);
Qualifier qualifier = resolveQualifier(resolutionContext, injectionPoint.asArgument());
@SuppressWarnings("unchecked") B bean = (B) beanResolver.resolveBean(genericType.orElse(injectionPoint.getType()), qualifier);
path.pop();
return bean;
} catch (NoSuchBeanException e) {
if (argument.isNullable()) {
path.pop();
return null;
}
throw new DependencyInjectionException(resolutionContext, injectionPoint, e);
}
}
private boolean isConfigurationProperties() {
return isConfigurationProperties;
}
private Qualifier resolveQualifier(BeanResolutionContext resolutionContext, Argument argument) {
return resolveQualifier(resolutionContext, argument, false);
}
private Qualifier resolveQualifier(
BeanResolutionContext resolutionContext,
Argument argument,
boolean innerConfiguration) {
AnnotationMetadata annotationMetadata = argument.getAnnotationMetadata();
boolean hasMetadata = annotationMetadata != AnnotationMetadata.EMPTY_METADATA;
Class<? extends Annotation> qualifierType = hasMetadata ? annotationMetadata.getAnnotationTypeByStereotype(javax.inject.Qualifier.class).orElse(null) : null;
if (qualifierType != null) {
return Qualifiers.byAnnotation(
annotationMetadata,
qualifierType
);
} else {
Class<?>[] byType = hasMetadata ? annotationMetadata.hasDeclaredAnnotation(Type.class) ? annotationMetadata.classValues(Type.class) : null : null;
if (byType != null) {
return Qualifiers.byType(byType);
} else {
Qualifier qualifier = null;
boolean isIterable = isIterable() || resolutionContext.get(EachProperty.class.getName(), Class.class).map(getBeanType()::equals).orElse(false);
if (isIterable) {
Optional<Qualifier> optional = resolutionContext.get(javax.inject.Qualifier.class.getName(), Map.class)
.map(map -> (Qualifier) map.get(argument));
qualifier = optional.orElse(null);
}
if (qualifier == null) {
if ((hasMetadata && argument.isAnnotationPresent(Parameter.class)) ||
(innerConfiguration && isIterable) ||
Qualifier.class == argument.getType()) {
final Optional<String> n = resolutionContext.get(NAMED_ATTRIBUTE, ConversionContext.STRING);
qualifier = n.map(Qualifiers::byName).orElse(null);
}
}
return qualifier;
}
}
}
@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
private Object resolveOptionalObject(Optional value) {
if (!value.isPresent()) {
return value;
} else {
Object convertedOptional = value.get();
if (convertedOptional instanceof Optional) {
return convertedOptional;
} else {
return value;
}
}
}
@SuppressWarnings("unchecked")
private Object coerceCollectionToCorrectType(Class collectionType, Collection beansOfType) {
if (collectionType.isInstance(beansOfType)) {
return beansOfType;
} else {
return CollectionUtils.convertCollection(collectionType, beansOfType).orElse(null);
}
}
private void addRequiredComponents(Argument... arguments) {
if (arguments != null) {
for (Argument argument : arguments) {
requiredComponents.add(argument.getType());
}
}
}
private void instrumentAnnotationMetadata(BeanContext context, Object object) {
if (object instanceof EnvironmentConfigurable && context instanceof ApplicationContext) {
((EnvironmentConfigurable) object).configure(((ApplicationContext) context).getEnvironment());
}
}
private final class BeanAnnotationMetadata extends AbstractEnvironmentAnnotationMetadata {
BeanAnnotationMetadata(AnnotationMetadata targetMetadata) {
super(targetMetadata);
}
@Nullable
@Override
protected Environment getEnvironment() {
return environment;
}
}
private final class MethodKey {
final String name;
final Class[] argumentTypes;
MethodKey(String name, Class[] argumentTypes) {
this.name = name;
this.argumentTypes = argumentTypes;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
@SuppressWarnings("unchecked") MethodKey methodKey = (MethodKey) o;
if (!name.equals(methodKey.name)) {
return false;
}
return Arrays.equals(argumentTypes, methodKey.argumentTypes);
}
@Override
public int hashCode() {
int result = name.hashCode();
result = 31 * result + Arrays.hashCode(argumentTypes);
return result;
}
}
private interface BeanResolver<T> {
T resolveBean(Class<T> beanType, Qualifier<T> qualifier);
}
}