
io.micronaut.context.AbstractBeanDefinition 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* Copyright 2017 original authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.micronaut.context;
import io.micronaut.context.annotation.*;
import io.micronaut.context.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.NoSuchBeanException;
import io.micronaut.core.annotation.AnnotationMetadata;
import io.micronaut.core.annotation.Internal;
import io.micronaut.core.annotation.UsedByGeneratedCode;
import io.micronaut.core.bind.annotation.Bindable;
import io.micronaut.core.convert.ArgumentConversionContext;
import io.micronaut.core.convert.ConversionContext;
import io.micronaut.core.naming.Named;
import io.micronaut.core.type.Argument;
import io.micronaut.core.type.DefaultArgument;
import io.micronaut.core.util.CollectionUtils;
import io.micronaut.core.util.StringUtils;
import io.micronaut.core.value.PropertyResolver;
import io.micronaut.inject.*;
import io.micronaut.inject.annotation.AbstractEnvironmentAnnotationMetadata;
import io.micronaut.inject.qualifiers.Qualifiers;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.Nullable;
import javax.inject.Inject;
import javax.inject.Provider;
import javax.inject.Scope;
import javax.inject.Singleton;
import java.lang.annotation.Annotation;
import java.lang.reflect.Array;
import java.lang.reflect.Modifier;
import java.util.*;
import java.util.stream.Stream;
/**
* 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 {@link 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
* @see io.micronaut.inject.writer.BeanDefinitionWriter
* @since 1.0
*/
@Internal
public class AbstractBeanDefinition extends AbstractBeanContextConditional implements BeanDefinition, EnvironmentConfigurable {
private static final Logger LOG = LoggerFactory.getLogger(AbstractBeanDefinition.class);
private static final String NAMED_ATTRIBUTE = Named.class.getName();
@SuppressWarnings("WeakerAccess")
protected final List methodInjectionPoints = new ArrayList<>(3);
@SuppressWarnings("WeakerAccess")
protected final List fieldInjectionPoints = new ArrayList<>(3);
@SuppressWarnings("WeakerAccess")
protected List postConstructMethods;
@SuppressWarnings("WeakerAccess")
protected List preDestroyMethods;
@SuppressWarnings("WeakerAccess")
protected Map> executableMethodMap;
private final Class type;
private final boolean isAbstract;
private final boolean isConfigurationProperties;
private final Class> declaringType;
private final ConstructorInjectionPoint constructor;
private final Collection requiredComponents = new HashSet<>(3);
private AnnotationMetadata beanAnnotationMetadata;
private Environment environment;
/**
* Constructs a bean definition that is produced from a method call on another type (factory bean).
*
* @param producedType The produced type
* @param declaringType The declaring type of the method
* @param methodName The method name
* @param methodMetadata The metadata for the method
* @param requiresReflection Whether reflection is required to invoke the method
* @param arguments The method arguments
*/
@SuppressWarnings({"unchecked", "WeakerAccess"})
@Internal
@UsedByGeneratedCode
protected AbstractBeanDefinition(Class producedType,
Class> declaringType,
String methodName,
AnnotationMetadata methodMetadata,
boolean requiresReflection,
Argument... arguments) {
AnnotationMetadata beanAnnotationMetadata = getAnnotationMetadata();
this.type = producedType;
this.isAbstract = false; // factory beans are never abstract
this.declaringType = declaringType;
if (requiresReflection) {
this.constructor = new ReflectionMethodConstructorInjectionPoint(
this,
declaringType,
methodName,
arguments,
methodMetadata
);
} else {
this.constructor = new DefaultMethodConstructorInjectionPoint(
this,
declaringType,
methodName,
arguments,
methodMetadata
);
}
this.isConfigurationProperties = hasStereotype(ConfigurationReader.class) || isIterable();
this.addRequiredComponents(arguments);
}
/**
* Constructs a bean for the given type.
*
* @param type The type
* @param constructorAnnotationMetadata The annotation metadata for the constructor
* @param requiresReflection Whether reflection is required
* @param arguments The constructor arguments used to build the bean
*/
@Internal
@UsedByGeneratedCode
protected AbstractBeanDefinition(Class type,
AnnotationMetadata constructorAnnotationMetadata,
boolean requiresReflection,
Argument... arguments) {
AnnotationMetadata beanAnnotationMetadata = getAnnotationMetadata();
this.type = type;
this.isAbstract = Modifier.isAbstract(this.type.getModifiers());
this.declaringType = type;
if (requiresReflection) {
this.constructor = new ReflectionConstructorInjectionPoint<>(
this,
type,
constructorAnnotationMetadata,
arguments);
} else {
this.constructor = new DefaultConstructorInjectionPoint<>(
this,
type,
constructorAnnotationMetadata,
arguments
);
}
this.isConfigurationProperties = hasStereotype(ConfigurationReader.class) || isIterable();
this.addRequiredComponents(arguments);
}
@Override
public @NonNull List> getTypeArguments(String type) {
if (type == null) {
return Collections.emptyList();
}
Map[]> typeArguments = getTypeArgumentsMap();
Argument>[] arguments = typeArguments.get(type);
if (arguments != null) {
return Arrays.asList(arguments);
}
return Collections.emptyList();
}
@Override
public AnnotationMetadata getAnnotationMetadata() {
if (this.beanAnnotationMetadata == null) {
this.beanAnnotationMetadata = initializeAnnotationMetadata();
}
return this.beanAnnotationMetadata;
}
@Override
public boolean isAbstract() {
return this.isAbstract;
}
@Override
public boolean isIterable() {
return hasDeclaredStereotype(EachProperty.class) || hasDeclaredStereotype(EachBean.class);
}
@Override
public boolean isPrimary() {
return hasDeclaredStereotype(Primary.class);
}
@SuppressWarnings("unchecked")
@Override
public Optional> findMethod(String name, Class... argumentTypes) {
if (executableMethodMap != null) {
MethodKey methodKey = new MethodKey(name, argumentTypes);
ExecutableMethod invocableMethod = (ExecutableMethod) executableMethodMap.get(methodKey);
if (invocableMethod != null) {
return Optional.of(invocableMethod);
}
}
return Optional.empty();
}
@Override
@SuppressWarnings({"unchecked"})
public Stream> findPossibleMethods(String name) {
if (executableMethodMap != null) {
if (executableMethodMap.keySet().stream().anyMatch(methodKey -> methodKey.name.equals(name))) {
return executableMethodMap
.values()
.stream()
.filter((method) -> method.getMethodName().equals(name));
}
}
return Stream.empty();
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
AbstractBeanDefinition> that = (AbstractBeanDefinition>) o;
return getClass().equals(that.getClass());
}
@Override
public int hashCode() {
return getClass().hashCode();
}
@Override
public String toString() {
return "Definition: " + declaringType.getName();
}
@Override
public boolean isProvided() {
return getAnnotationMetadata().hasDeclaredStereotype(Provided.class);
}
@Override
public boolean isSingleton() {
return getAnnotationMetadata().hasDeclaredStereotype(Singleton.class);
}
@Override
public Optional> getScope() {
return getAnnotationMetadata().getDeclaredAnnotationTypeByStereotype(Scope.class);
}
@Override
public final Class getBeanType() {
return type;
}
@Override
public final Optional> getDeclaringType() {
return Optional.ofNullable(declaringType);
}
@Override
public final ConstructorInjectionPoint getConstructor() {
return constructor;
}
@Override
public Collection getRequiredComponents() {
return Collections.unmodifiableCollection(requiredComponents);
}
@Override
public final Collection getInjectedMethods() {
return Collections.unmodifiableCollection(methodInjectionPoints);
}
@Override
public final Collection getInjectedFields() {
return Collections.unmodifiableCollection(fieldInjectionPoints);
}
@Override
public final Collection getPostConstructMethods() {
if (postConstructMethods != null) {
return Collections.unmodifiableCollection(postConstructMethods);
} else {
return Collections.emptyList();
}
}
@Override
public final Collection getPreDestroyMethods() {
if (preDestroyMethods != null) {
return Collections.unmodifiableCollection(preDestroyMethods);
} else {
return Collections.emptyList();
}
}
@Override
public String getName() {
return getBeanType().getName();
}
@SuppressWarnings("unchecked")
@Override
public T inject(BeanContext context, T bean) {
return (T) injectBean(new DefaultBeanResolutionContext(context, this), context, bean);
}
@SuppressWarnings("unchecked")
@Override
public T inject(BeanResolutionContext resolutionContext, BeanContext context, T bean) {
return (T) injectBean(resolutionContext, context, bean);
}
@Override
public Collection> getExecutableMethods() {
if (executableMethodMap != null) {
return Collections.unmodifiableCollection(this.executableMethodMap.values());
} else {
return Collections.emptyList();
}
}
/**
* Configures the bean for the given {@link BeanContext}. If the context features an
* {@link io.micronaut.context.env.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 (constructor instanceof EnvironmentConfigurable) {
((EnvironmentConfigurable) constructor).configure(environment);
}
for (MethodInjectionPoint methodInjectionPoint : methodInjectionPoints) {
if (methodInjectionPoint instanceof EnvironmentConfigurable) {
((EnvironmentConfigurable) methodInjectionPoint).configure(environment);
}
}
if (executableMethodMap != null) {
for (ExecutableMethod executableMethod : executableMethodMap.values()) {
if (executableMethod instanceof EnvironmentConfigurable) {
((EnvironmentConfigurable) executableMethod).configure(environment);
}
}
}
}
}
/**
* 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);
}
}
/**
* Resolves the proxied bean instance for this bean.
*
* @param beanContext The {@link BeanContext}
* @return The proxied bean
*/
@SuppressWarnings({"unchecked", "unused"})
@Internal
protected final Object getProxiedBean(BeanContext beanContext) {
DefaultBeanContext defaultBeanContext = (DefaultBeanContext) beanContext;
Optional qualifier = getAnnotationMetadata().getAnnotationNameByStereotype(javax.inject.Qualifier.class);
return defaultBeanContext.getProxyTargetBean(
getBeanType(),
(Qualifier) qualifier.map(q -> Qualifiers.byAnnotation(getAnnotationMetadata(), q)).orElse(null)
);
}
/**
* Adds a new {@link ExecutableMethod}.
*
* @param executableMethod The method
* @return The bean definition
*/
@SuppressWarnings("unused")
@Internal
@UsedByGeneratedCode
protected final AbstractBeanDefinition addExecutableMethod(ExecutableMethod executableMethod) {
MethodKey key = new MethodKey(executableMethod.getMethodName(), executableMethod.getArgumentTypes());
if (executableMethodMap == null) {
executableMethodMap = new LinkedHashMap<>(3);
}
executableMethodMap.put(key, executableMethod);
return this;
}
/**
* Adds an injection point for a field. Typically called by a dynamically generated subclass.
*
* @param declaringType The declaring type
* @param fieldType The field type
* @param field The name of the field
* @param annotationMetadata The annotation metadata for the field
* @param typeArguments The arguments
* @param requiresReflection Whether reflection is required
* @return this component definition
*/
@SuppressWarnings({"unused", "unchecked"})
@Internal
@UsedByGeneratedCode
protected final AbstractBeanDefinition addInjectionPoint(
Class declaringType,
Class fieldType,
String field,
@Nullable AnnotationMetadata annotationMetadata,
@Nullable Argument[] typeArguments,
boolean requiresReflection) {
if (annotationMetadata != null && annotationMetadata.hasDeclaredAnnotation(Inject.class)) {
requiredComponents.add(fieldType);
}
if (requiresReflection) {
fieldInjectionPoints.add(new ReflectionFieldInjectionPoint(
this,
declaringType,
fieldType,
field,
annotationMetadata,
typeArguments
));
} else {
fieldInjectionPoints.add(new DefaultFieldInjectionPoint(
this,
declaringType,
fieldType,
field,
annotationMetadata,
typeArguments
));
}
return this;
}
/**
* Adds an injection point for a method that cannot be resolved at runtime, but a compile time produced injection
* point exists. This allows the framework to recover and relay better error messages to the user instead of just
* NoSuchMethodError.
*
* @param declaringType The declaring type
* @param method The method
* @param arguments The argument types
* @param annotationMetadata The annotation metadata
* @param requiresReflection Whether the method requires reflection to invoke
* @return this component definition
*/
@SuppressWarnings({"unchecked", "unused"})
@Internal
@UsedByGeneratedCode
protected final AbstractBeanDefinition addInjectionPoint(
Class declaringType,
String method,
@Nullable Argument[] arguments,
@Nullable AnnotationMetadata annotationMetadata,
boolean requiresReflection) {
return addInjectionPointInternal(
declaringType,
method,
arguments,
annotationMetadata,
requiresReflection,
this.methodInjectionPoints
);
}
/**
* Adds a post construct method definition.
*
* @param declaringType The declaring type
* @param method The method
* @param arguments The arguments
* @param annotationMetadata The annotation metadata
* @param requiresReflection Whether the method requires reflection
* @return This bean definition
*/
@SuppressWarnings("unused")
@Internal
@UsedByGeneratedCode
protected final AbstractBeanDefinition addPostConstruct(Class declaringType,
String method,
@Nullable Argument[] arguments,
@Nullable AnnotationMetadata annotationMetadata,
boolean requiresReflection) {
if (postConstructMethods == null) {
postConstructMethods = new ArrayList<>(1);
}
return addInjectionPointInternal(declaringType, method, arguments, annotationMetadata, requiresReflection, this.postConstructMethods);
}
/**
* Adds a pre destroy method definition.
*
* @param declaringType The declaring type
* @param method The method
* @param arguments The arguments
* @param annotationMetadata The annotation metadata
* @param requiresReflection Whether the method requires reflection
* @return This bean definition
*/
@SuppressWarnings("unused")
@Internal
@UsedByGeneratedCode
protected final AbstractBeanDefinition addPreDestroy(Class declaringType,
String method,
Argument[] arguments,
AnnotationMetadata annotationMetadata,
boolean requiresReflection) {
if (preDestroyMethods == null) {
preDestroyMethods = new ArrayList<>(1);
}
return addInjectionPointInternal(declaringType, method, arguments, annotationMetadata, requiresReflection, this.preDestroyMethods);
}
/**
* The default implementation which provides no injection. To be overridden by compile time tooling.
*
* @param resolutionContext The resolution context
* @param context The bean context
* @param bean The bean
* @return The injected bean
*/
@Internal
@SuppressWarnings({"WeakerAccess", "unused"})
@UsedByGeneratedCode
protected Object injectBean(BeanResolutionContext resolutionContext, BeanContext context, Object bean) {
return bean;
}
/**
* Inject another bean, for example one created via factory.
*
* @param resolutionContext The reslution context
* @param context The context
* @param bean The bean
* @return The bean
*/
@Internal
@SuppressWarnings({"unused"})
@UsedByGeneratedCode
protected Object injectAnother(BeanResolutionContext resolutionContext, BeanContext context, Object bean) {
DefaultBeanContext defaultContext = (DefaultBeanContext) context;
if (bean == null) {
throw new BeanInstantiationException(resolutionContext, "Bean factory returned null");
}
return defaultContext.inject(resolutionContext, this, bean);
}
/**
* 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) {
DefaultBeanContext defaultContext = (DefaultBeanContext) context;
boolean addInCreationHandling = isSingleton() && !CollectionUtils.isNotEmpty(postConstructMethods);
DefaultBeanContext.BeanKey key = null;
if (addInCreationHandling) {
// ensure registration as an inflight bean if a post construct is present
// this is to ensure that if the post construct method does anything funky to
// cause recreation of this bean then we don't have a circular problem
key = new DefaultBeanContext.BeanKey(this, resolutionContext.getCurrentQualifier());
resolutionContext.addInFlightBean(key, bean);
}
final Set>> beanInitializedEventListeners
= ((DefaultBeanContext) context).beanInitializedEventListeners;
if (CollectionUtils.isNotEmpty(beanInitializedEventListeners)) {
for (Map.Entry> entry : beanInitializedEventListeners) {
if (entry.getKey().isAssignableFrom(getBeanType())) {
for (BeanInitializedEventListener listener : entry.getValue()) {
bean = listener.onInitialized(new BeanInitializingEvent(context, this, bean));
if (bean == null) {
throw new BeanInstantiationException(resolutionContext, "Listener [" + listener + "] returned null from onInitialized event");
}
}
}
}
}
for (int i = 0; i < methodInjectionPoints.size(); i++) {
MethodInjectionPoint methodInjectionPoint = methodInjectionPoints.get(i);
if (methodInjectionPoint.isPostConstructMethod() && methodInjectionPoint.requiresReflection()) {
injectBeanMethod(resolutionContext, defaultContext, i, bean);
}
}
if (bean instanceof LifeCycle) {
bean = ((LifeCycle) bean).start();
}
try {
return bean;
} finally {
if (addInCreationHandling) {
// ensure registration as an inflight bean if a post construct is present
// this is to ensure that if the post construct method does anything funky to
// cause recreation of this bean then we don't have a circular problem
resolutionContext.removeInFlightBean(key);
}
}
}
/**
* 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
*/
@UsedByGeneratedCode
protected Object preDestroy(BeanResolutionContext resolutionContext, BeanContext context, Object bean) {
DefaultBeanContext defaultContext = (DefaultBeanContext) context;
for (int i = 0; i < methodInjectionPoints.size(); i++) {
MethodInjectionPoint methodInjectionPoint = methodInjectionPoints.get(i);
if (methodInjectionPoint.isPreDestroyMethod() && methodInjectionPoint.requiresReflection()) {
injectBeanMethod(resolutionContext, defaultContext, i, bean);
}
}
if (bean instanceof LifeCycle) {
bean = ((LifeCycle) bean).stop();
}
return bean;
}
/**
* Inject 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
*/
@Internal
@SuppressWarnings("WeakerAccess")
protected void injectBeanMethod(BeanResolutionContext resolutionContext, DefaultBeanContext context, int methodIndex, Object bean) {
MethodInjectionPoint methodInjectionPoint = methodInjectionPoints.get(methodIndex);
Argument[] methodArgumentTypes = methodInjectionPoint.getArguments();
Object[] methodArgs = new Object[methodArgumentTypes.length];
for (int i = 0; i < methodArgumentTypes.length; i++) {
methodArgs[i] = getBeanForMethodArgument(resolutionContext, context, methodIndex, i);
}
try {
methodInjectionPoint.invoke(bean, methodArgs);
} catch (Throwable e) {
throw new BeanInstantiationException(this, e);
}
}
/**
* Injects the value of a field of a bean that requires reflection.
*
* @param resolutionContext The resolution context
* @param context The bean context
* @param index The index of the field
* @param bean The bean being injected
*/
@SuppressWarnings("unused")
@Internal
protected final void injectBeanField(BeanResolutionContext resolutionContext, DefaultBeanContext context, int index, Object bean) {
FieldInjectionPoint fieldInjectionPoint = fieldInjectionPoints.get(index);
boolean isInject = fieldInjectionPoint.getAnnotationMetadata().hasDeclaredAnnotation(Inject.class);
try {
Object value;
if (isInject) {
instrumentAnnotationMetadata(context, fieldInjectionPoint);
value = getBeanForField(resolutionContext, context, fieldInjectionPoint);
} else {
value = getValueForField(resolutionContext, context, index);
}
if (value != null) {
//noinspection unchecked
fieldInjectionPoint.set(bean, value);
}
} catch (Throwable e) {
if (e instanceof BeanContextException) {
throw (BeanContextException) e;
} else {
throw new DependencyInjectionException(resolutionContext, fieldInjectionPoint, "Error setting field value: " + e.getMessage(), e);
}
}
}
/**
* 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
* @return The value
*/
@SuppressWarnings({"unused", "unchecked"})
@Internal
protected final Object getValueForMethodArgument(BeanResolutionContext resolutionContext, BeanContext context, int methodIndex, int argIndex) {
MethodInjectionPoint injectionPoint = methodInjectionPoints.get(methodIndex);
Argument argument = injectionPoint.getArguments()[argIndex];
BeanResolutionContext.Path path = resolutionContext.getPath();
path.pushMethodArgumentResolve(this, injectionPoint, argument);
if (context instanceof ApplicationContext) {
// can't use orElseThrow here due to compiler bug
try {
String valueAnnStr = argument.getAnnotationMetadata().stringValue(Value.class).orElse(null);
Class argumentType;
boolean isCollection = false;
if (Collection.class.isAssignableFrom(argument.getType())) {
argumentType = argument.getFirstTypeVariable().map(Argument::getType).orElse((Class) Object.class);
isCollection = true;
} else {
argumentType = argument.getType();
}
if (isInnerConfiguration(argumentType)) {
Qualifier qualifier = resolveQualifier(resolutionContext, argument, true);
if (isCollection) {
Collection beans = ((DefaultBeanContext) context).getBeansOfType(resolutionContext, argumentType, qualifier);
return coerceCollectionToCorrectType(argument.getType(), beans);
} else {
return ((DefaultBeanContext) context).getBean(resolutionContext, argumentType, qualifier);
}
} else {
String argumentName = argument.getName();
String valString = resolvePropertyValueName(resolutionContext, injectionPoint.getAnnotationMetadata(), argument, valueAnnStr);
ApplicationContext applicationContext = (ApplicationContext) context;
ArgumentConversionContext conversionContext = ConversionContext.of(argument);
Optional value = resolveValue(applicationContext, conversionContext, valueAnnStr != null, valString);
if (argumentType == Optional.class) {
return resolveOptionalObject(value);
} else {
if (value.isPresent()) {
return value.get();
} else {
if (argument.isDeclaredNullable()) {
return null;
}
throw new DependencyInjectionException(resolutionContext, injectionPoint, conversionContext, valString);
}
}
}
} finally {
path.pop();
}
} else {
path.pop();
throw new DependencyInjectionException(resolutionContext, argument, "BeanContext must support property resolution");
}
}
/**
* 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
* @return The value
*/
@Internal
@UsedByGeneratedCode
protected final boolean containsValueForMethodArgument(BeanResolutionContext resolutionContext, BeanContext context, int methodIndex, int argIndex) {
if (context instanceof ApplicationContext) {
MethodInjectionPoint injectionPoint = methodInjectionPoints.get(methodIndex);
Argument argument = injectionPoint.getArguments()[argIndex];
String valueAnnStr = argument.getAnnotationMetadata().stringValue(Value.class).orElse(null);
String valString = resolvePropertyValueName(resolutionContext, injectionPoint.getAnnotationMetadata(), argument, valueAnnStr);
ApplicationContext applicationContext = (ApplicationContext) context;
Class type = argument.getType();
boolean isConfigProps = type.isAnnotationPresent(ConfigurationProperties.class);
boolean result = isConfigProps || Map.class.isAssignableFrom(type) || Collection.class.isAssignableFrom(type) ? applicationContext.containsProperties(valString) : applicationContext.containsProperty(valString);
if (!result && isConfigurationProperties()) {
String cliOption = resolveCliOption(argument.getName());
if (cliOption != null) {
result = applicationContext.containsProperty(cliOption);
}
}
if (result && injectionPoint instanceof MissingMethodInjectionPoint) {
if (LOG.isWarnEnabled()) {
LOG.warn("Bean definition for type [{}] is compiled against an older version and value [{}] can no longer be set for missing method: {}",
getBeanType(),
valString,
injectionPoint.getName());
}
result = false;
}
return result;
}
return false;
}
/**
* 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
* @return The resolved bean
*/
@Internal
@SuppressWarnings("WeakerAccess")
@UsedByGeneratedCode
protected final Object getBeanForMethodArgument(BeanResolutionContext resolutionContext, BeanContext context, int methodIndex, int argIndex) {
MethodInjectionPoint injectionPoint = methodInjectionPoints.get(methodIndex);
Argument argument = injectionPoint.getArguments()[argIndex];
if (argument instanceof DefaultArgument) {
argument = new EnvironmentAwareArgument((DefaultArgument) argument);
instrumentAnnotationMetadata(context, argument);
}
return getBeanForMethodArgument(resolutionContext, context, injectionPoint, argument);
}
/**
* Obtains all bean definitions 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 injectionPoint The method injection point
* @param argument The argument
* @return The resolved bean
*/
@SuppressWarnings("WeakerAccess")
@Internal
protected final Collection getBeansOfTypeForMethodArgument(BeanResolutionContext resolutionContext, BeanContext context, MethodInjectionPoint injectionPoint, Argument argument) {
return resolveBeanWithGenericsFromMethodArgument(resolutionContext, injectionPoint, argument, (beanType, qualifier) -> {
boolean hasNoGenerics = !argument.getType().isArray() && argument.getTypeVariables().isEmpty();
if (hasNoGenerics) {
return ((DefaultBeanContext) context).getBean(resolutionContext, beanType, qualifier);
} else {
return ((DefaultBeanContext) context).getBeansOfType(resolutionContext, beanType, qualifier);
}
}
);
}
/**
* Obtains a bean provider 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 injectionPoint The method injection point
* @param argument The argument
* @return The resolved bean
*/
@SuppressWarnings("WeakerAccess")
@Internal
protected final Provider getBeanProviderForMethodArgument(BeanResolutionContext resolutionContext, BeanContext context, MethodInjectionPoint injectionPoint, Argument argument) {
return resolveBeanWithGenericsFromMethodArgument(resolutionContext, injectionPoint, argument, (beanType, qualifier) ->
((DefaultBeanContext) context).getBeanProvider(resolutionContext, beanType, qualifier)
);
}
/**
* Obtains an optional bean 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 injectionPoint The method injection point
* @param argument The argument
* @return The resolved bean
*/
@SuppressWarnings("WeakerAccess")
@Internal
protected final Optional findBeanForMethodArgument(BeanResolutionContext resolutionContext, BeanContext context, MethodInjectionPoint injectionPoint, Argument argument) {
return resolveBeanWithGenericsFromMethodArgument(resolutionContext, injectionPoint, argument, (beanType, qualifier) ->
((DefaultBeanContext) context).findBean(resolutionContext, beanType, qualifier)
);
}
/**
* Obtains all bean definitions 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 injectionPoint The method injection point
* @param argument The argument
* @return The resolved bean
*/
@SuppressWarnings("WeakerAccess")
@Internal
protected final Stream streamOfTypeForMethodArgument(BeanResolutionContext resolutionContext, BeanContext context, MethodInjectionPoint injectionPoint, Argument argument) {
return resolveBeanWithGenericsFromMethodArgument(resolutionContext, injectionPoint, argument, (beanType, qualifier) ->
((DefaultBeanContext) context).streamOfType(resolutionContext, beanType, qualifier)
);
}
/**
* Obtains a bean definition for a constructor 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 argIndex The argument index
* @return The resolved bean
*/
@SuppressWarnings("unused")
@Internal
@UsedByGeneratedCode
protected final Object getBeanForConstructorArgument(BeanResolutionContext resolutionContext, BeanContext context, int argIndex) {
ConstructorInjectionPoint constructorInjectionPoint = getConstructor();
Argument> argument = constructorInjectionPoint.getArguments()[argIndex];
if (argument instanceof DefaultArgument) {
argument = new EnvironmentAwareArgument((DefaultArgument) argument);
instrumentAnnotationMetadata(context, argument);
}
Class argumentType = argument.getType();
if (argumentType == BeanResolutionContext.class) {
return resolutionContext;
} else if (argumentType.isArray()) {
Collection beansOfType = getBeansOfTypeForConstructorArgument(resolutionContext, context, constructorInjectionPoint, argument);
return beansOfType.toArray((Object[]) Array.newInstance(argumentType.getComponentType(), beansOfType.size()));
} else if (Collection.class.isAssignableFrom(argumentType)) {
Collection beansOfType = getBeansOfTypeForConstructorArgument(resolutionContext, context, constructorInjectionPoint, argument);
return coerceCollectionToCorrectType(argumentType, beansOfType);
} else if (Stream.class.isAssignableFrom(argumentType)) {
return streamOfTypeForConstructorArgument(resolutionContext, context, constructorInjectionPoint, argument);
} else if (Provider.class.isAssignableFrom(argumentType)) {
return getBeanProviderForConstructorArgument(resolutionContext, context, constructorInjectionPoint, argument);
} else if (Optional.class.isAssignableFrom(argumentType)) {
return findBeanForConstructorArgument(resolutionContext, context, constructorInjectionPoint, argument);
} else {
BeanResolutionContext.Path path = resolutionContext.getPath();
BeanResolutionContext.Segment current = path.peek();
boolean isNullable = argument.isDeclaredNullable();
if (isNullable && current != null && current.getArgument().equals(argument)) {
return null;
} else {
path.pushConstructorResolve(this, argument);
try {
Object bean;
Qualifier qualifier = resolveQualifier(resolutionContext, argument, isInnerConfiguration(argument.getType()));
if (Qualifier.class.isAssignableFrom(argumentType)) {
bean = qualifier;
} else {
//noinspection unchecked
bean = ((DefaultBeanContext) context).getBean(resolutionContext, argumentType, qualifier);
}
path.pop();
return bean;
} catch (NoSuchBeanException e) {
if (isNullable) {
path.pop();
return null;
}
throw new DependencyInjectionException(resolutionContext, argument, e);
}
}
}
}
/**
* Obtains a value for a bean definition for a constructor 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 argIndex The argument index
* @return The resolved bean
*/
@SuppressWarnings("unused")
@Internal
@UsedByGeneratedCode
protected final Object getValueForConstructorArgument(BeanResolutionContext resolutionContext, BeanContext context, int argIndex) {
ConstructorInjectionPoint constructorInjectionPoint = getConstructor();
BeanResolutionContext.Path path = resolutionContext.getPath();
Argument> argument = constructorInjectionPoint.getArguments()[argIndex];
path.pushConstructorResolve(this, argument);
try {
Object result;
if (context instanceof ApplicationContext) {
ApplicationContext propertyResolver = (ApplicationContext) context;
AnnotationMetadata argMetadata = argument.getAnnotationMetadata();
Optional valAnn = argMetadata.stringValue(Value.class);
String prop = resolvePropertyValueName(resolutionContext, argMetadata, argument, valAnn.orElse(null));
ArgumentConversionContext> conversionContext = ConversionContext.of(argument);
Optional> value = resolveValue(propertyResolver, conversionContext, valAnn.isPresent(), prop);
if (argument.getType() == Optional.class) {
return resolveOptionalObject(value);
} else {
// can't use orElseThrow here due to compiler bug
if (value.isPresent()) {
result = value.get();
} else {
if (argument.isDeclaredNullable()) {
result = null;
} else {
result = argMetadata.getValue(Bindable.class, "defaultValue", argument)
.orElseThrow(() -> new DependencyInjectionException(resolutionContext, conversionContext, prop));
}
}
}
} else {
throw new DependencyInjectionException(resolutionContext, argument, "BeanContext must support property resolution");
}
if (this instanceof ValidatedBeanDefinition) {
((ValidatedBeanDefinition) this).validateBeanArgument(
resolutionContext,
constructorInjectionPoint,
argument,
argIndex,
result
);
}
return result;
} catch (NoSuchBeanException | BeanInstantiationException e) {
throw new DependencyInjectionException(resolutionContext, argument, e);
} finally {
path.pop();
}
}
/**
* Obtains a bean provider for a constructor 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 constructorInjectionPoint The constructor injection point
* @param argument The argument
* @return The resolved bean
*/
@SuppressWarnings("WeakerAccess")
@Internal
protected final Provider getBeanProviderForConstructorArgument(BeanResolutionContext resolutionContext, BeanContext context, @SuppressWarnings("unused") ConstructorInjectionPoint constructorInjectionPoint, Argument argument) {
return resolveBeanWithGenericsFromConstructorArgument(resolutionContext, argument, (beanType, qualifier) ->
((DefaultBeanContext) context).getBeanProvider(resolutionContext, beanType, qualifier)
);
}
/**
* Obtains all bean definitions for a constructor 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 constructorInjectionPoint The constructor injection point
* @param argument The argument
* @return The resolved bean
*/
@SuppressWarnings("WeakerAccess")
@Internal
protected final Collection getBeansOfTypeForConstructorArgument(BeanResolutionContext resolutionContext, BeanContext context, @SuppressWarnings("unused") ConstructorInjectionPoint constructorInjectionPoint, Argument argument) {
return resolveBeanWithGenericsFromConstructorArgument(resolutionContext, argument, (beanType, qualifier) -> {
boolean hasNoGenerics = !argument.getType().isArray() && argument.getTypeVariables().isEmpty();
if (hasNoGenerics) {
return ((DefaultBeanContext) context).getBean(resolutionContext, beanType, qualifier);
} else {
return ((DefaultBeanContext) context).getBeansOfType(resolutionContext, beanType, qualifier);
}
}
);
}
/**
* Obtains all bean definitions for a constructor 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 constructorInjectionPoint The constructor injection point
* @param argument The argument
* @return The resolved bean
*/
@SuppressWarnings("WeakerAccess")
@Internal
protected final Stream streamOfTypeForConstructorArgument(BeanResolutionContext resolutionContext, BeanContext context, @SuppressWarnings("unused") ConstructorInjectionPoint constructorInjectionPoint, Argument argument) {
return resolveBeanWithGenericsFromConstructorArgument(resolutionContext, argument, (beanType, qualifier) ->
((DefaultBeanContext) context).streamOfType(resolutionContext, beanType, qualifier)
);
}
/**
* Obtains all bean definitions for a constructor 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 constructorInjectionPoint The constructor injection point
* @param argument The argument
* @return The resolved bean
*/
@SuppressWarnings("WeakerAccess")
@Internal
protected final Optional findBeanForConstructorArgument(BeanResolutionContext resolutionContext, BeanContext context, @SuppressWarnings("unused") ConstructorInjectionPoint constructorInjectionPoint, Argument argument) {
return resolveBeanWithGenericsFromConstructorArgument(resolutionContext, argument, (beanType, qualifier) ->
((DefaultBeanContext) context).findBean(resolutionContext, beanType, qualifier)
);
}
/**
* Obtains a bean definition for the field 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 fieldIndex The field index
* @return The resolved bean
*/
@SuppressWarnings("unused")
@Internal
@UsedByGeneratedCode
protected final Object getBeanForField(BeanResolutionContext resolutionContext, BeanContext context, int fieldIndex) {
FieldInjectionPoint injectionPoint = fieldInjectionPoints.get(fieldIndex);
instrumentAnnotationMetadata(context, injectionPoint);
return getBeanForField(resolutionContext, context, injectionPoint);
}
/**
* Obtains a value for the given field from the bean context
*
* 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
* @return The resolved bean
*/
@SuppressWarnings("WeakerAccess")
@Internal
@UsedByGeneratedCode
protected final Object getValueForField(BeanResolutionContext resolutionContext, BeanContext context, int fieldIndex) {
FieldInjectionPoint injectionPoint = fieldInjectionPoints.get(fieldIndex);
BeanResolutionContext.Path path = resolutionContext.getPath();
path.pushFieldResolve(this, injectionPoint);
try {
if (context instanceof PropertyResolver) {
final AnnotationMetadata annotationMetadata = injectionPoint.getAnnotationMetadata();
String valueAnnVal = annotationMetadata.stringValue(Value.class).orElse(null);
Argument> fieldArgument = injectionPoint.asArgument();
Class argumentType;
boolean isCollection = false;
if (Collection.class.isAssignableFrom(injectionPoint.getType())) {
argumentType = fieldArgument.getFirstTypeVariable().map(Argument::getType).orElse((Class) Object.class);
isCollection = true;
} else {
argumentType = fieldArgument.getType();
}
if (isInnerConfiguration(argumentType)) {
Qualifier qualifier = resolveQualifier(resolutionContext, fieldArgument, true);
if (isCollection) {
Collection beans = ((DefaultBeanContext) context).getBeansOfType(resolutionContext, argumentType, qualifier);
return coerceCollectionToCorrectType(fieldArgument.getType(), beans);
} else {
return ((DefaultBeanContext) context).getBean(resolutionContext, argumentType, qualifier);
}
} else {
String valString = resolvePropertyValueName(resolutionContext, injectionPoint, valueAnnVal, annotationMetadata);
ArgumentConversionContext conversionContext = ConversionContext.of(fieldArgument);
Optional value = resolveValue((ApplicationContext) context, conversionContext, valueAnnVal != null, valString);
if (argumentType == Optional.class) {
return resolveOptionalObject(value);
} else {
if (value.isPresent()) {
return value.get();
} else {
if (fieldArgument.isDeclaredNullable()) {
return null;
}
throw new DependencyInjectionException(resolutionContext, injectionPoint, "Error resolving field value [" + valString + "]. Property doesn't exist or cannot be converted");
}
}
}
} else {
throw new DependencyInjectionException(resolutionContext, injectionPoint, "@Value requires a BeanContext that implements PropertyResolver");
}
} finally {
path.pop();
}
}
/**
* Resolve a value for the given field of the given type and path. Only
* used by applications compiled with versions of Micronaut prior to 1.2.0.
*
* @param resolutionContext The resolution context
* @param context The bean context
* @param propertyType The required property type
* @param propertyPath The property path
* @param The generic type
* @return An optional value
*/
@SuppressWarnings("unused")
@Internal
@UsedByGeneratedCode
protected final Optional getValueForPath(
BeanResolutionContext resolutionContext,
BeanContext context,
Argument propertyType,
String... propertyPath) {
if (context instanceof PropertyResolver) {
PropertyResolver propertyResolver = (PropertyResolver) context;
Class> beanType = getBeanType();
String pathString = propertyPath.length > 1 ? String.join(".", propertyPath) : propertyPath[0];
String valString = resolvePropertyPath(resolutionContext, pathString);
return propertyResolver.getProperty(valString, ConversionContext.of(propertyType));
}
return Optional.empty();
}
/**
* 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 The generic type
* @return An optional value
*/
@SuppressWarnings("unused")
@Internal
@UsedByGeneratedCode
protected final Optional getValueForPath(
BeanResolutionContext resolutionContext,
BeanContext context,
Argument propertyType,
String propertyPath) {
if (context instanceof PropertyResolver) {
PropertyResolver propertyResolver = (PropertyResolver) context;
Class> beanType = getBeanType();
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
* @return True if it does
*/
@Internal
@UsedByGeneratedCode
protected final boolean containsValueForField(BeanResolutionContext resolutionContext, BeanContext context, int fieldIndex) {
if (context instanceof ApplicationContext) {
FieldInjectionPoint injectionPoint = fieldInjectionPoints.get(fieldIndex);
final AnnotationMetadata annotationMetadata = injectionPoint.getAnnotationMetadata();
String valueAnnVal = annotationMetadata.stringValue(Value.class).orElse(null);
String valString = resolvePropertyValueName(resolutionContext, injectionPoint, valueAnnVal, annotationMetadata);
ApplicationContext applicationContext = (ApplicationContext) context;
Class fieldType = injectionPoint.getType();
boolean isConfigProps = fieldType.isAnnotationPresent(ConfigurationProperties.class);
boolean result = isConfigProps || Map.class.isAssignableFrom(fieldType) || Collection.class.isAssignableFrom(fieldType) ? applicationContext.containsProperties(valString) : applicationContext.containsProperty(valString);
if (!result && isConfigurationProperties()) {
String cliOption = resolveCliOption(injectionPoint.getName());
if (cliOption != null) {
return applicationContext.containsProperty(cliOption);
}
}
return result;
}
return false;
}
/**
* 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
*/
@SuppressWarnings("unused")
@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
*/
@SuppressWarnings({"WeakerAccess", "SameParameterValue"})
@Internal
@UsedByGeneratedCode
protected final boolean containsProperties(@SuppressWarnings("unused") BeanResolutionContext resolutionContext, BeanContext context, String subProperty) {
boolean isSubProperty = StringUtils.isNotEmpty(subProperty);
if (!isSubProperty && !requiredComponents.isEmpty()) {
// if the bean requires dependency injection we disable this optimization
return true;
}
if (isConfigurationProperties && context instanceof ApplicationContext) {
AnnotationMetadata annotationMetadata = getAnnotationMetadata();
ApplicationContext appCtx = (ApplicationContext) context;
if (annotationMetadata.getValue(ConfigurationProperties.class, "cliPrefix").isPresent()) {
return true;
} else {
String path = getConfigurationPropertiesPath(resolutionContext);
return appCtx.containsProperties(path);
}
}
return false;
}
/**
* Resolves a bean for the given {@link FieldInjectionPoint}.
*
* @param resolutionContext The {@link BeanResolutionContext}
* @param context The {@link BeanContext}
* @param injectionPoint The {@link FieldInjectionPoint}
* @return The resolved bean
* @throws DependencyInjectionException If the bean cannot be resolved
*/
@SuppressWarnings("WeakerAccess")
@Internal
@UsedByGeneratedCode
protected final Object getBeanForField(BeanResolutionContext resolutionContext, BeanContext context, FieldInjectionPoint injectionPoint) {
Class beanType = injectionPoint.getType();
if (beanType.isArray()) {
Collection beansOfType = getBeansOfTypeForField(resolutionContext, context, injectionPoint);
return beansOfType.toArray((Object[]) Array.newInstance(beanType.getComponentType(), beansOfType.size()));
} else if (Collection.class.isAssignableFrom(beanType)) {
Collection beansOfType = getBeansOfTypeForField(resolutionContext, context, injectionPoint);
if (beanType.isInstance(beansOfType)) {
return beansOfType;
} else {
//noinspection unchecked
return CollectionUtils.convertCollection(beanType, beansOfType).orElse(null);
}
} else if (Stream.class.isAssignableFrom(beanType)) {
return getStreamOfTypeForField(resolutionContext, context, injectionPoint);
} else if (Provider.class.isAssignableFrom(beanType)) {
return getBeanProviderForField(resolutionContext, context, injectionPoint);
} else if (Optional.class.isAssignableFrom(beanType)) {
return findBeanForField(resolutionContext, context, injectionPoint);
} else {
BeanResolutionContext.Path path = resolutionContext.getPath();
path.pushFieldResolve(this, injectionPoint);
try {
Qualifier qualifier = resolveQualifier(resolutionContext, injectionPoint.asArgument());
@SuppressWarnings("unchecked") Object bean = ((DefaultBeanContext) context).getBean(resolutionContext, beanType, qualifier);
path.pop();
return bean;
} catch (NoSuchBeanException e) {
if (injectionPoint.isDeclaredNullable()) {
path.pop();
return null;
}
throw new DependencyInjectionException(resolutionContext, injectionPoint, e);
}
}
}
/**
* Obtains a bean definition for the field 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 injectionPoint The field injection point
* @return The resolved bean
*/
@SuppressWarnings("WeakerAccess")
@Internal
protected final Provider getBeanProviderForField(BeanResolutionContext resolutionContext, BeanContext context, FieldInjectionPoint injectionPoint) {
return resolveBeanWithGenericsForField(resolutionContext, injectionPoint, (beanType, qualifier) ->
((DefaultBeanContext) context).getBeanProvider(resolutionContext, beanType, qualifier)
);
}
/**
* Obtains a an optional for the field 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 injectionPoint The field injection point
* @return The resolved bean
*/
@SuppressWarnings("WeakerAccess")
@Internal
protected final Optional findBeanForField(BeanResolutionContext resolutionContext, BeanContext context, FieldInjectionPoint injectionPoint) {
return resolveBeanWithGenericsForField(resolutionContext, injectionPoint, (beanType, qualifier) ->
((DefaultBeanContext) context).findBean(resolutionContext, beanType, qualifier)
);
}
/**
* Obtains a bean definition for the field 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 injectionPoint The field injection point
* @return The resolved bean
*/
@SuppressWarnings("WeakerAccess")
@Internal
protected final Collection getBeansOfTypeForField(BeanResolutionContext resolutionContext, BeanContext context, FieldInjectionPoint injectionPoint) {
return resolveBeanWithGenericsForField(resolutionContext, injectionPoint, (beanType, qualifier) -> {
boolean hasNoGenerics = !injectionPoint.getType().isArray() && injectionPoint.asArgument().getTypeVariables().isEmpty();
if (hasNoGenerics) {
return ((DefaultBeanContext) context).getBean(resolutionContext, beanType, qualifier);
} else {
return ((DefaultBeanContext) context).getBeansOfType(resolutionContext, beanType, qualifier);
}
}
);
}
/**
* Obtains a bean definition for the field 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 injectionPoint The field injection point
* @return The resolved bean
*/
@SuppressWarnings("WeakerAccess")
@Internal
protected final Stream getStreamOfTypeForField(BeanResolutionContext resolutionContext, BeanContext context, FieldInjectionPoint injectionPoint) {
return resolveBeanWithGenericsForField(resolutionContext, injectionPoint, (beanType, qualifier) ->
((DefaultBeanContext) context).streamOfType(resolutionContext, beanType, qualifier)
);
}
/**
* A method that subclasses can override to provide information on type arguments.
*
* @return The type arguments
*/
@Internal
protected Map[]> getTypeArgumentsMap() {
return Collections.emptyMap();
}
/**
* Resolves the annotation metadata for this bean. Subclasses
*
* @return The {@link AnnotationMetadata}
*/
protected AnnotationMetadata resolveAnnotationMetadata() {
return AnnotationMetadata.EMPTY_METADATA;
}
private AnnotationMetadata initializeAnnotationMetadata() {
AnnotationMetadata annotationMetadata = resolveAnnotationMetadata();
if (annotationMetadata != AnnotationMetadata.EMPTY_METADATA) {
// we make a copy of the result of annotation metadata which is normally a reference
// to the class metadata
return new BeanAnnotationMetadata(annotationMetadata);
} else {
return AnnotationMetadata.EMPTY_METADATA;
}
}
private AbstractBeanDefinition addInjectionPointInternal(
Class declaringType,
String method,
@Nullable Argument[] arguments,
@Nullable AnnotationMetadata annotationMetadata,
boolean requiresReflection,
List targetInjectionPoints) {
boolean isPreDestroy = targetInjectionPoints == this.preDestroyMethods;
boolean isPostConstruct = targetInjectionPoints == this.postConstructMethods;
MethodInjectionPoint injectionPoint;
if (requiresReflection) {
injectionPoint = new ReflectionMethodInjectionPoint(
this,
declaringType,
method,
arguments,
annotationMetadata
);
} else {
injectionPoint = new DefaultMethodInjectionPoint(
this,
declaringType,
method,
arguments,
annotationMetadata
);
}
targetInjectionPoints.add(injectionPoint);
if (isPostConstruct || isPreDestroy) {
this.methodInjectionPoints.add(injectionPoint);
}
addRequiredComponents(arguments);
return this;
}
private Object getBeanForMethodArgument(BeanResolutionContext resolutionContext, BeanContext context, MethodInjectionPoint injectionPoint, Argument argument) {
Class argumentType = argument.getType();
if (argumentType.isArray()) {
Collection beansOfType = getBeansOfTypeForMethodArgument(resolutionContext, context, injectionPoint, argument);
return beansOfType.toArray((Object[]) Array.newInstance(argumentType.getComponentType(), beansOfType.size()));
} else if (Collection.class.isAssignableFrom(argumentType)) {
Collection beansOfType = getBeansOfTypeForMethodArgument(resolutionContext, context, injectionPoint, argument);
return coerceCollectionToCorrectType(argumentType, beansOfType);
} else if (Stream.class.isAssignableFrom(argumentType)) {
return streamOfTypeForMethodArgument(resolutionContext, context, injectionPoint, argument);
} else if (Provider.class.isAssignableFrom(argumentType)) {
return getBeanProviderForMethodArgument(resolutionContext, context, injectionPoint, argument);
} else if (Optional.class.isAssignableFrom(argumentType)) {
return findBeanForMethodArgument(resolutionContext, context, injectionPoint, argument);
} else {
BeanResolutionContext.Path path = resolutionContext.getPath();
path.pushMethodArgumentResolve(this, injectionPoint, argument);
try {
Qualifier qualifier = resolveQualifier(resolutionContext, argument);
@SuppressWarnings("unchecked")
Object bean = ((DefaultBeanContext) context).getBean(resolutionContext, argumentType, qualifier);
path.pop();
return bean;
} catch (NoSuchBeanException e) {
if (argument.isDeclaredNullable()) {
path.pop();
return null;
}
throw new DependencyInjectionException(resolutionContext, injectionPoint, argument, e);
}
}
}
private Optional resolveValue(
ApplicationContext context,
ArgumentConversionContext> argument,
boolean hasValueAnnotation,
String valString) {
if (hasValueAnnotation) {
return context.resolvePlaceholders(valString).flatMap(v ->
context.getConversionService().convert(v, argument)
);
} else {
Optional> value = context.getProperty(valString, argument);
if (!value.isPresent() && isConfigurationProperties()) {
String cliOption = resolveCliOption(argument.getArgument().getName());
if (cliOption != null) {
return context.getProperty(cliOption, argument);
}
}
return value;
}
}
private String resolvePropertyValueName(
BeanResolutionContext resolutionContext,
AnnotationMetadata annotationMetadata,
Argument argument,
String valueAnnStr) {
String valString;
if (valueAnnStr != null) {
valString = valueAnnStr;
} else {
valString = annotationMetadata.stringValue(Property.class, "name")
.orElseGet(() ->
argument.getAnnotationMetadata().stringValue(Property.class, "name")
.orElseThrow(() ->
new DependencyInjectionException(
resolutionContext,
argument,
"Value resolution attempted but @Value annotation is missing"
)
)
);
valString = substituteWildCards(resolutionContext, valString);
}
return valString;
}
private String resolvePropertyValueName(
BeanResolutionContext resolutionContext,
FieldInjectionPoint injectionPoint,
String valueAnn,
AnnotationMetadata annotationMetadata) {
String valString;
if (valueAnn != null) {
valString = valueAnn;
} else {
valString = annotationMetadata.stringValue(Property.class, "name")
.orElseThrow(() -> new DependencyInjectionException(resolutionContext, injectionPoint, "Value resolution attempted but @Value annotation is missing"));
valString = substituteWildCards(resolutionContext, valString);
}
return valString;
}
private String resolvePropertyPath(
BeanResolutionContext resolutionContext,
String path) {
String valString = getConfigurationPropertiesPath(resolutionContext);
return valString + "." + path;
}
private String getConfigurationPropertiesPath(BeanResolutionContext resolutionContext) {
String valString = getAnnotationMetadata()
.stringValue(ConfigurationReader.class, "prefix")
.orElseThrow(() -> new IllegalStateException("Resolve property path called for non @ConfigurationProperties bean"));
valString = substituteWildCards(
resolutionContext,
valString
);
return valString;
}
private String substituteWildCards(BeanResolutionContext resolutionContext, String valString) {
if (valString.indexOf('*') > -1) {
Optional namedBean = resolutionContext.get(Named.class.getName(), ArgumentConversionContext.STRING);
if (namedBean.isPresent()) {
valString = valString.replace("*", namedBean.get());
}
}
return valString;
}
private String resolveCliOption(String name) {
String attr = "cliPrefix";
AnnotationMetadata annotationMetadata = getAnnotationMetadata();
if (annotationMetadata.isPresent(ConfigurationProperties.class, attr)) {
return annotationMetadata.stringValue(ConfigurationProperties.class, attr).map(val -> val + name).orElse(null);
}
return null;
}
private boolean isInnerConfiguration(Class argumentType) {
return isConfigurationProperties &&
argumentType.getName().indexOf('$') > -1 &&
!argumentType.isEnum() &&
!argumentType.isPrimitive() &&
Modifier.isPublic(argumentType.getModifiers()) && Modifier.isStatic(argumentType.getModifiers()) &&
isInnerOfAnySuperclass(argumentType);
}
private boolean isInnerOfAnySuperclass(Class argumentType) {
Class beanType = getBeanType();
while (beanType != null) {
if ((beanType.getName() + "$" + argumentType.getSimpleName()).equals(argumentType.getName())) {
return true;
}
beanType = beanType.getSuperclass();
}
return false;
}
private B resolveBeanWithGenericsFromMethodArgument(BeanResolutionContext resolutionContext, MethodInjectionPoint injectionPoint, Argument argument, BeanResolver beanResolver) throws X {
BeanResolutionContext.Path path = resolutionContext.getPath();
path.pushMethodArgumentResolve(this, injectionPoint, argument);
try {
Qualifier qualifier = resolveQualifier(resolutionContext, argument);
Class argumentType = argument.getType();
Class genericType = resolveGenericType(argument, argumentType);
@SuppressWarnings("unchecked") B bean = (B) beanResolver.resolveBean(genericType != null ? genericType : argumentType, qualifier);
path.pop();
return bean;
} catch (NoSuchBeanException e) {
throw new DependencyInjectionException(resolutionContext, injectionPoint, argument, e);
}
}
private Class resolveGenericType(Argument argument, Class argumentType) {
Class genericType;
if (argumentType.isArray()) {
genericType = argumentType.getComponentType();
} else {
Map> genericTypes = argument.getTypeVariables();
if (genericTypes.size() == 1) {
genericType = genericTypes.values().iterator().next().getType();
} else {
genericType = null;
}
}
return genericType;
}
private B resolveBeanWithGenericsFromConstructorArgument(BeanResolutionContext resolutionContext, Argument argument, BeanResolver beanResolver) {
BeanResolutionContext.Path path = resolutionContext.getPath();
path.pushConstructorResolve(this, argument);
try {
Class argumentType = argument.getType();
Class genericType = resolveGenericType(argument, argumentType);
Qualifier qualifier = resolveQualifier(resolutionContext, argument);
@SuppressWarnings("unchecked") B bean = (B) beanResolver.resolveBean(genericType != null ? genericType : argumentType, qualifier);
path.pop();
return bean;
} catch (NoSuchBeanException e) {
if (argument.isNullable()) {
path.pop();
return null;
}
throw new DependencyInjectionException(resolutionContext, argument, e);
}
}
private B resolveBeanWithGenericsForField(BeanResolutionContext resolutionContext, FieldInjectionPoint injectionPoint, BeanResolver beanResolver) {
BeanResolutionContext.Path path = resolutionContext.getPath();
path.pushFieldResolve(this, injectionPoint);
Argument argument = injectionPoint.asArgument();
try {
Optional genericType = injectionPoint.getType().isArray() ? Optional.of(injectionPoint.getType().getComponentType()) : argument.getFirstTypeVariable().map(Argument::getType);
Qualifier qualifier = resolveQualifier(resolutionContext, injectionPoint.asArgument());
@SuppressWarnings("unchecked") B bean = (B) beanResolver.resolveBean(genericType.orElse(injectionPoint.getType()), qualifier);
path.pop();
return bean;
} catch (NoSuchBeanException e) {
if (argument.isNullable()) {
path.pop();
return null;
}
throw new DependencyInjectionException(resolutionContext, injectionPoint, e);
}
}
private boolean isConfigurationProperties() {
return isConfigurationProperties;
}
private Qualifier resolveQualifier(BeanResolutionContext resolutionContext, Argument argument) {
return resolveQualifier(resolutionContext, argument, false);
}
private Qualifier resolveQualifier(
BeanResolutionContext resolutionContext,
Argument argument,
boolean innerConfiguration) {
AnnotationMetadata annotationMetadata = argument.getAnnotationMetadata();
boolean hasMetadata = annotationMetadata != AnnotationMetadata.EMPTY_METADATA;
Class extends Annotation> qualifierType = hasMetadata ? annotationMetadata.getAnnotationTypeByStereotype(javax.inject.Qualifier.class).orElse(null) : null;
if (qualifierType != null) {
return Qualifiers.byAnnotation(
annotationMetadata,
qualifierType
);
} else {
Class>[] byType = hasMetadata ? annotationMetadata.hasDeclaredAnnotation(Type.class) ? annotationMetadata.classValues(Type.class) : null : null;
if (byType != null) {
return Qualifiers.byType(byType);
} else {
Qualifier qualifier = null;
boolean isIterable = isIterable() || resolutionContext.get(EachProperty.class.getName(), Class.class).map(getBeanType()::equals).orElse(false);
if (isIterable) {
Optional optional = resolutionContext.get(javax.inject.Qualifier.class.getName(), Map.class)
.map(map -> (Qualifier) map.get(argument));
qualifier = optional.orElse(null);
}
if (qualifier == null) {
if ((hasMetadata && argument.isAnnotationPresent(Parameter.class)) ||
(innerConfiguration && isIterable) ||
Qualifier.class == argument.getType()) {
final Optional n = resolutionContext.get(NAMED_ATTRIBUTE, ArgumentConversionContext.STRING);
qualifier = n.map(Qualifiers::byName).orElse(null);
}
}
return qualifier;
}
}
}
@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
private Object resolveOptionalObject(Optional value) {
if (!value.isPresent()) {
return value;
} else {
Object convertedOptional = value.get();
if (convertedOptional instanceof Optional) {
return convertedOptional;
} else {
return value;
}
}
}
@SuppressWarnings("unchecked")
private Object coerceCollectionToCorrectType(Class collectionType, Collection beansOfType) {
if (collectionType.isInstance(beansOfType)) {
return beansOfType;
} else {
return CollectionUtils.convertCollection(collectionType, beansOfType).orElse(null);
}
}
private void addRequiredComponents(Argument... arguments) {
if (arguments != null) {
for (Argument argument : arguments) {
requiredComponents.add(argument.getType());
}
}
}
private void instrumentAnnotationMetadata(BeanContext context, Object object) {
if (object instanceof EnvironmentConfigurable && context instanceof ApplicationContext) {
((EnvironmentConfigurable) object).configure(((ApplicationContext) context).getEnvironment());
}
}
/**
* Internal environment aware annotation metadata delegate.
*/
private final class BeanAnnotationMetadata extends AbstractEnvironmentAnnotationMetadata {
BeanAnnotationMetadata(AnnotationMetadata targetMetadata) {
super(targetMetadata);
}
@Nullable
@Override
protected Environment getEnvironment() {
return environment;
}
}
/**
* Class used as a method key.
*/
private final class MethodKey {
final String name;
final Class[] argumentTypes;
MethodKey(String name, Class[] argumentTypes) {
this.name = name;
this.argumentTypes = argumentTypes;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
@SuppressWarnings("unchecked") MethodKey methodKey = (MethodKey) o;
if (!name.equals(methodKey.name)) {
return false;
}
return Arrays.equals(argumentTypes, methodKey.argumentTypes);
}
@Override
public int hashCode() {
int result = name.hashCode();
result = 31 * result + Arrays.hashCode(argumentTypes);
return result;
}
}
/**
* Bean resolver.
*
* @param The type
*/
private interface BeanResolver {
T resolveBean(Class beanType, Qualifier qualifier);
}
}