AbstractInitializableBeanDefinition.java
/*
* 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.DefaultBeanContext.ListenersSupplier;
import io.micronaut.context.annotation.ConfigurationProperties;
import io.micronaut.context.annotation.EachBean;
import io.micronaut.context.annotation.Parameter;
import io.micronaut.context.annotation.Property;
import io.micronaut.context.annotation.Value;
import io.micronaut.context.env.ConfigurationPath;
import io.micronaut.context.env.Environment;
import io.micronaut.context.event.BeanInitializedEventListener;
import io.micronaut.context.event.BeanInitializingEvent;
import io.micronaut.context.exceptions.BeanContextException;
import io.micronaut.context.exceptions.BeanInstantiationException;
import io.micronaut.context.exceptions.DependencyInjectionException;
import io.micronaut.context.exceptions.DisabledBeanException;
import io.micronaut.context.exceptions.NoSuchBeanException;
import io.micronaut.core.annotation.AnnotationMetadata;
import io.micronaut.core.annotation.AnnotationUtil;
import io.micronaut.core.annotation.Internal;
import io.micronaut.core.annotation.NonNull;
import io.micronaut.core.annotation.Nullable;
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.convert.ConversionService;
import io.micronaut.core.naming.Named;
import io.micronaut.core.reflect.ClassUtils;
import io.micronaut.core.reflect.ReflectionUtils;
import io.micronaut.core.type.Argument;
import io.micronaut.core.util.CollectionUtils;
import io.micronaut.core.value.PropertyResolver;
import io.micronaut.inject.BeanDefinition;
import io.micronaut.inject.ConstructorInjectionPoint;
import io.micronaut.inject.ExecutableMethod;
import io.micronaut.inject.ExecutableMethodsDefinition;
import io.micronaut.inject.FieldInjectionPoint;
import io.micronaut.inject.InjectableBeanDefinition;
import io.micronaut.inject.InjectionPoint;
import io.micronaut.inject.InstantiatableBeanDefinition;
import io.micronaut.inject.MethodInjectionPoint;
import io.micronaut.inject.ValidatedBeanDefinition;
import io.micronaut.inject.annotation.AbstractEnvironmentAnnotationMetadata;
import io.micronaut.inject.annotation.EvaluatedAnnotationMetadata;
import io.micronaut.inject.qualifiers.InterceptorBindingQualifier;
import io.micronaut.inject.qualifiers.Qualifiers;
import io.micronaut.inject.qualifiers.TypeAnnotationQualifier;
import jakarta.inject.Singleton;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.OptionalDouble;
import java.util.OptionalInt;
import java.util.OptionalLong;
import java.util.Set;
import java.util.function.Consumer;
import java.util.stream.Stream;
/**
* <p>Default implementation of the {@link BeanDefinition} interface. This class is generally not used directly in user
* code.
* Instead a build time tool does analysis of source code and dynamically produces subclasses of this class containing
* information about the available injection points for a given class.</p>
*
* <p>For technical reasons the class has to be marked as public, but is regarded as internal and should be used by
* compiler tools and plugins (such as AST transformation frameworks)</p>
*
* <p>The {@code io.micronaut.inject.writer.BeanDefinitionWriter} class can be used to produce bean definitions at
* compile or runtime</p>
*
* @param <T> The Bean definition type
* @author Graeme Rocher
* @author Denis Stepanov
* @since 3.0
*/
@Internal
public abstract class AbstractInitializableBeanDefinition<T> extends AbstractBeanContextConditional
implements InstantiatableBeanDefinition<T>, InjectableBeanDefinition<T>, EnvironmentConfigurable, BeanContextConfigurable {
@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
private static final Optional<Class<? extends Annotation>> SINGLETON_SCOPE = Optional.of(Singleton.class);
private final Class<T> type;
private final AnnotationMetadata annotationMetadata;
private final PrecalculatedInfo precalculatedInfo;
@Nullable
private final MethodOrFieldReference constructor;
@Nullable
private final MethodReference[] methodInjection;
@Nullable
private final FieldReference[] fieldInjection;
@Nullable
private final ExecutableMethodsDefinition<T> executableMethodsDefinition;
@Nullable
private final Map<String, Argument<?>[]> typeArgumentsMap;
@Nullable
private AnnotationReference[] annotationInjection;
@Nullable
private Environment environment;
@Nullable
private Optional<Argument<?>> containerElement;
@Nullable
private ConstructorInjectionPoint<T> constructorInjectionPoint;
@Nullable
private List<MethodInjectionPoint<T, ?>> methodInjectionPoints;
@Nullable
private List<FieldInjectionPoint<T, ?>> fieldInjectionPoints;
@Nullable
private List<MethodInjectionPoint<T, ?>> postConstructMethods;
@Nullable
private List<MethodInjectionPoint<T, ?>> preDestroyMethods;
@Nullable
private Collection<Class<?>> requiredComponents;
@Nullable
private Argument<?>[] requiredParametrizedArguments;
private Qualifier<T> declaredQualifier;
@Internal
@UsedByGeneratedCode
protected AbstractInitializableBeanDefinition(
Class<T> beanType,
@Nullable MethodOrFieldReference constructor,
@Nullable AnnotationMetadata annotationMetadata,
@Nullable MethodReference[] methodInjection,
@Nullable FieldReference[] fieldInjection,
@Nullable AnnotationReference[] annotationInjection,
@Nullable ExecutableMethodsDefinition<T> executableMethodsDefinition,
@Nullable Map<String, Argument<?>[]> typeArgumentsMap,
@NonNull PrecalculatedInfo precalculatedInfo) {
this.type = beanType;
if (annotationMetadata == null || annotationMetadata == AnnotationMetadata.EMPTY_METADATA) {
this.annotationMetadata = AnnotationMetadata.EMPTY_METADATA;
} else {
AnnotationMetadata beanAnnotationMetadata = annotationMetadata;
if (annotationMetadata.hasPropertyExpressions()) {
// we make a copy of the result of annotation metadata which is normally a reference
// to the class metadata
beanAnnotationMetadata = new BeanAnnotationMetadata(annotationMetadata);
}
this.annotationMetadata = EvaluatedAnnotationMetadata.wrapIfNecessary(beanAnnotationMetadata);
}
this.constructor = constructor;
this.methodInjection = methodInjection;
this.fieldInjection = fieldInjection;
this.annotationInjection = annotationInjection;
this.executableMethodsDefinition = executableMethodsDefinition;
this.typeArgumentsMap = typeArgumentsMap;
this.precalculatedInfo = precalculatedInfo;
}
@Override
public Qualifier<T> getDeclaredQualifier() {
if (declaredQualifier == null) {
declaredQualifier = InstantiatableBeanDefinition.super.getDeclaredQualifier();
}
return declaredQualifier;
}
@Override
public final boolean isContainerType() {
return precalculatedInfo.isContainerType;
}
@Override
@SuppressWarnings("java:S2789") // performance optimization
public final Optional<Argument<?>> getContainerElement() {
if (precalculatedInfo.isContainerType) {
if (containerElement != null) {
return containerElement;
}
if (getBeanType().isArray()) {
containerElement = Optional.of(Argument.of(getBeanType().getComponentType()));
} else {
final List<Argument<?>> iterableArguments = getTypeArguments(Iterable.class);
if (!iterableArguments.isEmpty()) {
containerElement = Optional.of(iterableArguments.iterator().next());
}
}
return containerElement;
}
return Optional.empty();
}
@Override
public final boolean hasPropertyExpressions() {
return getAnnotationMetadata().hasPropertyExpressions();
}
@Override
public boolean hasEvaluatedExpressions() {
return precalculatedInfo.hasEvaluatedExpressions();
}
@Override
public final @NonNull
List<Argument<?>> getTypeArguments(String type) {
if (type == null || typeArgumentsMap == null) {
return Collections.emptyList();
}
Argument<?>[] arguments = typeArgumentsMap.get(type);
if (arguments != null) {
return Arrays.asList(arguments);
}
return Collections.emptyList();
}
@Override
@NonNull
public AnnotationMetadata getAnnotationMetadata() {
return annotationMetadata;
}
@Override
public boolean isAbstract() {
return precalculatedInfo.isAbstract;
}
@Override
public boolean isIterable() {
return precalculatedInfo.isIterable;
}
@Override
public final boolean isConfigurationProperties() {
return precalculatedInfo.isConfigurationProperties;
}
@Override
public boolean isPrimary() {
return precalculatedInfo.isPrimary;
}
@Override
public boolean requiresMethodProcessing() {
return precalculatedInfo.requiresMethodProcessing;
}
@Override
public final <R> Optional<ExecutableMethod<T, R>> findMethod(String name, Class<?>... argumentTypes) {
if (executableMethodsDefinition == null) {
return Optional.empty();
}
return executableMethodsDefinition.findMethod(name, argumentTypes);
}
@Override
public final <R> Stream<ExecutableMethod<T, R>> findPossibleMethods(String name) {
if (executableMethodsDefinition == null) {
return Stream.empty();
}
return executableMethodsDefinition.findPossibleMethods(name);
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
return o != null && getClass() == o.getClass();
}
@Override
public int hashCode() {
return getClass().hashCode();
}
@Override
public String toString() {
Class<?> declaringType = constructor == null ? type : constructor.declaringType;
return "Definition: " + declaringType.getName();
}
@Override
public boolean isSingleton() {
return precalculatedInfo.isSingleton;
}
@Override
public final Optional<Class<? extends Annotation>> getScope() {
return precalculatedInfo.scope.flatMap(scopeClassName -> {
if (Singleton.class.getName().equals(scopeClassName)) {
return SINGLETON_SCOPE;
}
return (Optional) ClassUtils.forName(scopeClassName, getClass().getClassLoader());
});
}
@Override
public final Optional<String> getScopeName() {
return precalculatedInfo.scope;
}
@Override
public final Class<T> getBeanType() {
return type;
}
@Override
@NonNull
public Set<Class<?>> getExposedTypes() {
return Collections.EMPTY_SET;
}
@Override
public final Optional<Class<?>> getDeclaringType() {
if (constructor == null) {
return Optional.of(type);
}
return Optional.of(constructor.declaringType);
}
@Override
public final ConstructorInjectionPoint<T> getConstructor() {
if (constructorInjectionPoint != null) {
return constructorInjectionPoint;
}
if (constructor == null) {
DefaultConstructorInjectionPoint<T> point = new DefaultConstructorInjectionPoint<>(
this,
getBeanType(),
AnnotationMetadata.EMPTY_METADATA,
Argument.ZERO_ARGUMENTS
);
if (environment != null) {
point.configure(environment);
}
constructorInjectionPoint = point;
} else if (constructor instanceof MethodReference methodConstructor) {
if ("<init>".equals(methodConstructor.methodName)) {
DefaultConstructorInjectionPoint<T> point = new DefaultConstructorInjectionPoint<>(
this,
methodConstructor.declaringType,
methodConstructor.annotationMetadata,
methodConstructor.arguments
);
if (environment != null) {
point.configure(environment);
}
constructorInjectionPoint = point;
} else {
DefaultMethodConstructorInjectionPoint<T> point = new DefaultMethodConstructorInjectionPoint<>(
this,
methodConstructor.declaringType,
methodConstructor.methodName,
methodConstructor.arguments,
methodConstructor.annotationMetadata
);
if (environment != null) {
point.configure(environment);
}
constructorInjectionPoint = point;
}
} else if (constructor instanceof FieldReference fieldConstructor) {
DefaultFieldConstructorInjectionPoint<T> point = new DefaultFieldConstructorInjectionPoint<>(
this,
fieldConstructor.declaringType,
type,
fieldConstructor.argument.getName(),
fieldConstructor.argument.getAnnotationMetadata()
);
if (environment != null) {
point.configure(environment);
}
constructorInjectionPoint = point;
}
return constructorInjectionPoint;
}
@Override
public final Collection<Class<?>> getRequiredComponents() {
if (requiredComponents != null) {
return requiredComponents;
}
Set<Class<?>> requiredComponents = new HashSet<>();
Consumer<Argument> argumentConsumer = argument -> {
if (argument.isContainerType() || argument.isProvider()) {
argument.getFirstTypeVariable()
.map(Argument::getType)
.ifPresent(requiredComponents::add);
} else {
requiredComponents.add(argument.getType());
}
};
if (constructor != null) {
if (constructor instanceof MethodReference methodConstructor) {
if (methodConstructor.arguments != null && methodConstructor.arguments.length > 0) {
for (Argument<?> argument : methodConstructor.arguments) {
argumentConsumer.accept(argument);
}
}
}
}
if (methodInjection != null) {
for (MethodReference methodReference : methodInjection) {
if (methodReference.annotationMetadata.hasDeclaredAnnotation(AnnotationUtil.INJECT)) {
if (methodReference.arguments != null && methodReference.arguments.length > 0) {
for (Argument<?> argument : methodReference.arguments) {
argumentConsumer.accept(argument);
}
}
}
}
}
if (fieldInjection != null) {
for (FieldReference fieldReference : fieldInjection) {
if (annotationMetadata != null && annotationMetadata.hasDeclaredAnnotation(AnnotationUtil.INJECT)) {
argumentConsumer.accept(fieldReference.argument);
}
}
}
if (annotationInjection != null) {
for (AnnotationReference annotationReference : annotationInjection) {
if (annotationReference.argument != null) {
argumentConsumer.accept(annotationReference.argument);
}
}
}
this.requiredComponents = Collections.unmodifiableSet(requiredComponents);
return this.requiredComponents;
}
@Override
public final List<MethodInjectionPoint<T, ?>> getInjectedMethods() {
if (methodInjection == null) {
return Collections.emptyList();
}
if (methodInjectionPoints != null) {
return methodInjectionPoints;
}
List<MethodInjectionPoint<T, ?>> methodInjectionPoints = new ArrayList<>(methodInjection.length);
for (MethodReference methodReference : methodInjection) {
MethodInjectionPoint<T, ?> methodInjectionPoint = new DefaultMethodInjectionPoint<>(
this,
methodReference.declaringType,
methodReference.methodName,
methodReference.arguments,
methodReference.annotationMetadata
);
methodInjectionPoints.add(methodInjectionPoint);
if (environment != null) {
((EnvironmentConfigurable) methodInjectionPoint).configure(environment);
}
}
this.methodInjectionPoints = Collections.unmodifiableList(methodInjectionPoints);
return this.methodInjectionPoints;
}
@Override
public final List<FieldInjectionPoint<T, ?>> getInjectedFields() {
if (fieldInjection == null) {
return Collections.emptyList();
}
if (fieldInjectionPoints != null) {
return fieldInjectionPoints;
}
List<FieldInjectionPoint<T, ?>> fieldInjectionPoints = new ArrayList<>(fieldInjection.length);
for (FieldReference fieldReference : fieldInjection) {
FieldInjectionPoint<T, ?> fieldInjectionPoint = new DefaultFieldInjectionPoint<>(
this,
fieldReference.declaringType,
fieldReference.argument.getType(),
fieldReference.argument.getName(),
fieldReference.argument.getAnnotationMetadata(),
fieldReference.argument.getTypeParameters()
);
if (environment != null) {
((EnvironmentConfigurable) fieldInjectionPoint).configure(environment);
}
fieldInjectionPoints.add(fieldInjectionPoint);
}
this.fieldInjectionPoints = Collections.unmodifiableList(fieldInjectionPoints);
return this.fieldInjectionPoints;
}
@Override
public final List<MethodInjectionPoint<T, ?>> getPostConstructMethods() {
if (methodInjection == null) {
return Collections.emptyList();
}
if (postConstructMethods != null) {
return postConstructMethods;
}
List<MethodInjectionPoint<T, ?>> postConstructMethods = new ArrayList<>(1);
for (MethodInjectionPoint<T, ?> methodInjectionPoint : getInjectedMethods()) {
if (methodInjectionPoint.isPostConstructMethod()) {
postConstructMethods.add(methodInjectionPoint);
}
}
this.postConstructMethods = Collections.unmodifiableList(postConstructMethods);
return this.postConstructMethods;
}
@Override
public final List<MethodInjectionPoint<T, ?>> getPreDestroyMethods() {
if (methodInjection == null) {
return Collections.emptyList();
}
if (preDestroyMethods != null) {
return preDestroyMethods;
}
List<MethodInjectionPoint<T, ?>> preDestroyMethods = new ArrayList<>(1);
for (MethodInjectionPoint<T, ?> methodInjectionPoint : getInjectedMethods()) {
if (methodInjectionPoint.isPreDestroyMethod()) {
preDestroyMethods.add(methodInjectionPoint);
}
}
this.preDestroyMethods = Collections.unmodifiableList(preDestroyMethods);
return this.preDestroyMethods;
}
@Override
@NonNull
public final String getName() {
return getBeanType().getName();
}
@Override
public T inject(BeanResolutionContext resolutionContext, BeanContext context, T bean) {
return bean;
}
@Override
public final Collection<ExecutableMethod<T, ?>> getExecutableMethods() {
if (executableMethodsDefinition == null) {
return Collections.emptyList();
}
return executableMethodsDefinition.getExecutableMethods();
}
/**
* Configures the bean for the given {@link BeanContext}. If the context features an
* {@link Environment} this method configures the annotation metadata such that
* environment aware values are returned.
*
* @param environment The environment
*/
@Internal
@Override
public final void configure(Environment environment) {
if (environment != null) {
this.environment = environment;
if (constructorInjectionPoint instanceof EnvironmentConfigurable environmentConfigurable) {
environmentConfigurable.configure(environment);
}
if (methodInjectionPoints != null) {
for (MethodInjectionPoint<T, ?> methodInjectionPoint : methodInjectionPoints) {
if (methodInjectionPoint instanceof EnvironmentConfigurable environmentConfigurable) {
environmentConfigurable.configure(environment);
}
}
}
if (fieldInjectionPoints != null) {
for (FieldInjectionPoint<T, ?> fieldInjectionPoint : fieldInjectionPoints) {
if (fieldInjectionPoint instanceof EnvironmentConfigurable environmentConfigurable) {
environmentConfigurable.configure(environment);
}
}
}
if (executableMethodsDefinition instanceof EnvironmentConfigurable environmentConfigurable) {
environmentConfigurable.configure(environment);
}
}
}
@Override
public void configure(BeanContext beanContext) {
if (beanContext == null || !hasEvaluatedExpressions()) {
return;
}
if (annotationMetadata instanceof EvaluatedAnnotationMetadata eam) {
eam.configure(beanContext);
eam.setBeanDefinition(this);
}
if (constructor != null) {
if (constructor instanceof MethodReference mr) {
if (mr.annotationMetadata instanceof EvaluatedAnnotationMetadata eam) {
eam.configure(beanContext);
eam.setBeanDefinition(this);
}
if (mr.arguments != null) {
for (Argument<?> argument: mr.arguments) {
if (argument instanceof ExpressionsAwareArgument<?> exprArg) {
exprArg.configure(beanContext);
exprArg.setBeanDefinition(this);
}
}
}
}
if (constructor instanceof FieldReference fr
&& fr.argument instanceof ExpressionsAwareArgument<?> exprArg) {
exprArg.configure(beanContext);
exprArg.setBeanDefinition(this);
}
}
if (constructorInjectionPoint != null) {
if (constructorInjectionPoint.getAnnotationMetadata() instanceof EvaluatedAnnotationMetadata eam) {
eam.configure(beanContext);
eam.setBeanDefinition(this);
}
}
if (methodInjection != null) {
for (MethodReference methodReference: methodInjection) {
if (methodReference.annotationMetadata instanceof EvaluatedAnnotationMetadata eam) {
eam.configure(beanContext);
eam.setBeanDefinition(this);
}
if (methodReference.arguments != null) {
for (Argument<?> argument: methodReference.arguments) {
if (argument instanceof ExpressionsAwareArgument<?> exprArg) {
exprArg.configure(beanContext);
exprArg.setBeanDefinition(this);
}
}
}
}
}
if (methodInjectionPoints != null) {
for (MethodInjectionPoint<T, ?> methodInjectionPoint : methodInjectionPoints) {
if (methodInjectionPoint.getAnnotationMetadata() instanceof EvaluatedAnnotationMetadata eam) {
eam.configure(beanContext);
eam.setBeanDefinition(this);
}
}
}
if (fieldInjection != null) {
for (FieldReference fieldReference: fieldInjection) {
if (fieldReference.argument instanceof ExpressionsAwareArgument<?> exprArg) {
exprArg.configure(beanContext);
exprArg.setBeanDefinition(this);
}
}
}
if (fieldInjectionPoints != null) {
for (FieldInjectionPoint<T, ?> fieldInjectionPoint : fieldInjectionPoints) {
if (fieldInjectionPoint.getAnnotationMetadata() instanceof EvaluatedAnnotationMetadata eam) {
eam.configure(beanContext);
eam.setBeanDefinition(this);
}
}
}
if (executableMethodsDefinition instanceof BeanContextConfigurable ctxConfigurable) {
ctxConfigurable.configure(beanContext);
}
}
/**
* Allows printing warning messages produced by the compiler.
*
* @param message The message
* @deprecated No longer used
*/
@Internal
@Deprecated(forRemoval = true, since = "4.4.0")
protected final void warn(String message) {
}
/**
* Allows printing warning messages produced by the compiler.
*
* @param type The type
* @param method The method
* @param property The property
* @deprecated No longer used
*/
@SuppressWarnings("unused")
@Internal
@Deprecated(forRemoval = true, since = "4.4.0")
protected final void warnMissingProperty(Class type, String method, String property) {
}
/**
* Implementing possible {@link io.micronaut.inject.ParametrizedInstantiatableBeanDefinition#getRequiredArguments()}.
*
* @return The arguments required to construct parametrized bean
*/
public final Argument<?>[] getRequiredArguments() {
if (requiredParametrizedArguments != null) {
return requiredParametrizedArguments;
}
ConstructorInjectionPoint<T> ctor = getConstructor();
if (ctor != null) {
requiredParametrizedArguments = Arrays.stream(ctor.getArguments())
.filter(arg -> {
Optional<String> qualifierType = AnnotationUtil.findQualifierAnnotation(arg.getAnnotationMetadata());
return qualifierType.isPresent() && qualifierType.get().equals(Parameter.class.getName());
})
.toArray(Argument[]::new);
} else {
requiredParametrizedArguments = Argument.ZERO_ARGUMENTS;
}
return requiredParametrizedArguments;
}
/**
* Implementing possible {@link io.micronaut.inject.ParametrizedInstantiatableBeanDefinition#instantiate(BeanResolutionContext, BeanContext)}.
*
* @param resolutionContext The {@link BeanResolutionContext}
* @param context The {@link BeanContext}
* @param requiredArgumentValues The required arguments values. The keys should match the names of the arguments
* returned by {@link #getRequiredArguments()}
* @return The instantiated bean
* @throws BeanInstantiationException If the bean cannot be instantiated for the arguments supplied
*/
@SuppressWarnings({"java:S2789", "OptionalAssignedToNull"}) // performance optimization
public final T instantiate(BeanResolutionContext resolutionContext,
BeanContext context,
Map<String, Object> requiredArgumentValues) throws BeanInstantiationException {
requiredArgumentValues = requiredArgumentValues != null ? new LinkedHashMap<>(requiredArgumentValues) : Collections.emptyMap();
Optional<Class> eachBeanType = null;
for (Argument<?> requiredArgument : getRequiredArguments()) {
try (BeanResolutionContext.Path ignored = resolutionContext.getPath().pushConstructorResolve(this, requiredArgument)) {
String argumentName = requiredArgument.getName();
Object value = requiredArgumentValues.get(argumentName);
if (value == null && !requiredArgument.isNullable()) {
if (eachBeanType == null) {
eachBeanType = classValue(EachBean.class);
}
if (eachBeanType.filter(type -> type == requiredArgument.getType()).isPresent()) {
throw new DisabledBeanException("@EachBean parameter disabled for argument: " + requiredArgument.getName());
}
throw new BeanInstantiationException(resolutionContext, "Missing bean argument value: " + argumentName);
}
boolean requiresConversion = value != null && !requiredArgument.getType().isInstance(value);
if (requiresConversion) {
Optional<?> converted = context.getConversionService().convert(value, requiredArgument.getType(), ConversionContext.of(requiredArgument));
Object finalValue = value;
value = converted.orElseThrow(() -> new BeanInstantiationException(resolutionContext, "Invalid value [" + finalValue + "] for argument: " + argumentName));
requiredArgumentValues.put(argumentName, value);
}
}
}
return doInstantiate(resolutionContext, context, requiredArgumentValues);
}
/**
* Method to be implemented by the generated code if the bean definition is implementing {@link io.micronaut.inject.ParametrizedInstantiatableBeanDefinition}.
*
* @param resolutionContext The resolution context
* @param context The bean context
* @param requiredArgumentValues The required arguments
* @return The built instance
*/
@Internal
@UsedByGeneratedCode
protected T doInstantiate(BeanResolutionContext resolutionContext, BeanContext context, Map<String, Object> requiredArgumentValues) {
throw new IllegalStateException("Method must be implemented for 'ParametrizedInstantiatableBeanDefinition' instance!");
}
/**
* Default postConstruct hook that only invokes methods that require reflection. Generated subclasses should
* override to call methods that don't require reflection.
*
* @param resolutionContext The resolution hook
* @param context The context
* @param bean The bean
* @return The bean
*/
@SuppressWarnings({"unused", "unchecked"})
@Internal
@UsedByGeneratedCode
protected Object postConstruct(BeanResolutionContext resolutionContext, BeanContext context, Object bean) {
final List<Map.Entry<Class<?>, ListenersSupplier<BeanInitializedEventListener>>> beanInitializedEventListeners
= ((DefaultBeanContext) context).beanInitializedEventListeners;
if (CollectionUtils.isNotEmpty(beanInitializedEventListeners)) {
for (Map.Entry<Class<?>, ListenersSupplier<BeanInitializedEventListener>> entry : beanInitializedEventListeners) {
if (entry.getKey().isAssignableFrom(getBeanType())) {
for (BeanInitializedEventListener listener : entry.getValue().get(resolutionContext)) {
bean = listener.onInitialized(new BeanInitializingEvent(context, this, bean));
if (bean == null) {
throw new BeanInstantiationException(resolutionContext, "Listener [" + listener + "] returned null from onInitialized event");
}
}
}
}
}
if (bean instanceof LifeCycle lifeCycle) {
bean = lifeCycle.start();
}
return bean;
}
/**
* Default preDestroy hook that only invokes methods that require reflection. Generated subclasses should override
* to call methods that don't require reflection.
*
* @param resolutionContext The resolution hook
* @param context The context
* @param bean The bean
* @return The bean
*/
@Internal
@UsedByGeneratedCode
protected Object preDestroy(BeanResolutionContext resolutionContext, BeanContext context, Object bean) {
if (bean instanceof LifeCycle lifeCycle) {
bean = lifeCycle.stop();
}
return bean;
}
/**
* Check if the class is an inner configuration.
*
* @param clazz The class to check
* @return true if the inner configuration
*/
@Internal
@UsedByGeneratedCode
protected boolean isInnerConfiguration(Class<?> clazz) {
return false;
}
/**
* Checks whether the bean should be loaded.
*
* @param resolutionContext - the resolution context
* @param context - the bean context
*/
@Internal
@UsedByGeneratedCode
protected void checkIfShouldLoad(BeanResolutionContext resolutionContext, BeanContext context) {
}
/**
* Check the value of the injected bean property to decide whether the
* bean should be loaded.
*
* @param injectedBeanPropertyName the name of the injected bean property
* @param beanPropertyValue the value of injected bean property
* @param requiredValue the value which is required for the bean to be loaded
* @param notEqualsValue the value which bean property should not be equal to for the bean to be loaded
*/
@Internal
@UsedByGeneratedCode
protected final void checkInjectedBeanPropertyValue(String injectedBeanPropertyName,
@Nullable Object beanPropertyValue,
@Nullable String requiredValue,
@Nullable String notEqualsValue) {
if (beanPropertyValue instanceof Optional optional) {
beanPropertyValue = optional.orElse(null);
}
String convertedValue = ConversionService.SHARED.convert(beanPropertyValue, String.class).orElse(null);
if (convertedValue == null && notEqualsValue == null) {
throw new DisabledBeanException("Bean [" + getBeanType() + "] is disabled since required bean property [" + injectedBeanPropertyName + "] id not set");
} else if (convertedValue != null) {
if (requiredValue != null && !convertedValue.equals(requiredValue)) {
throw new DisabledBeanException("Bean [" + getBeanType() + "] is disabled since bean property [" + injectedBeanPropertyName + "] " +
"value is not equal to [" + requiredValue + "]");
} else if (requiredValue == null && convertedValue.equals(notEqualsValue)) {
throw new DisabledBeanException("Bean [" + getBeanType() + "] is disabled since bean property [" + injectedBeanPropertyName + "] " +
"value is equal to [" + notEqualsValue + "]");
}
}
}
/**
* Invoke a bean method that requires reflection.
*
* @param resolutionContext The resolution context
* @param context The bean context
* @param methodIndex The method index
* @param bean The bean
* @param methodArgs The method args
*/
@Internal
@SuppressWarnings("WeakerAccess")
protected final void invokeMethodWithReflection(BeanResolutionContext resolutionContext, BeanContext context, int methodIndex, Object bean, Object[] methodArgs) {
MethodReference methodRef = methodInjection[methodIndex];
Argument<?>[] methodArgumentTypes = methodRef.arguments == null ? Argument.ZERO_ARGUMENTS : methodRef.arguments;
if (ClassUtils.REFLECTION_LOGGER.isDebugEnabled()) {
ClassUtils.REFLECTION_LOGGER.debug("Bean of type [{}] uses reflection to inject method: '{}'", getBeanType(), methodRef.methodName);
}
try {
Method method = ReflectionUtils.getMethod(
methodRef.declaringType,
methodRef.methodName,
Argument.toClassArray(methodArgumentTypes)
).orElseThrow(() -> ReflectionUtils.newNoSuchMethodError(methodRef.declaringType, methodRef.methodName, Argument.toClassArray(methodArgumentTypes)));
method.setAccessible(true);
ReflectionUtils.invokeMethod(bean, method, methodArgs);
} catch (Throwable e) {
if (e instanceof BeanContextException exception) {
throw exception;
} else {
throw new DependencyInjectionException(resolutionContext, "Error invoking method: " + methodRef.methodName, e);
}
}
}
/**
* Sets the value of a field of an object that requires reflection.
*
* @param resolutionContext The resolution context
* @param context The object context
* @param index The index of the field
* @param object The object whose field should be modifie
* @param value The instance being set
*/
@SuppressWarnings("unused")
@Internal
protected final void setFieldWithReflection(BeanResolutionContext resolutionContext, BeanContext context, int index, Object object, Object value) {
FieldReference fieldRef = fieldInjection[index];
try {
if (ClassUtils.REFLECTION_LOGGER.isDebugEnabled()) {
ClassUtils.REFLECTION_LOGGER.debug("Bean of type [{}] uses reflection to inject field: '{}'", getBeanType(), fieldRef.argument.getName());
}
Field field = ReflectionUtils.getRequiredField(fieldRef.declaringType, fieldRef.argument.getName());
field.setAccessible(true);
field.set(object, value);
} catch (Throwable e) {
if (e instanceof BeanContextException exception) {
throw exception;
} else {
throw new DependencyInjectionException(resolutionContext, "Error setting field value: " + e.getMessage(), e);
}
}
}
/**
* Obtains a value for the given method argument.
*
* @param resolutionContext The resolution context
* @param context The bean context
* @param methodIndex The method index
* @param argIndex The argument index
* @param qualifier The qualifier
* @return The value
*/
@SuppressWarnings({"unused"})
@Internal
@Deprecated
protected final Object getValueForMethodArgument(BeanResolutionContext resolutionContext, BeanContext context, int methodIndex, int argIndex, Qualifier qualifier) {
MethodReference methodRef = methodInjection[methodIndex];
Argument<?> argument = methodRef.arguments[argIndex];
try (BeanResolutionContext.Path ignored = resolutionContext.getPath()
.pushMethodArgumentResolve(this, methodRef.methodName, argument, methodRef.arguments)) {
return resolveValue(resolutionContext, context, methodRef.annotationMetadata, argument, qualifier);
}
}
/**
* Obtains a property value for the given method argument.
*
* @param resolutionContext The resolution context
* @param context The bean context
* @param methodIndex The method index
* @param argIndex The argument index
* @param propertyValue The property value
* @param cliProperty The cli property
* @return The value
*/
@SuppressWarnings({"unused"})
@Internal
protected final Object getPropertyValueForMethodArgument(BeanResolutionContext resolutionContext,
BeanContext context,
int methodIndex,
int argIndex,
String propertyValue,
String cliProperty) {
MethodReference methodRef = methodInjection[methodIndex];
Argument<?> argument = methodRef.arguments[argIndex];
try (BeanResolutionContext.Path path = resolutionContext.getPath()
.pushMethodArgumentResolve(this, methodRef.methodName, argument, methodRef.arguments)) {
Object val = resolutionContext.resolvePropertyValue( argument, propertyValue, cliProperty, false);
if (this instanceof ValidatedBeanDefinition validatedBeanDefinition) {
validatedBeanDefinition.validateBeanArgument(
resolutionContext,
Objects.requireNonNull(path.peek()).getInjectionPoint(),
argument,
argIndex,
val
);
}
return val;
}
}
/**
* Obtains a placeholder value for the given method argument.
*
* @param resolutionContext The resolution context
* @param context The bean context
* @param methodIndex The method index
* @param argIndex The argument index
* @param value The property value
* @return The value
*/
@SuppressWarnings({"unused"})
@Internal
protected final Object getPropertyPlaceholderValueForMethodArgument(BeanResolutionContext resolutionContext,
BeanContext context,
int methodIndex,
int argIndex,
String value) {
MethodReference methodRef = methodInjection[methodIndex];
Argument<?> argument = methodRef.arguments[argIndex];
try (BeanResolutionContext.Path ignored = resolutionContext.getPath()
.pushMethodArgumentResolve(this, methodRef.methodName, argument, methodRef.arguments)) {
return resolutionContext.resolvePropertyValue(argument, value, null, true);
}
}
@Internal
@UsedByGeneratedCode
protected final Object getEvaluatedExpressionValueForMethodArgument(int methodIndex,
int argIndex) {
MethodReference methodRef = methodInjection[methodIndex];
Argument<?> argument = methodRef.arguments[argIndex];
return getExpressionValueForArgument(argument);
}
/**
* Obtains a property value for the given method argument.
*
* @param resolutionContext The resolution context
* @param context The bean context
* @param setterName The setter name
* @param argument The argument
* @param propertyValue The property value
* @param cliProperty The cli property
* @return The value
*/
@SuppressWarnings({"unused"})
@Internal
protected final Object getPropertyValueForSetter(BeanResolutionContext resolutionContext,
BeanContext context,
String setterName,
Argument<?> argument,
String propertyValue,
String cliProperty) {
try (BeanResolutionContext.Path path = resolutionContext.getPath()
.pushMethodArgumentResolve(this, setterName, argument, new Argument[]{argument})) {
Object val = resolutionContext.resolvePropertyValue(argument, propertyValue, cliProperty, false);
if (this instanceof ValidatedBeanDefinition validatedBeanDefinition) {
validatedBeanDefinition.validateBeanArgument(
resolutionContext,
Objects.requireNonNull(path.peek()).getInjectionPoint(),
argument,
0,
val
);
}
return val;
}
}
/**
* Obtains a placeholder value for the given method argument.
*
* @param resolutionContext The resolution context
* @param context The bean context
* @param setterName The setter name
* @param argument The argument
* @param value The value
* @return The value
*/
@SuppressWarnings({"unused"})
@Internal
protected final Object getPropertyPlaceholderValueForSetter(BeanResolutionContext resolutionContext,
BeanContext context,
String setterName,
Argument<?> argument,
String value) {
try (BeanResolutionContext.Path ignored = resolutionContext.getPath()
.pushMethodArgumentResolve(this, setterName, argument, new Argument[]{argument})) {
return resolutionContext.resolvePropertyValue(argument, value, null, true);
}
}
/**
* Obtains a value for the given method argument.
*
* @param resolutionContext The resolution context
* @param context The bean context
* @param methodIndex The method index
* @param argIndex The argument index
* @param isValuePrefix Is value prefix in cases when beans are requested
* @return The value
*/
@Internal
@UsedByGeneratedCode
@Deprecated
protected final boolean containsValueForMethodArgument(BeanResolutionContext resolutionContext, BeanContext context, int methodIndex, int argIndex, boolean isValuePrefix) {
MethodReference methodRef = methodInjection[methodIndex];
AnnotationMetadata parentAnnotationMetadata = methodRef.annotationMetadata;
Argument<?> argument = methodRef.arguments[argIndex];
return resolveContainsValue(resolutionContext, context, parentAnnotationMetadata, argument, isValuePrefix);
}
/**
* Obtains a bean definition for the method at the given index and the argument at the given index
* <p>
* Warning: this method is used by internal generated code and should not be called by user code.
*
* @param resolutionContext The resolution context
* @param context The context
* @param methodIndex The method index
* @param argIndex The argument index
* @param qualifier The qualifier
* @param <K> The bean type
* @return The resolved bean
*/
@Internal
@SuppressWarnings("WeakerAccess")
@UsedByGeneratedCode
protected final <K> K getBeanForMethodArgument(BeanResolutionContext resolutionContext, BeanContext context, int methodIndex, int argIndex, Qualifier<K> qualifier) {
MethodReference methodRef = methodInjection[methodIndex];
Argument<K> argument = resolveArgument(context, argIndex, methodRef.arguments);
BeanResolutionContext.Path path = resolutionContext.getPath();
try (BeanResolutionContext.Path ignored = path
.pushMethodArgumentResolve(this, methodRef.methodName, argument, methodRef.arguments)) {
return resolveBean(
resolutionContext,
argument,
qualifier,
!InjectionPoint.isInjectionRequired(methodRef.annotationMetadata)
);
}
}
@Internal
@UsedByGeneratedCode
protected final boolean isMethodResolved(int methodIndex, Object[] parameters) {
MethodReference methodRef = methodInjection[methodIndex];
Argument<?>[] arguments = methodRef.arguments;
if (arguments.length != parameters.length) {
return false;
}
for (int i = 0; i < parameters.length; i++) {
Object value = parameters[i];
if (value == null) {
Argument<?> argument = arguments[i];
if (!argument.isDeclaredNullable()
&& !InjectionPoint.isInjectionRequired(methodRef.annotationMetadata)) {
return false;
}
}
}
return true;
}
/**
* Obtains all bean definitions for a method argument at the given index.
*
* @param resolutionContext The resolution context
* @param context The context
* @param methodIndex The method index
* @param argumentIndex The argument index
* @param genericType The generic type
* @param qualifier The qualifier
* @param <K> The bean type
* @param <R> The result collection type
* @return The resolved bean
*/
@Internal
@UsedByGeneratedCode
protected final <K, R extends Collection<K>> R getBeansOfTypeForMethodArgument(BeanResolutionContext resolutionContext, BeanContext context, int methodIndex, int argumentIndex, Argument<K> genericType, Qualifier<K> qualifier) {
MethodReference methodRef = methodInjection[methodIndex];
Argument<R> argument = resolveArgument(context, argumentIndex, methodRef.arguments);
try (BeanResolutionContext.Path ignored =
resolutionContext.getPath().pushMethodArgumentResolve(this, methodRef.methodName, argument, methodRef.arguments)) {
return resolveBeansOfType(resolutionContext, context, argument, resolveArgument(context, genericType), qualifier);
}
}
/**
* Obtains a bean definition for the method at the given index and the argument at the given index
* <p>
* Warning: this method is used by internal generated code and should not be called by user code.
*
* @param resolutionContext The resolution context
* @param context The context
* @param setterName The setter name
* @param argument The argument
* @param qualifier The qualifier
* @return The resolved bean
*/
@Internal
@SuppressWarnings("WeakerAccess")
@UsedByGeneratedCode
protected final Object getBeanForSetter(BeanResolutionContext resolutionContext, BeanContext context, String setterName, Argument argument, Qualifier qualifier) {
try (BeanResolutionContext.Path ignored = resolutionContext.getPath()
.pushMethodArgumentResolve(this, setterName, argument, new Argument[]{argument})) {
return resolveBean(resolutionContext, argument, qualifier, !InjectionPoint.isInjectionRequired(argument.getAnnotationMetadata()));
}
}
/**
* Obtains all bean definitions for a method argument at the given index.
*
* @param resolutionContext The resolution context
* @param context The context
* @param setterName The setter name
* @param argument The argument
* @param genericType The generic type
* @param qualifier The qualifier
* @return The resolved bean
*/
@Internal
@UsedByGeneratedCode
protected final Collection<Object> getBeansOfTypeForSetter(BeanResolutionContext resolutionContext, BeanContext context, String setterName, Argument argument, Argument genericType, Qualifier qualifier) {
try (BeanResolutionContext.Path ignored =
resolutionContext.getPath().pushMethodArgumentResolve(this, setterName, argument, new Argument[]{argument})) {
return resolveBeansOfType(resolutionContext, context, argument, resolveArgument(context, genericType), qualifier);
}
}
/**
* Obtains an optional bean for the method at the given index and the argument at the given index
* <p>
* Warning: this method is used by internal generated code and should not be called by user code.
*
* @param resolutionContext The resolution context
* @param context The context
* @param methodIndex The method index
* @param argIndex The argument index
* @param genericType The generic type
* @param qualifier The qualifier
* @param <K> The bean type
* @return The resolved bean
*/
@Internal
@UsedByGeneratedCode
protected final <K> Optional<K> findBeanForMethodArgument(BeanResolutionContext resolutionContext, BeanContext context, int methodIndex, int argIndex, Argument<K> genericType, Qualifier<K> qualifier) {
MethodReference methodRef = methodInjection[methodIndex];
Argument<K> argument = resolveArgument(context, argIndex, methodRef.arguments);
try (BeanResolutionContext.Path ignored =
resolutionContext.getPath().pushMethodArgumentResolve(this, methodRef.methodName, argument, methodRef.arguments)) {
return resolveOptionalBean(resolutionContext, argument, resolveArgument(context, genericType), qualifier);
}
}
/**
* Obtains all bean definitions for the method at the given index and the argument at the given index
* <p>
* Warning: this method is used by internal generated code and should not be called by user code.
*
* @param resolutionContext The resolution context
* @param context The context
* @param methodIndex The method index
* @param argIndex The argument index
* @param genericType The generic type
* @param qualifier The qualifier
* @return The resolved bean
*/
@Internal
@UsedByGeneratedCode
protected final Stream<?> getStreamOfTypeForMethodArgument(BeanResolutionContext resolutionContext, BeanContext context, int methodIndex, int argIndex, Argument genericType, Qualifier qualifier) {
MethodReference methodRef = methodInjection[methodIndex];
Argument<?> argument = resolveArgument(context, argIndex, methodRef.arguments);
try (BeanResolutionContext.Path ignored =
resolutionContext.getPath().pushMethodArgumentResolve(this, methodRef.methodName, argument, methodRef.arguments)) {
return resolveStreamOfType(resolutionContext, argument, resolveArgument(context, genericType), qualifier);
}
}
/**
* Obtains all bean definitions for the method at the given index and the argument at the given index
* <p>
* Warning: this method is used by internal generated code and should not be called by user code.
*
* @param resolutionContext The resolution context
* @param context The context
* @param methodIndex The method index
* @param argIndex The argument index
* @param genericType The generic type
* @param qualifier The qualifier
* @return The resolved bean
* @param <V> The bean type
*/
@Internal
@UsedByGeneratedCode
protected final <V> Map<String, V> getMapOfTypeForMethodArgument(
BeanResolutionContext resolutionContext,
BeanContext context,
int methodIndex,
int argIndex,
Argument<V> genericType,
Qualifier<V> qualifier) {
MethodReference methodRef = methodInjection[methodIndex];
Argument<Map<String, V>> argument = resolveArgument(context, argIndex, methodRef.arguments);
try (BeanResolutionContext.Path ignored =
resolutionContext.getPath().pushMethodArgumentResolve(this, methodRef.methodName, argument, methodRef.arguments)) {
return resolveMapOfType(resolutionContext, argument, resolveArgument(context, genericType), qualifier);
}
}
/**
* Obtains a bean definition for a constructor at the given index
* <p>
* Warning: this method is used by internal generated code and should not be called by user code.
*
* @param resolutionContext The resolution context
* @param context The context
* @param argIndex The argument index
* @param qualifier The qualifier
* @return The resolved bean
*/
@Internal
@UsedByGeneratedCode
protected final Object getBeanForConstructorArgument(BeanResolutionContext resolutionContext, BeanContext context, int argIndex, Qualifier qualifier) {
MethodReference constructorMethodRef = (MethodReference) constructor;
Argument<?> argument = resolveArgument(context, argIndex, constructorMethodRef.arguments);
BeanResolutionContext.Path path = resolutionContext.getPath();
if (argument != null && argument.isDeclaredNullable()) {
BeanResolutionContext.Segment<?, ?> current = path.peek();
if (current != null && current.getArgument().equals(argument)) {
return null;
}
}
try (BeanResolutionContext.Path ignored = path
.pushConstructorResolve(this, argument)) {
return resolveBean(resolutionContext, argument, qualifier, false);
}
}
/**
* Obtains a value for a bean definition for a constructor at the given index
* <p>
* Warning: this method is used by internal generated code and should not be called by user code.
*
* @param resolutionContext The resolution context
* @param context The context
* @param argIndex The argument index
* @param qualifier The qualifier
* @return The resolved bean
*/
@Internal
@UsedByGeneratedCode
@Deprecated
protected final Object getValueForConstructorArgument(BeanResolutionContext resolutionContext, BeanContext context, int argIndex, Qualifier qualifier) {
MethodReference constructorRef = (MethodReference) constructor;
Argument<?> argument = constructorRef.arguments[argIndex];
try (BeanResolutionContext.Path ignored = resolutionContext.getPath().pushConstructorResolve(this, argument)) {
try {
Object result = resolveValue(resolutionContext, context, constructorRef.annotationMetadata, argument, qualifier);
if (this instanceof ValidatedBeanDefinition validatedBeanDefinition) {
validatedBeanDefinition.validateBeanArgument(
resolutionContext,
getConstructor(),
argument,
argIndex,
result
);
}
return result;
} catch (NoSuchBeanException | BeanInstantiationException e) {
throw new DependencyInjectionException(resolutionContext, argument, e);
}
}
}
/**
* Obtains a property value for a bean definition for a constructor at the given index
* <p>
* Warning: this method is used by internal generated code and should not be called by user code.
*
* @param resolutionContext The resolution context
* @param context The context
* @param argIndex The argument index
* @param propertyValue The property value
* @param cliProperty The cli property
* @return The resolved bean
*/
@Internal
@UsedByGeneratedCode
protected final Object getPropertyValueForConstructorArgument(BeanResolutionContext resolutionContext,
BeanContext context,
int argIndex,
String propertyValue,
String cliProperty) {
MethodReference constructorRef = (MethodReference) constructor;
Argument<?> argument = constructorRef.arguments[argIndex];
try (BeanResolutionContext.Path ignored = resolutionContext.getPath().pushConstructorResolve(this, argument)) {
try {
Object result = resolutionContext.resolvePropertyValue(argument, propertyValue, cliProperty, false);
if (this instanceof ValidatedBeanDefinition validatedBeanDefinition) {
validatedBeanDefinition.validateBeanArgument(
resolutionContext,
getConstructor(),
argument,
argIndex,
result
);
}
return result;
} catch (NoSuchBeanException | BeanInstantiationException e) {
throw new DependencyInjectionException(resolutionContext, argument, e);
}
}
}
@Internal
@UsedByGeneratedCode
protected final Object getEvaluatedExpressionValueForConstructorArgument(int argIndex) {
MethodReference constructorRef = (MethodReference) constructor;
Argument<?> argument = constructorRef.arguments[argIndex];
return getExpressionValueForArgument(argument);
}
/**
* Obtains a property value for a bean definition for a constructor at the given index
* <p>
* Warning: this method is used by internal generated code and should not be called by user code.
*
* @param resolutionContext The resolution context
* @param context The context
* @param argIndex The argument index
* @param propertyValue The property value
* @return The resolved bean
*/
@Internal
@UsedByGeneratedCode
protected final Object getPropertyPlaceholderValueForConstructorArgument(BeanResolutionContext resolutionContext,
BeanContext context,
int argIndex,
String propertyValue) {
MethodReference constructorRef = (MethodReference) constructor;
Argument<?> argument = constructorRef.arguments[argIndex];
try (BeanResolutionContext.Path ignored = resolutionContext.getPath().pushConstructorResolve(this, argument)) {
try {
Object result = resolutionContext.resolvePropertyValue(argument, propertyValue, null, true);
if (this instanceof ValidatedBeanDefinition validatedBeanDefinition) {
validatedBeanDefinition.validateBeanArgument(
resolutionContext,
getConstructor(),
argument,
argIndex,
result
);
}
return result;
} catch (NoSuchBeanException | BeanInstantiationException e) {
throw new DependencyInjectionException(resolutionContext, argument, e);
}
}
}
/**
* Obtains all bean definitions for a constructor argument at the given index.
*
* @param resolutionContext The resolution context
* @param context The context
* @param argumentIndex The argument index
* @param genericType The generic type
* @param qualifier The qualifier
* @return The resolved bean
*/
@Internal
@UsedByGeneratedCode
protected final Collection<Object> getBeansOfTypeForConstructorArgument(BeanResolutionContext resolutionContext, BeanContext context, int argumentIndex, Argument genericType, Qualifier qualifier) {
MethodReference constructorMethodRef = (MethodReference) constructor;
Argument argument = resolveArgument(context, argumentIndex, constructorMethodRef.arguments);
try (BeanResolutionContext.Path ignored = resolutionContext.getPath().pushConstructorResolve(this, argument)) {
return resolveBeansOfType(resolutionContext, context, argument, resolveArgument(context, genericType), qualifier);
}
}
/**
* Obtains all bean definitions for a constructor argument at the given index
* <p>
* Warning: this method is used by internal generated code and should not be called by user code.
*
* @param resolutionContext The resolution context
* @param context The context
* @param argumentIndex The argument index
* @param genericType The generic type
* @param qualifier The qualifier
* @param <K> The bean type
* @param <R> The result collection type
* @return The resolved bean
*/
@Internal
@UsedByGeneratedCode
protected final <K, R extends Collection<BeanRegistration<K>>> R getBeanRegistrationsForConstructorArgument(BeanResolutionContext resolutionContext, BeanContext context, int argumentIndex, Argument<K> genericType, Qualifier<K> qualifier) {
MethodReference constructorMethodRef = (MethodReference) constructor;
Argument<R> argument = resolveArgument(context, argumentIndex, constructorMethodRef.arguments);
try (BeanResolutionContext.Path ignored = resolutionContext.getPath().pushConstructorResolve(this, argument)) {
return resolveBeanRegistrations(resolutionContext, argument, resolveArgument(context, genericType), qualifier);
}
}
/**
* Obtains a bean registration for a method injection point.
* <p>
* Warning: this method is used by internal generated code and should not be called by user code.
*
* @param resolutionContext The resolution context
* @param context The context
* @param argumentIndex The arg index
* @param genericType The generic type
* @param qualifier The qualifier
* @param <K> The bean type
* @return The resolved bean registration
*/
@Internal
@UsedByGeneratedCode
protected final <K> BeanRegistration<K> getBeanRegistrationForConstructorArgument(BeanResolutionContext resolutionContext, BeanContext context, int argumentIndex, Argument<K> genericType, Qualifier<K> qualifier) {
MethodReference constructorMethodRef = (MethodReference) constructor;
Argument<K> argument = resolveArgument(context, argumentIndex, constructorMethodRef.arguments);
try (BeanResolutionContext.Path ignored = resolutionContext.getPath().pushConstructorResolve(this, argument)) {
return resolveBeanRegistration(resolutionContext, context, argument, resolveArgument(context, genericType), qualifier);
}
}
/**
* Obtains all bean definitions for a method injection point.
* <p>
* Warning: this method is used by internal generated code and should not be called by user code.
*
* @param resolutionContext The resolution context
* @param context The context
* @param methodIndex The method index
* @param argIndex The arg index
* @param genericType The generic type
* @param qualifier The qualifier
* @param <K> The bean type
* @param <R> The result collection type
* @return The resolved bean
*/
@Internal
@UsedByGeneratedCode
protected final <K, R extends Collection<BeanRegistration<K>>> R getBeanRegistrationsForMethodArgument(BeanResolutionContext resolutionContext, BeanContext context, int methodIndex, int argIndex, Argument<K> genericType, Qualifier<K> qualifier) {
MethodReference methodReference = methodInjection[methodIndex];
Argument<R> argument = resolveArgument(context, argIndex, methodReference.arguments);
try (BeanResolutionContext.Path ignored = resolutionContext.getPath()
.pushMethodArgumentResolve(this, methodReference.methodName, argument, methodReference.arguments)) {
return resolveBeanRegistrations(resolutionContext, argument, resolveArgument(context, genericType), qualifier);
}
}
/**
* Obtains a bean registration for a method injection point.
* <p>
* Warning: this method is used by internal generated code and should not be called by user code.
*
* @param resolutionContext The resolution context
* @param context The context
* @param methodIndex The method index
* @param argIndex The arg index
* @param genericType The generic type
* @param qualifier The qualifier
* @param <K> The bean type
* @return The resolved bean registration
*/
@Internal
@UsedByGeneratedCode
protected final <K> BeanRegistration<K> getBeanRegistrationForMethodArgument(BeanResolutionContext resolutionContext, BeanContext context, int methodIndex, int argIndex, Argument<K> genericType, Qualifier<K> qualifier) {
MethodReference methodRef = methodInjection[methodIndex];
Argument<K> argument = resolveArgument(context, argIndex, methodRef.arguments);
try (BeanResolutionContext.Path ignored = resolutionContext.getPath()
.pushMethodArgumentResolve(this, methodRef.methodName, argument, methodRef.arguments)) {
return resolveBeanRegistration(resolutionContext, context, argument, resolveArgument(context, genericType), qualifier);
}
}
/**
* Obtains all bean definitions for a constructor argument at the given index
* <p>
* Warning: this method is used by internal generated code and should not be called by user code.
*
* @param resolutionContext The resolution context
* @param context The context
* @param argIndex The argument index
* @param genericType The generic type
* @param qualifier The qualifier
* @param <K> The bean type
* @return The resolved bean
*/
@Internal
@UsedByGeneratedCode
protected final <K> Stream<K> getStreamOfTypeForConstructorArgument(BeanResolutionContext resolutionContext, BeanContext context, int argIndex, Argument<K> genericType, Qualifier<K> qualifier) {
MethodReference constructorMethodRef = (MethodReference) constructor;
Argument<K> argument = resolveArgument(context, argIndex, constructorMethodRef.arguments);
try (BeanResolutionContext.Path ignored = resolutionContext.getPath().pushConstructorResolve(this, argument)) {
return resolveStreamOfType(resolutionContext, argument, resolveArgument(context, genericType), qualifier);
}
}
/**
* Obtains all bean definitions for a constructor argument at the given index
* <p>
* Warning: this method is used by internal generated code and should not be called by user code.
*
* @param resolutionContext The resolution context
* @param context The context
* @param argIndex The argument index
* @param genericType The generic type
* @param qualifier The qualifier
* @return The resolved bean
* @param <V> The bean type
*/
@Internal
@UsedByGeneratedCode
protected final <V> Map<String, V> getMapOfTypeForConstructorArgument(
BeanResolutionContext resolutionContext,
BeanContext context,
int argIndex,
Argument<V> genericType,
Qualifier<V> qualifier) {
MethodReference constructorMethodRef = (MethodReference) constructor;
if (constructorMethodRef == null) {
throw new IllegalStateException("No constructor found for bean: " + getBeanType());
}
Argument<Map<String, V>> argument = resolveArgument(context, argIndex, constructorMethodRef.arguments);
try (BeanResolutionContext.Path ignored = resolutionContext.getPath().pushConstructorResolve(this, argument)) {
return resolveMapOfType(resolutionContext, argument, resolveArgument(context, genericType), qualifier);
}
}
/**
* Obtains all bean definitions for a constructor argument at the given index
* <p>
* Warning: this method is used by internal generated code and should not be called by user code.
*
* @param resolutionContext The resolution context
* @param context The context
* @param argIndex The argument index
* @param genericType The generic type
* @param qualifier The qualifier
* @param <K> The bean type
* @return The resolved bean
*/
@Internal
@UsedByGeneratedCode
protected final <K> Optional<K> findBeanForConstructorArgument(BeanResolutionContext resolutionContext, BeanContext context, int argIndex, Argument<K> genericType, Qualifier<K> qualifier) {
MethodReference constructorMethodRef = (MethodReference) constructor;
Argument<K> argument = resolveArgument(context, argIndex, constructorMethodRef.arguments);
try (BeanResolutionContext.Path ignored = resolutionContext.getPath().pushConstructorResolve(this, argument)) {
return resolveOptionalBean(resolutionContext, argument, resolveArgument(context, genericType), qualifier);
}
}
/**
* Obtains a bean definition for the field at the given index and the argument at the given index
* <p>
* Warning: this method is used by internal generated code and should not be called by user code.
*
* @param resolutionContext The resolution context
* @param context The context
* @param fieldIndex The field index
* @param qualifier The qualifier
* @param <K> The bean type
* @return The resolved bean
*/
@Internal
@UsedByGeneratedCode
protected final <K> K getBeanForField(BeanResolutionContext resolutionContext, BeanContext context, int fieldIndex, Qualifier<K> qualifier) {
final Argument<K> argument = resolveArgument(context, fieldInjection[fieldIndex].argument);
try (BeanResolutionContext.Path ignored = resolutionContext.getPath().pushFieldResolve(this, argument)) {
return resolveBean(resolutionContext, argument, qualifier, !InjectionPoint.isInjectionRequired(argument.getAnnotationMetadata()));
}
}
@Internal
@UsedByGeneratedCode
protected final <K> K getBeanForAnnotation(BeanResolutionContext resolutionContext, BeanContext context, int annotationBeanIndex, Qualifier<K> qualifier) {
final Argument<K> argument = resolveArgument(context, annotationInjection[annotationBeanIndex].argument);
try (BeanResolutionContext.Path ignored = resolutionContext.getPath()
.pushAnnotationResolve(this, argument)) {
return resolveBean(resolutionContext, argument, qualifier, false);
}
}
/**
* Obtains a value for the given field from the bean context
* <p>
* Warning: this method is used by internal generated code and should not be called by user code.
*
* @param resolutionContext The resolution context
* @param context The context
* @param fieldIndex The index of the field
* @param qualifier The qualifier
* @return The resolved bean
*/
@Internal
@UsedByGeneratedCode
@Deprecated
protected final Object getValueForField(BeanResolutionContext resolutionContext, BeanContext context, int fieldIndex, Qualifier qualifier) {
FieldReference fieldRef = fieldInjection[fieldIndex];
try (BeanResolutionContext.Path ignored = resolutionContext.getPath().pushFieldResolve(this, fieldRef.argument)) {
return resolveValue(resolutionContext, context, fieldRef.argument.getAnnotationMetadata(), fieldRef.argument, qualifier);
}
}
/**
* Obtains a property value for the given field from the bean context
* <p>
* Warning: this method is used by internal generated code and should not be called by user code.
*
* @param resolutionContext The resolution context
* @param context The context
* @param argument The argument
* @param propertyValue The property value
* @param cliProperty The clie property name
* @return The resolved bean
*/
@Internal
@UsedByGeneratedCode
@Deprecated
protected final Object getPropertyValueForField(BeanResolutionContext resolutionContext, BeanContext context, Argument argument, String propertyValue, String cliProperty) {
try (BeanResolutionContext.Path ignored = resolutionContext.getPath().pushFieldResolve(this, argument)) {
return resolutionContext.resolvePropertyValue(argument, propertyValue, cliProperty, false);
}
}
/**
* Obtains a property placeholder value for the given field from the bean context
* <p>
* Warning: this method is used by internal generated code and should not be called by user code.
*
* @param resolutionContext The resolution context
* @param context The context
* @param argument The argument
* @param placeholder The placeholder
* @return The resolved bean
*/
@Internal
@UsedByGeneratedCode
@Deprecated
protected final Object getPropertyPlaceholderValueForField(BeanResolutionContext resolutionContext, BeanContext context, Argument argument, String placeholder) {
try (BeanResolutionContext.Path ignored = resolutionContext.getPath().pushFieldResolve(this, argument)) {
return resolutionContext.resolvePropertyValue(argument, placeholder, null, true);
}
}
/**
* Resolve a value for the given field of the given type and path.
*
* @param resolutionContext The resolution context
* @param context The bean context
* @param propertyType The required property type
* @param propertyPath The property path
* @param <T1> The generic type
* @return An optional value
*/
@Internal
@UsedByGeneratedCode
protected final <T1> Optional<T1> getValueForPath(
BeanResolutionContext resolutionContext,
BeanContext context,
Argument<T1> propertyType,
String propertyPath) {
if (context instanceof PropertyResolver propertyResolver) {
String valString = substituteWildCards(resolutionContext, propertyPath);
return propertyResolver.getProperty(valString, ConversionContext.of(propertyType));
}
return Optional.empty();
}
/**
* Obtains a value for the given field argument.
*
* @param resolutionContext The resolution context
* @param context The bean context
* @param fieldIndex The field index
* @param isValuePrefix Is value prefix in cases when beans are requested
* @return True if it does
*/
@Internal
@UsedByGeneratedCode
@Deprecated
protected final boolean containsValueForField(BeanResolutionContext resolutionContext, BeanContext context, int fieldIndex, boolean isValuePrefix) {
FieldReference fieldRef = fieldInjection[fieldIndex];
return resolveContainsValue(resolutionContext, context, fieldRef.argument.getAnnotationMetadata(), fieldRef.argument, isValuePrefix);
}
/**
* If this bean is a {@link ConfigurationProperties} bean return whether any properties for it are configured
* within the context.
*
* @param resolutionContext the resolution context
* @param context The context
* @return True if it does
*/
@Internal
@UsedByGeneratedCode
protected final boolean containsProperties(BeanResolutionContext resolutionContext, BeanContext context) {
return containsProperties(resolutionContext, context, null);
}
/**
* If this bean is a {@link ConfigurationProperties} bean return whether any properties for it are configured
* within the context.
*
* @param resolutionContext the resolution context
* @param context The context
* @param subProperty The subproperty to check
* @return True if it does
*/
@Internal
@UsedByGeneratedCode
protected final boolean containsProperties(@SuppressWarnings("unused") BeanResolutionContext resolutionContext, BeanContext context, String subProperty) {
return precalculatedInfo.isConfigurationProperties;
}
/**
* Obtains all bean definitions for the field at the given index.
*
* @param resolutionContext The resolution context
* @param context The context
* @param fieldIndex The field index
* @param genericType The generic type
* @param qualifier The qualifier
* @param <K> The bean type
* @param <R> The result collection type
* @return The resolved bean
*/
@Internal
@UsedByGeneratedCode
protected final <K, R extends Collection<K>> Object getBeansOfTypeForField(BeanResolutionContext resolutionContext, BeanContext context, int fieldIndex, Argument<K> genericType, Qualifier<K> qualifier) {
// Keep Object type for backwards compatibility
final FieldReference fieldRef = fieldInjection[fieldIndex];
final Argument<R> argument = resolveArgument(context, fieldRef.argument);
try (BeanResolutionContext.Path ignored = resolutionContext.getPath().pushFieldResolve(this, argument)) {
return resolveBeansOfType(resolutionContext, context, argument, resolveArgument(context, genericType), qualifier);
}
}
/**
* Obtains all bean definitions for a field injection point.
* <p>
* Warning: this method is used by internal generated code and should not be called by user code.
*
* @param resolutionContext The resolution context
* @param context The context
* @param fieldIndex The field index
* @param genericType The generic type
* @param qualifier The qualifier
* @param <K> The bean type
* @param <R> The result collection type
* @return The resolved bean
*/
@Internal
@UsedByGeneratedCode
protected final <K, R extends Collection<BeanRegistration<K>>> R getBeanRegistrationsForField(BeanResolutionContext resolutionContext, BeanContext context, int fieldIndex, Argument<K> genericType, Qualifier<K> qualifier) {
FieldReference fieldRef = fieldInjection[fieldIndex];
Argument<R> argument = resolveArgument(context, fieldRef.argument);
try (BeanResolutionContext.Path ignored = resolutionContext.getPath().pushFieldResolve(this, argument)) {
return resolveBeanRegistrations(resolutionContext, argument, resolveArgument(context, genericType), qualifier);
}
}
/**
* Obtains a bean registration for a field injection point.
* <p>
* Warning: this method is used by internal generated code and should not be called by user code.
*
* @param resolutionContext The resolution context
* @param context The context
* @param fieldIndex The field index
* @param genericType The generic type
* @param qualifier The qualifier
* @param <K> The bean type
* @return The resolved bean registration
*/
@Internal
@UsedByGeneratedCode
protected final <K> BeanRegistration<K> getBeanRegistrationForField(BeanResolutionContext resolutionContext, BeanContext context, int fieldIndex, Argument<K> genericType, Qualifier<K> qualifier) {
FieldReference fieldRef = fieldInjection[fieldIndex];
Argument<K> argument = resolveArgument(context, fieldRef.argument);
try (BeanResolutionContext.Path ignored = resolutionContext.getPath().pushFieldResolve(this, argument)) {
return resolveBeanRegistration(resolutionContext, context, argument, resolveArgument(context, genericType), qualifier);
}
}
/**
* Obtains an optional for the field at the given index and the argument at the given index
* <p>
* Warning: this method is used by internal generated code and should not be called by user code.
*
* @param resolutionContext The resolution context
* @param context The context
* @param fieldIndex The field index
* @param genericType The generic type
* @param qualifier The qualifier
* @param <K> The bean type
* @return The resolved bean
*/
@Internal
@UsedByGeneratedCode
protected final <K> Optional<K> findBeanForField(BeanResolutionContext resolutionContext, BeanContext context, int fieldIndex, Argument<K> genericType, Qualifier<K> qualifier) {
FieldReference fieldRef = fieldInjection[fieldIndex];
Argument<K> argument = resolveArgument(context, fieldRef.argument);
try (BeanResolutionContext.Path ignored = resolutionContext.getPath().pushFieldResolve(this, argument)) {
return resolveOptionalBean(resolutionContext, argument, resolveArgument(context, genericType), qualifier);
}
}
/**
* Obtains a bean definition for the field at the given index and the argument at the given index
* <p>
* Warning: this method is used by internal generated code and should not be called by user code.
*
* @param resolutionContext The resolution context
* @param context The context
* @param fieldIndex The field index
* @param genericType The generic type
* @param qualifier The qualifier
* @param <K> The bean type
* @return The resolved bean
*/
@Internal
@UsedByGeneratedCode
protected final <K> Stream<K> getStreamOfTypeForField(BeanResolutionContext resolutionContext, BeanContext context, int fieldIndex, Argument<K> genericType, Qualifier<K> qualifier) {
FieldReference fieldRef = fieldInjection[fieldIndex];
Argument<K> argument = resolveArgument(context, fieldRef.argument);
try (BeanResolutionContext.Path ignored = resolutionContext.getPath().pushFieldResolve(this, argument)) {
return resolveStreamOfType(resolutionContext, argument, resolveArgument(context, genericType), qualifier);
}
}
/**
* Obtains a bean definition for the field at the given index and the argument at the given index
* <p>
* Warning: this method is used by internal generated code and should not be called by user code.
*
* @param resolutionContext The resolution context
* @param context The context
* @param fieldIndex The field index
* @param genericType The generic type
* @param qualifier The qualifier
* @param <V> The bean type
* @return The resolved bean
*/
@Internal
@UsedByGeneratedCode
protected final <V> Map<String, V> getMapOfTypeForField(
BeanResolutionContext resolutionContext,
BeanContext context, int fieldIndex,
Argument<V> genericType,
Qualifier<V> qualifier) {
FieldReference fieldRef = fieldInjection[fieldIndex];
@SuppressWarnings("unchecked")
Argument<Map<String, V>> argument = resolveArgument(context, fieldRef.argument);
try (BeanResolutionContext.Path ignored = resolutionContext.getPath().pushFieldResolve(this, argument)) {
return resolveMapOfType(resolutionContext, argument, resolveArgument(context, genericType), qualifier);
}
}
@Internal
@UsedByGeneratedCode
protected final boolean containsPropertiesValue(BeanResolutionContext resolutionContext, BeanContext context, String value) {
if (!(context instanceof ApplicationContext applicationContext)) {
return false;
}
value = substituteWildCards(resolutionContext, value);
return applicationContext.containsProperties(value);
}
@Internal
@UsedByGeneratedCode
protected final boolean containsPropertyValue(BeanResolutionContext resolutionContext, BeanContext context, String value) {
if (!(context instanceof ApplicationContext applicationContext)) {
return false;
}
value = substituteWildCards(resolutionContext, value);
return applicationContext.containsProperty(value);
}
private boolean resolveContainsValue(BeanResolutionContext resolutionContext, BeanContext context, AnnotationMetadata parentAnnotationMetadata, Argument argument, boolean isValuePrefix) {
if (!(context instanceof ApplicationContext applicationContext)) {
return false;
}
String valueAnnStr = argument.getAnnotationMetadata().stringValue(Value.class).orElse(null);
String valString = resolvePropertyValueName(resolutionContext, parentAnnotationMetadata, argument, valueAnnStr);
boolean result = isValuePrefix ? applicationContext.containsProperties(valString) : applicationContext.containsProperty(valString);
if (!result && precalculatedInfo.isConfigurationProperties) {
String cliOption = resolveCliOption(argument.getName());
if (cliOption != null) {
result = applicationContext.containsProperty(cliOption);
}
}
return result;
}
private Object resolveValue(BeanResolutionContext resolutionContext, BeanContext context, AnnotationMetadata parentAnnotationMetadata, Argument<?> argument, Qualifier qualifier) {
AnnotationMetadata argumentAnnotationMetadata = argument.getAnnotationMetadata();
if (argumentAnnotationMetadata.hasEvaluatedExpressions()) {
boolean isOptional = argument.isOptional();
if (isOptional) {
Argument<?> t = argument.getFirstTypeVariable().orElse(Argument.OBJECT_ARGUMENT);
Object v = argumentAnnotationMetadata.getValue(Value.class, t).orElse(null);
return Optional.ofNullable(v);
} else {
return argumentAnnotationMetadata.getValue(Value.class, argument).orElse(null);
}
}
String valueAnnVal = argumentAnnotationMetadata.stringValue(Value.class).orElse(null);
Argument<?> argumentType;
boolean isCollection = false;
final boolean wrapperType = argument.isWrapperType();
final Class<?> argumentJavaType = argument.getType();
if (Collection.class.isAssignableFrom(argumentJavaType)) {
argumentType = argument.getFirstTypeVariable().orElse(Argument.OBJECT_ARGUMENT);
isCollection = true;
} else if (wrapperType) {
argumentType = argument.getWrappedType();
} else {
argumentType = argument;
}
if (isInnerConfiguration(argumentType)) {
qualifier = qualifier == null ? resolveQualifier(resolutionContext, argumentType, argument) : qualifier;
if (isCollection) {
Collection<?> beans = resolutionContext.getBeansOfType(argumentType, qualifier);
return coerceCollectionToCorrectType((Class) argumentJavaType, beans, resolutionContext, argument);
} else {
return resolutionContext.getBean(argumentType, qualifier);
}
} else {
String valString = resolvePropertyValueName(resolutionContext, parentAnnotationMetadata, argumentAnnotationMetadata, valueAnnVal);
ArgumentConversionContext conversionContext = wrapperType ? ConversionContext.of(argumentType) : ConversionContext.of(argument);
Optional value = resolveValue((ApplicationContext) context, conversionContext, valueAnnVal != null, valString);
resolutionContext.valueResolved(
argument,
qualifier,
valString,
value.orElse(null)
);
if (argument.isOptional()) {
if (value.isEmpty()) {
return value;
} else {
Object convertedOptional = value.get();
if (convertedOptional instanceof Optional) {
return convertedOptional;
} else {
return value;
}
}
} else {
if (wrapperType) {
final Object v = value.orElse(null);
if (OptionalInt.class == argumentJavaType) {
return v instanceof Integer i ? OptionalInt.of(i) : OptionalInt.empty();
} else if (OptionalLong.class == argumentJavaType) {
return v instanceof Long l ? OptionalLong.of(l) : OptionalLong.empty();
} else if (OptionalDouble.class == argumentJavaType) {
return v instanceof Double d ? OptionalDouble.of(d) : OptionalDouble.empty();
}
}
if (value.isPresent()) {
return value.get();
} else {
if (argument.isDeclaredNullable()) {
return null;
}
return argumentAnnotationMetadata.getValue(Bindable.class, "defaultValue", argument)
.orElseThrow(() -> DependencyInjectionException.missingProperty(resolutionContext, conversionContext, valString));
}
}
}
}
private <K> @Nullable K resolveBean(
BeanResolutionContext resolutionContext,
Argument<K> argument,
@Nullable Qualifier<K> qualifier,
boolean isOptional) {
qualifier = qualifier == null ? resolveQualifier(resolutionContext, argument, argument) : qualifier;
Class<K> t = argument.getType();
if (Qualifier.class.isAssignableFrom(t)) {
return (K) qualifier;
}
try {
boolean isNotInnerConfiguration = !precalculatedInfo.isConfigurationProperties || !isInnerConfiguration(argument);
ConfigurationPath previousPath = isNotInnerConfiguration ? resolutionContext.setConfigurationPath(null) : null;
try {
if (argument.isDeclaredNullable() || isOptional) {
K k = resolutionContext.findBean(argument, qualifier).orElse(null);
return k;
}
K bean = resolutionContext.getBean(argument, qualifier);
return bean;
} finally {
if (previousPath != null) {
resolutionContext.setConfigurationPath(previousPath);
}
}
} catch (DisabledBeanException e) {
if (ConditionLog.LOG.isDebugEnabled()) {
ConditionLog.LOG.debug("Bean of type [{}] disabled for reason: {}", argument.getTypeName(), e.getMessage());
}
if (isIterable() && getAnnotationMetadata().hasDeclaredAnnotation(EachBean.class)) {
throw new DisabledBeanException("Bean [" + getBeanType().getSimpleName() + "] disabled by parent: " + e.getMessage());
} else {
throw new DependencyInjectionException(resolutionContext, e);
}
} catch (NoSuchBeanException e) {
throw new DependencyInjectionException(resolutionContext, e);
}
}
private <K> Optional<K> resolveValue(
ApplicationContext context,
ArgumentConversionContext<K> argument,
boolean hasValueAnnotation,
String valString) {
if (hasValueAnnotation) {
return context.resolvePlaceholders(valString).flatMap(v -> context.getConversionService().convert(v, argument));
} else {
Optional<K> value = context.getProperty(valString, argument);
if (value.isEmpty() && precalculatedInfo.isConfigurationProperties) {
String cliOption = resolveCliOption(argument.getArgument().getName());
if (cliOption != null) {
return context.getProperty(cliOption, argument);
}
}
return value;
}
}
private String resolvePropertyValueName(BeanResolutionContext resolutionContext, AnnotationMetadata parentAnnotationMetadata, Argument argument, String valueAnnStr) {
return resolvePropertyValueName(resolutionContext, parentAnnotationMetadata, argument.getAnnotationMetadata(), valueAnnStr);
}
private String resolvePropertyValueName(BeanResolutionContext resolutionContext, AnnotationMetadata parentAnnotationMetadata, AnnotationMetadata annotationMetadata, String valueAnnStr) {
if (valueAnnStr != null) {
return valueAnnStr;
}
String valString = getProperty(resolutionContext, parentAnnotationMetadata, annotationMetadata);
return substituteWildCards(resolutionContext, valString);
}
private String getProperty(BeanResolutionContext resolutionContext, AnnotationMetadata parentAnnotationMetadata, AnnotationMetadata annotationMetadata) {
Optional<String> property = parentAnnotationMetadata.stringValue(Property.class, "name");
if (property.isPresent()) {
return property.get();
}
if (parentAnnotationMetadata != annotationMetadata) {
property = annotationMetadata.stringValue(Property.class, "name");
if (property.isPresent()) {
return property.get();
}
}
throw new DependencyInjectionException(resolutionContext, "Value resolution attempted but @Value annotation is missing");
}
private String substituteWildCards(BeanResolutionContext resolutionContext, String valString) {
ConfigurationPath configurationPath = resolutionContext.getConfigurationPath();
if (configurationPath.isNotEmpty()) {
return configurationPath.resolveValue(valString);
}
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 <K, R extends Collection<K>> R resolveBeansOfType(BeanResolutionContext resolutionContext, BeanContext context, Argument<R> returnType, Argument<K> beanType, Qualifier<K> qualifier) {
if (beanType == null) {
throw noGenericsError(resolutionContext, returnType);
}
qualifier = qualifier == null ? resolveQualifier(resolutionContext, beanType, returnType) : qualifier;
Collection<K> beansOfType = resolutionContext.getBeansOfType(resolveArgument(context, beanType), qualifier);
return coerceCollectionToCorrectType(returnType.getType(), beansOfType, resolutionContext, returnType);
}
@NonNull
private static <K, R> DependencyInjectionException noGenericsError(BeanResolutionContext resolutionContext, Argument<R> returnType) {
return new DependencyInjectionException(resolutionContext, "Type " + returnType.getType() + " has no generic argument");
}
private <R> boolean isInnerConfiguration(@Nullable Argument<R> argument) {
if (argument == null || !precalculatedInfo.isConfigurationProperties) {
return false;
}
if (argument.isContainerType() || argument.isOptional() || argument.isProvider()) {
return isInnerConfiguration(argument.getFirstTypeVariable().orElse(null));
} else if (precalculatedInfo.isIterable && isEachBeanParent(argument)) {
return true;
}
return isInnerConfiguration(argument.getType());
}
private <R> boolean isEachBeanParent(Argument<R> argument) {
// treat each bean declaration like an inner configuration
Class<?> t = getAnnotationMetadata().classValue(EachBean.class).orElse(null);
return t != null && t.equals(argument.getType());
}
private <K> Stream<K> resolveStreamOfType(BeanResolutionContext resolutionContext, Argument<K> returnType, Argument<K> beanType, Qualifier<K> qualifier) {
if (beanType == null) {
throw noGenericsError(resolutionContext, returnType);
}
qualifier = qualifier == null ? resolveQualifier(resolutionContext, beanType, returnType) : qualifier;
return resolutionContext.streamOfType(beanType, qualifier);
}
private <V> Map<String, V> resolveMapOfType(
BeanResolutionContext resolutionContext,
Argument<Map<String, V>> returnType,
Argument<V> beanType,
Qualifier<V> qualifier) {
if (beanType == null) {
throw noGenericsError(resolutionContext, returnType);
}
qualifier = qualifier == null ? resolveQualifier(resolutionContext, beanType, returnType) : qualifier;
Map<String, V> map = resolutionContext.mapOfType(beanType, qualifier);
if (returnType.isInstance(map)) {
return map;
}
return resolutionContext.getContext().getConversionService().convertRequired(map, returnType);
}
private <K> Optional<K> resolveOptionalBean(BeanResolutionContext resolutionContext, Argument<K> returnType, Argument<K> beanType, Qualifier<K> qualifier) {
if (beanType == null) {
throw noGenericsError(resolutionContext, returnType);
}
qualifier = qualifier == null ? resolveQualifier(resolutionContext, beanType, returnType) : qualifier;
return resolutionContext.findBean(beanType, qualifier);
}
private <I, K extends Collection<BeanRegistration<I>>> K resolveBeanRegistrations(BeanResolutionContext resolutionContext,
Argument<K> returnType,
Argument<I> beanType,
Qualifier<I> qualifier) {
try {
if (beanType == null) {
throw new DependencyInjectionException(resolutionContext, "Cannot resolve bean registrations. Argument [" + returnType + "] missing generic type information.");
}
qualifier = qualifier == null ? resolveQualifier(resolutionContext, beanType, returnType) : qualifier;
Collection<BeanRegistration<I>> beanRegistrations = resolutionContext.getBeanRegistrations(beanType, qualifier);
return coerceCollectionToCorrectType(returnType.getType(), beanRegistrations, resolutionContext, returnType);
} catch (NoSuchBeanException e) {
if (returnType.isNullable()) {
return null;
}
throw new DependencyInjectionException(resolutionContext, e);
}
}
private <K> Argument<K> resolveArgument(BeanContext context, int argIndex, Argument<?>[] arguments) {
if (arguments == null) {
return null;
}
return resolveArgument(context, (Argument<K>) arguments[argIndex]);
}
private <K> Argument<K> resolveArgument(BeanContext context, Argument<K> argument) {
return ExpressionsAwareArgument.wrapIfNecessary(argument, context, this);
}
private <B> BeanRegistration<B> resolveBeanRegistration(BeanResolutionContext resolutionContext, BeanContext context,
@NonNull Argument<B> returnType, Argument<B> beanType, Qualifier<B> qualifier) {
try {
if (beanType == null) {
throw new DependencyInjectionException(resolutionContext, "Cannot resolve bean registration. Argument [" + returnType + "] missing generic type information.");
}
qualifier = qualifier == null ? resolveQualifier(resolutionContext, beanType, returnType) : qualifier;
return context.getBeanRegistration(beanType, qualifier);
} catch (NoSuchBeanException e) {
if (returnType.isNullable()) {
return null;
}
throw new DependencyInjectionException(resolutionContext, returnType, e);
}
}
@Nullable
private <B, R> Qualifier<B> resolveQualifier(BeanResolutionContext resolutionContext, Argument<B> beanType, Argument<R> resultType) {
if (isInnerConfiguration(beanType)) {
ConfigurationPath configurationPath = resolutionContext.getConfigurationPath();
Qualifier<B> q = configurationPath.beanQualifier();
if (q instanceof Named named && resultType.isContainerType()) {
return Qualifiers.byNamePrefix(named.getName());
}
if (q == null && isEachBeanParent(beanType)) {
return (Qualifier<B>) resolutionContext.getCurrentQualifier();
}
return q;
}
if (Qualifier.class == resultType.getType()) {
final Qualifier<B> currentQualifier = (Qualifier<B>) resolutionContext.getCurrentQualifier();
if (currentQualifier != null &&
currentQualifier.getClass() != InterceptorBindingQualifier.class &&
currentQualifier.getClass() != TypeAnnotationQualifier.class) {
return currentQualifier;
}
return resolutionContext.getConfigurationPath().beanQualifier();
} else if (precalculatedInfo.isIterable && resultType.isAnnotationPresent(Parameter.class)) {
return (Qualifier<B>) resolutionContext.getCurrentQualifier();
}
return null;
}
@SuppressWarnings("unchecked")
private <I, K extends Collection<I>> K coerceCollectionToCorrectType(Class<K> collectionType, Collection<I> beansOfType, BeanResolutionContext resolutionContext, Argument<?> argument) {
if (argument.isArray() || collectionType.isInstance(beansOfType)) {
// Arrays are converted by compile-time code
return (K) beansOfType;
} else {
return (K) CollectionUtils.convertCollection(collectionType, beansOfType)
.orElseThrow(() -> new DependencyInjectionException(resolutionContext, "Cannot create a collection of type: " + collectionType.getName()));
}
}
private Object getExpressionValueForArgument(Argument<?> argument) {
Argument<?> t = argument.isOptional() ? argument.getFirstTypeVariable().orElse(Argument.OBJECT_ARGUMENT) : argument;
Optional<?> expressionValue =
argument.getAnnotationMetadata()
.getValue(Value.class, t);
return expressionValue.orElse(null);
}
@Internal
@UsedByGeneratedCode
public record PrecalculatedInfo(
Optional<String> scope,
boolean isAbstract,
boolean isIterable,
boolean isSingleton,
boolean isPrimary,
boolean isConfigurationProperties,
boolean isContainerType,
boolean requiresMethodProcessing,
boolean hasEvaluatedExpressions
) {
public PrecalculatedInfo(Optional<String> scope, boolean isAbstract, boolean isIterable, boolean isSingleton, boolean isPrimary, boolean isConfigurationProperties, boolean isContainerType, boolean requiresMethodProcessing) {
this(scope, isAbstract, isIterable, isSingleton, isPrimary, isConfigurationProperties, isContainerType, requiresMethodProcessing, false);
}
}
/**
* Internal environment aware annotation metadata delegate.
*/
private final class BeanAnnotationMetadata extends AbstractEnvironmentAnnotationMetadata {
BeanAnnotationMetadata(AnnotationMetadata targetMetadata) {
super(targetMetadata);
}
@Nullable
@Override
protected Environment getEnvironment() {
return environment;
}
}
/**
* The data class containing all method reference information.
*/
@Internal
@SuppressWarnings("VisibilityModifier")
public static final class MethodReference extends MethodOrFieldReference {
public final String methodName;
public final Argument[] arguments;
public final AnnotationMetadata annotationMetadata;
public final boolean isPreDestroyMethod;
public final boolean isPostConstructMethod;
public MethodReference(Class declaringType,
String methodName,
Argument[] arguments,
@Nullable AnnotationMetadata annotationMetadata) {
this(declaringType, methodName, arguments, annotationMetadata, false, false);
}
public MethodReference(Class declaringType,
String methodName,
Argument[] arguments,
@Nullable AnnotationMetadata annotationMetadata,
boolean isPostConstructMethod,
boolean isPreDestroyMethod) {
super(declaringType);
this.methodName = methodName;
this.isPostConstructMethod = isPostConstructMethod;
this.isPreDestroyMethod = isPreDestroyMethod;
if (arguments != null) {
for (int i = 0; i < arguments.length; i++) {
Argument<?> argument = arguments[i];
if (argument.getAnnotationMetadata().hasEvaluatedExpressions()) {
arguments[i] = ExpressionsAwareArgument.wrapIfNecessary(argument);
}
}
}
this.arguments = arguments;
this.annotationMetadata =
annotationMetadata == null
? AnnotationMetadata.EMPTY_METADATA
: EvaluatedAnnotationMetadata.wrapIfNecessary(annotationMetadata);
}
}
/**
* The data class containing all filed reference information.
*/
@Internal
@SuppressWarnings("VisibilityModifier")
public static final class FieldReference extends MethodOrFieldReference {
public final Argument argument;
public FieldReference(Class declaringType, Argument argument) {
super(declaringType);
this.argument = ExpressionsAwareArgument.wrapIfNecessary(argument);
}
}
/**
* The shared data class between method and field reference.
*/
@Internal
public abstract static class MethodOrFieldReference {
final Class declaringType;
public MethodOrFieldReference(Class<?> declaringType) {
this.declaringType = declaringType;
}
}
/**
* The data class containing annotation injection information.
*/
@Internal
@SuppressWarnings("VisibilityModifier")
public static final class AnnotationReference {
public final Argument argument;
public AnnotationReference(Argument argument) {
this.argument = ExpressionsAwareArgument.wrapIfNecessary(argument);
}
}
}