io.micronaut.context.AbstractInitializableBeanDefinition Maven / Gradle / Ivy
/*
* Copyright 2017-2020 original authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.micronaut.context;
import io.micronaut.context.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.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 org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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;
/**
* 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.
*
* 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)
*
* The {@code io.micronaut.inject.writer.BeanDefinitionWriter} class can be used to produce bean definitions at
* compile or runtime
*
* @param The Bean definition type
* @author Graeme Rocher
* @author Denis Stepanov
* @since 3.0
*/
@Internal
public abstract class AbstractInitializableBeanDefinition extends AbstractBeanContextConditional
implements InstantiatableBeanDefinition, InjectableBeanDefinition, EnvironmentConfigurable, BeanContextConfigurable {
private static final Logger LOG = LoggerFactory.getLogger(AbstractInitializableBeanDefinition.class);
@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
private static final Optional> SINGLETON_SCOPE = Optional.of(Singleton.class);
private final Class 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 executableMethodsDefinition;
@Nullable
private final Map[]> typeArgumentsMap;
@Nullable
private AnnotationReference[] annotationInjection;
@Nullable
private Environment environment;
@Nullable
private Optional> containerElement;
@Nullable
private ConstructorInjectionPoint constructorInjectionPoint;
@Nullable
private List> methodInjectionPoints;
@Nullable
private List> fieldInjectionPoints;
@Nullable
private List> postConstructMethods;
@Nullable
private List> preDestroyMethods;
@Nullable
private Collection> requiredComponents;
@Nullable
private Argument>[] requiredParametrizedArguments;
private Qualifier declaredQualifier;
@Internal
@UsedByGeneratedCode
protected AbstractInitializableBeanDefinition(
Class beanType,
@Nullable MethodOrFieldReference constructor,
@Nullable AnnotationMetadata annotationMetadata,
@Nullable MethodReference[] methodInjection,
@Nullable FieldReference[] fieldInjection,
@Nullable AnnotationReference[] annotationInjection,
@Nullable ExecutableMethodsDefinition executableMethodsDefinition,
@Nullable Map[]> 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 final boolean isConfigurationProperties() {
return precalculatedInfo.isConfigurationProperties;
}
@Override
public Qualifier 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> getContainerElement() {
if (precalculatedInfo.isContainerType) {
if (containerElement != null) {
return containerElement;
}
if (getBeanType().isArray()) {
containerElement = Optional.of(Argument.of(getBeanType().getComponentType()));
} else {
final List> 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> 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 boolean isPrimary() {
return precalculatedInfo.isPrimary;
}
@Override
public boolean requiresMethodProcessing() {
return precalculatedInfo.requiresMethodProcessing;
}
@Override
public final Optional> findMethod(String name, Class>... argumentTypes) {
if (executableMethodsDefinition == null) {
return Optional.empty();
}
return executableMethodsDefinition.findMethod(name, argumentTypes);
}
@Override
public final Stream> 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> 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 getScopeName() {
return precalculatedInfo.scope;
}
@Override
public final Class getBeanType() {
return type;
}
@Override
@NonNull
public Set> getExposedTypes() {
return Collections.EMPTY_SET;
}
@Override
public final Optional> getDeclaringType() {
if (constructor == null) {
return Optional.of(type);
}
return Optional.of(constructor.declaringType);
}
@Override
public final ConstructorInjectionPoint getConstructor() {
if (constructorInjectionPoint != null) {
return constructorInjectionPoint;
}
if (constructor == null) {
DefaultConstructorInjectionPoint 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 ("".equals(methodConstructor.methodName)) {
DefaultConstructorInjectionPoint point = new DefaultConstructorInjectionPoint<>(
this,
methodConstructor.declaringType,
methodConstructor.annotationMetadata,
methodConstructor.arguments
);
if (environment != null) {
point.configure(environment);
}
constructorInjectionPoint = point;
} else {
DefaultMethodConstructorInjectionPoint 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 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> getRequiredComponents() {
if (requiredComponents != null) {
return requiredComponents;
}
Set> requiredComponents = new HashSet<>();
Consumer 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> getInjectedMethods() {
if (methodInjection == null) {
return Collections.emptyList();
}
if (methodInjectionPoints != null) {
return methodInjectionPoints;
}
List> methodInjectionPoints = new ArrayList<>(methodInjection.length);
for (MethodReference methodReference : methodInjection) {
MethodInjectionPoint 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> getInjectedFields() {
if (fieldInjection == null) {
return Collections.emptyList();
}
if (fieldInjectionPoints != null) {
return fieldInjectionPoints;
}
List> fieldInjectionPoints = new ArrayList<>(fieldInjection.length);
for (FieldReference fieldReference : fieldInjection) {
FieldInjectionPoint 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> getPostConstructMethods() {
if (methodInjection == null) {
return Collections.emptyList();
}
if (postConstructMethods != null) {
return postConstructMethods;
}
List> postConstructMethods = new ArrayList<>(1);
for (MethodInjectionPoint methodInjectionPoint : getInjectedMethods()) {
if (methodInjectionPoint.isPostConstructMethod()) {
postConstructMethods.add(methodInjectionPoint);
}
}
this.postConstructMethods = Collections.unmodifiableList(postConstructMethods);
return this.postConstructMethods;
}
@Override
public final List> getPreDestroyMethods() {
if (methodInjection == null) {
return Collections.emptyList();
}
if (preDestroyMethods != null) {
return preDestroyMethods;
}
List> preDestroyMethods = new ArrayList<>(1);
for (MethodInjectionPoint 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> 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 methodInjectionPoint : methodInjectionPoints) {
if (methodInjectionPoint instanceof EnvironmentConfigurable environmentConfigurable) {
environmentConfigurable.configure(environment);
}
}
}
if (fieldInjectionPoints != null) {
for (FieldInjectionPoint 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 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 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
*/
@Internal
protected final void warn(String message) {
if (LOG.isWarnEnabled()) {
LOG.warn(message);
}
}
/**
* Allows printing warning messages produced by the compiler.
*
* @param type The type
* @param method The method
* @param property The property
*/
@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);
}
}
/**
* 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 ctor = getConstructor();
if (ctor != null) {
requiredParametrizedArguments = Arrays.stream(ctor.getArguments())
.filter(arg -> {
Optional 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 requiredArgumentValues) throws BeanInstantiationException {
requiredArgumentValues = requiredArgumentValues != null ? new LinkedHashMap<>(requiredArgumentValues) : Collections.emptyMap();
Optional 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 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, ListenersSupplier>> beanInitializedEventListeners
= ((DefaultBeanContext) context).beanInitializedEventListeners;
if (CollectionUtils.isNotEmpty(beanInitializedEventListeners)) {
for (Map.Entry, ListenersSupplier> 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) {
beanPropertyValue = ((Optional>) beanPropertyValue).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 [" + getBeanType() + "] uses reflection to inject method: '" + 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) {
throw (BeanContextException) e;
} else {
throw new DependencyInjectionException(resolutionContext, "Error invoking method: " + methodRef.methodName, e);
}
}
}
/**
* Sets the value of a field of a 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 [" + getBeanType() + "] uses reflection to inject field: '" + 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) {
throw (BeanContextException) e;
} 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 = resolvePropertyValue(resolutionContext, context, 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 resolvePropertyValue(resolutionContext, context, 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 = resolvePropertyValue(resolutionContext, context, 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 resolvePropertyValue(resolutionContext, context, 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
*
* 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 The bean type
* @return The resolved bean
*/
@Internal
@SuppressWarnings("WeakerAccess")
@UsedByGeneratedCode
protected final K getBeanForMethodArgument(BeanResolutionContext resolutionContext, BeanContext context, int methodIndex, int argIndex, 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 resolveBean(resolutionContext, argument, qualifier);
}
}
/**
* 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 The bean type
* @param The result collection type
* @return The resolved bean
*/
@Internal
@UsedByGeneratedCode
protected final > R getBeansOfTypeForMethodArgument(BeanResolutionContext resolutionContext, BeanContext context, int methodIndex, int argumentIndex, Argument genericType, Qualifier qualifier) {
MethodReference methodRef = methodInjection[methodIndex];
Argument 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
*
* 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);
}
}
/**
* 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