BeanDefinitionWriter.java
/*
* Copyright 2017-2024 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.inject.writer;
import io.micronaut.aop.chain.ConstructorInterceptorChain;
import io.micronaut.aop.chain.MethodInterceptorChain;
import io.micronaut.aop.writer.AopProxyWriter;
import io.micronaut.context.AbstractBeanDefinitionBeanConstructor;
import io.micronaut.context.AbstractExecutableMethod;
import io.micronaut.context.AbstractInitializableBeanDefinition;
import io.micronaut.context.AbstractInitializableBeanDefinitionAndReference;
import io.micronaut.context.BeanContext;
import io.micronaut.context.BeanRegistration;
import io.micronaut.context.BeanResolutionContext;
import io.micronaut.context.DefaultBeanContext;
import io.micronaut.context.Qualifier;
import io.micronaut.context.RequiresCondition;
import io.micronaut.context.annotation.Any;
import io.micronaut.context.annotation.Bean;
import io.micronaut.context.annotation.ConfigurationBuilder;
import io.micronaut.context.annotation.ConfigurationProperties;
import io.micronaut.context.annotation.ConfigurationReader;
import io.micronaut.context.annotation.Context;
import io.micronaut.context.annotation.DefaultScope;
import io.micronaut.context.annotation.EachBean;
import io.micronaut.context.annotation.EachProperty;
import io.micronaut.context.annotation.InjectScope;
import io.micronaut.context.annotation.Parameter;
import io.micronaut.context.annotation.Primary;
import io.micronaut.context.annotation.Property;
import io.micronaut.context.annotation.PropertySource;
import io.micronaut.context.annotation.Requirements;
import io.micronaut.context.annotation.Requires;
import io.micronaut.context.annotation.Value;
import io.micronaut.context.condition.Condition;
import io.micronaut.context.conditions.MatchesAbsenceOfBeansCondition;
import io.micronaut.context.conditions.MatchesAbsenceOfClassNamesCondition;
import io.micronaut.context.conditions.MatchesAbsenceOfClassesCondition;
import io.micronaut.context.conditions.MatchesConditionUtils;
import io.micronaut.context.conditions.MatchesConfigurationCondition;
import io.micronaut.context.conditions.MatchesCurrentNotOsCondition;
import io.micronaut.context.conditions.MatchesCurrentOsCondition;
import io.micronaut.context.conditions.MatchesCustomCondition;
import io.micronaut.context.conditions.MatchesDynamicCondition;
import io.micronaut.context.conditions.MatchesEnvironmentCondition;
import io.micronaut.context.conditions.MatchesMissingPropertyCondition;
import io.micronaut.context.conditions.MatchesNotEnvironmentCondition;
import io.micronaut.context.conditions.MatchesPresenceOfBeansCondition;
import io.micronaut.context.conditions.MatchesPresenceOfClassesCondition;
import io.micronaut.context.conditions.MatchesPresenceOfEntitiesCondition;
import io.micronaut.context.conditions.MatchesPresenceOfResourcesCondition;
import io.micronaut.context.conditions.MatchesPropertyCondition;
import io.micronaut.context.conditions.MatchesSdkCondition;
import io.micronaut.context.env.ConfigurationPath;
import io.micronaut.core.annotation.AccessorsStyle;
import io.micronaut.core.annotation.AnnotationClassValue;
import io.micronaut.core.annotation.AnnotationMetadata;
import io.micronaut.core.annotation.AnnotationMetadataProvider;
import io.micronaut.core.annotation.AnnotationUtil;
import io.micronaut.core.annotation.AnnotationValue;
import io.micronaut.core.annotation.AnnotationValueBuilder;
import io.micronaut.core.annotation.Generated;
import io.micronaut.core.annotation.Internal;
import io.micronaut.core.annotation.NextMajorVersion;
import io.micronaut.core.annotation.NonNull;
import io.micronaut.core.annotation.Nullable;
import io.micronaut.core.beans.BeanConstructor;
import io.micronaut.core.convert.ConversionService;
import io.micronaut.core.convert.ConversionServiceProvider;
import io.micronaut.core.expressions.EvaluatedExpressionReference;
import io.micronaut.core.naming.NameUtils;
import io.micronaut.core.order.OrderUtil;
import io.micronaut.core.order.Ordered;
import io.micronaut.core.reflect.ReflectionUtils;
import io.micronaut.core.type.Argument;
import io.micronaut.core.type.DefaultArgument;
import io.micronaut.core.type.TypeVariableResolver;
import io.micronaut.core.util.ArrayUtils;
import io.micronaut.core.util.CollectionUtils;
import io.micronaut.core.util.StringUtils;
import io.micronaut.core.util.Toggleable;
import io.micronaut.inject.AdvisedBeanType;
import io.micronaut.inject.BeanContextConditional;
import io.micronaut.inject.BeanDefinition;
import io.micronaut.inject.BeanDefinitionReference;
import io.micronaut.inject.DisposableBeanDefinition;
import io.micronaut.inject.ExecutableMethod;
import io.micronaut.inject.ExecutableMethodsDefinition;
import io.micronaut.inject.InitializingBeanDefinition;
import io.micronaut.inject.InjectableBeanDefinition;
import io.micronaut.inject.InjectionPoint;
import io.micronaut.inject.InstantiatableBeanDefinition;
import io.micronaut.inject.ParametrizedInstantiatableBeanDefinition;
import io.micronaut.inject.ProxyBeanDefinition;
import io.micronaut.inject.ValidatedBeanDefinition;
import io.micronaut.inject.annotation.AbstractAnnotationMetadataBuilder;
import io.micronaut.inject.annotation.AnnotationMetadataGenUtils;
import io.micronaut.inject.annotation.AnnotationMetadataHierarchy;
import io.micronaut.inject.annotation.AnnotationMetadataReference;
import io.micronaut.inject.annotation.MutableAnnotationMetadata;
import io.micronaut.inject.ast.ClassElement;
import io.micronaut.inject.ast.ConstructorElement;
import io.micronaut.inject.ast.Element;
import io.micronaut.inject.ast.ElementQuery;
import io.micronaut.inject.ast.FieldElement;
import io.micronaut.inject.ast.MemberElement;
import io.micronaut.inject.ast.MethodElement;
import io.micronaut.inject.ast.ParameterElement;
import io.micronaut.inject.ast.PropertyElement;
import io.micronaut.inject.ast.TypedElement;
import io.micronaut.inject.ast.beans.BeanElement;
import io.micronaut.inject.ast.beans.BeanElementBuilder;
import io.micronaut.inject.configuration.ConfigurationMetadataBuilder;
import io.micronaut.inject.qualifiers.AnyQualifier;
import io.micronaut.inject.qualifiers.Qualifiers;
import io.micronaut.inject.visitor.BeanElementVisitor;
import io.micronaut.inject.visitor.BeanElementVisitorContext;
import io.micronaut.inject.visitor.VisitorContext;
import io.micronaut.sourcegen.bytecode.ByteCodeWriter;
import io.micronaut.sourcegen.model.AnnotationDef;
import io.micronaut.sourcegen.model.ClassDef;
import io.micronaut.sourcegen.model.ClassTypeDef;
import io.micronaut.sourcegen.model.ExpressionDef;
import io.micronaut.sourcegen.model.FieldDef;
import io.micronaut.sourcegen.model.MethodDef;
import io.micronaut.sourcegen.model.ObjectDef;
import io.micronaut.sourcegen.model.StatementDef;
import io.micronaut.sourcegen.model.TypeDef;
import io.micronaut.sourcegen.model.VariableDef;
import jakarta.inject.Singleton;
import javax.lang.model.element.Modifier;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.TreeMap;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.stream.IntStream;
import java.util.stream.Stream;
import static io.micronaut.core.util.StringUtils.EMPTY_STRING_ARRAY;
import static io.micronaut.inject.visitor.BeanElementVisitor.VISITORS;
/**
* <p>Responsible for building {@link BeanDefinition} instances at compile time. Uses ASM build the class definition.</p>
*
* <p>Should be used from AST frameworks to build bean definitions from source code data.</p>
*
* <p>For example:</p>
*
* <pre>
* {@code
*
* BeanDefinitionWriter writer = new BeanDefinitionWriter("my.package", "MyClass", "jakarta.inject.Singleton", true)
* writer.visitBeanDefinitionConstructor()
* writer.visitFieldInjectionPoint("my.Qualifier", false, "my.package.MyDependency", "myfield" )
* writer.visitBeanDefinitionEnd()
* writer.writeTo(new File(..))
* }
* </pre>
*
* @author Graeme Rocher
* @author Denis Stepanov
* @see BeanDefinition
* @since 1.0
*/
@Internal
public final class BeanDefinitionWriter implements ClassOutputWriter, BeanDefinitionVisitor, BeanElement, Toggleable {
@NextMajorVersion("Inline as true")
public static final String OMIT_CONFPROP_INJECTION_POINTS = "micronaut.processing.omit.confprop.injectpoints";
public static final String CLASS_SUFFIX = "$Definition";
private static final Constructor<AbstractBeanDefinitionBeanConstructor> CONSTRUCTOR_ABSTRACT_CONSTRUCTOR_IP = ReflectionUtils.findConstructor(
AbstractBeanDefinitionBeanConstructor.class,
BeanDefinition.class)
.orElseThrow(() -> new ClassGenerationException("Invalid version of Micronaut present on the class path"));
private static final Method POST_CONSTRUCT_METHOD = ReflectionUtils.getRequiredInternalMethod(AbstractInitializableBeanDefinition.class, "postConstruct", BeanResolutionContext.class, BeanContext.class, Object.class);
private static final Method INJECT_BEAN_METHOD =
ReflectionUtils.getRequiredInternalMethod(InjectableBeanDefinition.class, "inject", BeanResolutionContext.class, BeanContext.class, Object.class);
private static final Method PRE_DESTROY_METHOD = ReflectionUtils.getRequiredInternalMethod(AbstractInitializableBeanDefinition.class, "preDestroy", BeanResolutionContext.class, BeanContext.class, Object.class);
private static final Method GET_BEAN_FOR_CONSTRUCTOR_ARGUMENT = getBeanLookupMethod("getBeanForConstructorArgument", false);
private static final Method GET_BEAN_REGISTRATIONS_FOR_CONSTRUCTOR_ARGUMENT = getBeanLookupMethod("getBeanRegistrationsForConstructorArgument", true);
private static final Method GET_BEAN_REGISTRATION_FOR_CONSTRUCTOR_ARGUMENT = getBeanLookupMethod("getBeanRegistrationForConstructorArgument", true);
private static final Method GET_BEANS_OF_TYPE_FOR_CONSTRUCTOR_ARGUMENT = getBeanLookupMethod("getBeansOfTypeForConstructorArgument", true);
private static final Method GET_STREAM_OF_TYPE_FOR_CONSTRUCTOR_ARGUMENT = getBeanLookupMethod("getStreamOfTypeForConstructorArgument", true);
private static final Method GET_MAP_OF_TYPE_FOR_CONSTRUCTOR_ARGUMENT = getBeanLookupMethod("getMapOfTypeForConstructorArgument", true);
private static final Method FIND_BEAN_FOR_CONSTRUCTOR_ARGUMENT = getBeanLookupMethod("findBeanForConstructorArgument", true);
private static final Method GET_BEAN_FOR_FIELD = getBeanLookupMethod("getBeanForField", false);
private static final Method GET_BEAN_FOR_ANNOTATION = getBeanLookupMethod("getBeanForAnnotation", false);
private static final Method GET_BEAN_REGISTRATIONS_FOR_FIELD = getBeanLookupMethod("getBeanRegistrationsForField", true);
private static final Method GET_BEAN_REGISTRATION_FOR_FIELD = getBeanLookupMethod("getBeanRegistrationForField", true);
private static final Method GET_BEANS_OF_TYPE_FOR_FIELD = getBeanLookupMethod("getBeansOfTypeForField", true);
private static final Method GET_VALUE_FOR_FIELD = getBeanLookupMethod("getValueForField", false);
private static final Method GET_STREAM_OF_TYPE_FOR_FIELD = getBeanLookupMethod("getStreamOfTypeForField", true);
private static final Method GET_MAP_OF_TYPE_FOR_FIELD = getBeanLookupMethod("getMapOfTypeForField", true);
private static final Method FIND_BEAN_FOR_FIELD = getBeanLookupMethod("findBeanForField", true);
private static final Method GET_VALUE_FOR_PATH = ReflectionUtils.getRequiredInternalMethod(AbstractInitializableBeanDefinition.class, "getValueForPath", BeanResolutionContext.class, BeanContext.class, Argument.class, String.class);
private static final Method CONTAINS_PROPERTIES_METHOD = ReflectionUtils.getRequiredInternalMethod(AbstractInitializableBeanDefinition.class, "containsProperties", BeanResolutionContext.class, BeanContext.class);
private static final Method GET_BEAN_FOR_METHOD_ARGUMENT = getBeanLookupMethodForArgument("getBeanForMethodArgument", false);
private static final Method GET_BEAN_REGISTRATIONS_FOR_METHOD_ARGUMENT = getBeanLookupMethodForArgument("getBeanRegistrationsForMethodArgument", true);
private static final Method GET_BEAN_REGISTRATION_FOR_METHOD_ARGUMENT = getBeanLookupMethodForArgument("getBeanRegistrationForMethodArgument", true);
private static final Method GET_BEANS_OF_TYPE_FOR_METHOD_ARGUMENT = getBeanLookupMethodForArgument("getBeansOfTypeForMethodArgument", true);
private static final Method GET_STREAM_OF_TYPE_FOR_METHOD_ARGUMENT = getBeanLookupMethodForArgument("getStreamOfTypeForMethodArgument", true);
private static final Method GET_MAP_OF_TYPE_FOR_METHOD_ARGUMENT = getBeanLookupMethodForArgument("getMapOfTypeForMethodArgument", true);
private static final Method FIND_BEAN_FOR_METHOD_ARGUMENT = getBeanLookupMethodForArgument("findBeanForMethodArgument", true);
private static final Method CHECK_INJECTED_BEAN_PROPERTY_VALUE = ReflectionUtils.getRequiredInternalMethod(
AbstractInitializableBeanDefinition.class,
"checkInjectedBeanPropertyValue",
String.class,
Object.class,
String.class,
String.class);
private static final Method GET_PROPERTY_VALUE_FOR_METHOD_ARGUMENT = ReflectionUtils.getRequiredInternalMethod(
AbstractInitializableBeanDefinition.class,
"getPropertyValueForMethodArgument",
BeanResolutionContext.class,
BeanContext.class,
int.class,
int.class,
String.class,
String.class);
private static final Method GET_PROPERTY_PLACEHOLDER_VALUE_FOR_METHOD_ARGUMENT = ReflectionUtils.getRequiredInternalMethod(
AbstractInitializableBeanDefinition.class,
"getPropertyPlaceholderValueForMethodArgument",
BeanResolutionContext.class,
BeanContext.class,
int.class,
int.class,
String.class);
private static final Method GET_EVALUATED_EXPRESSION_VALUE_FOR_METHOD_ARGUMENT = ReflectionUtils.getRequiredInternalMethod(
AbstractInitializableBeanDefinition.class,
"getEvaluatedExpressionValueForMethodArgument",
int.class,
int.class);
private static final Method GET_BEAN_FOR_SETTER = ReflectionUtils.getRequiredInternalMethod(
AbstractInitializableBeanDefinition.class,
"getBeanForSetter",
BeanResolutionContext.class,
BeanContext.class,
String.class,
Argument.class,
Qualifier.class);
private static final Method GET_BEANS_OF_TYPE_FOR_SETTER = ReflectionUtils.getRequiredInternalMethod(
AbstractInitializableBeanDefinition.class,
"getBeansOfTypeForSetter",
BeanResolutionContext.class,
BeanContext.class,
String.class,
Argument.class,
Argument.class,
Qualifier.class);
private static final Method GET_PROPERTY_VALUE_FOR_SETTER = ReflectionUtils.getRequiredInternalMethod(
AbstractInitializableBeanDefinition.class,
"getPropertyValueForSetter",
BeanResolutionContext.class,
BeanContext.class,
String.class,
Argument.class,
String.class,
String.class);
private static final Method GET_PROPERTY_PLACEHOLDER_VALUE_FOR_SETTER = ReflectionUtils.getRequiredInternalMethod(
AbstractInitializableBeanDefinition.class,
"getPropertyPlaceholderValueForSetter",
BeanResolutionContext.class,
BeanContext.class,
String.class,
Argument.class,
String.class);
private static final Method GET_PROPERTY_VALUE_FOR_CONSTRUCTOR_ARGUMENT = ReflectionUtils.getRequiredInternalMethod(
AbstractInitializableBeanDefinition.class,
"getPropertyValueForConstructorArgument",
BeanResolutionContext.class,
BeanContext.class,
int.class,
String.class,
String.class);
private static final Method GET_PROPERTY_PLACEHOLDER_VALUE_FOR_CONSTRUCTOR_ARGUMENT = ReflectionUtils.getRequiredInternalMethod(
AbstractInitializableBeanDefinition.class,
"getPropertyPlaceholderValueForConstructorArgument",
BeanResolutionContext.class,
BeanContext.class,
int.class,
String.class);
private static final Method GET_EVALUATED_EXPRESSION_VALUE_FOR_CONSTRUCTOR_ARGUMENT = ReflectionUtils.getRequiredInternalMethod(
AbstractInitializableBeanDefinition.class,
"getEvaluatedExpressionValueForConstructorArgument",
int.class);
private static final Method GET_PROPERTY_VALUE_FOR_FIELD = ReflectionUtils.getRequiredInternalMethod(
AbstractInitializableBeanDefinition.class,
"getPropertyValueForField",
BeanResolutionContext.class,
BeanContext.class,
Argument.class,
String.class,
String.class);
private static final Method GET_PROPERTY_PLACEHOLDER_VALUE_FOR_FIELD = ReflectionUtils.getRequiredInternalMethod(
AbstractInitializableBeanDefinition.class,
"getPropertyPlaceholderValueForField",
BeanResolutionContext.class,
BeanContext.class,
Argument.class,
String.class);
private static final Method CONTAINS_PROPERTIES_VALUE_METHOD = ReflectionUtils.getRequiredInternalMethod(
AbstractInitializableBeanDefinition.class,
"containsPropertiesValue",
BeanResolutionContext.class,
BeanContext.class,
String.class);
private static final Method CONTAINS_PROPERTY_VALUE_METHOD = ReflectionUtils.getRequiredInternalMethod(
AbstractInitializableBeanDefinition.class,
"containsPropertyValue",
BeanResolutionContext.class,
BeanContext.class,
String.class);
private static final ClassTypeDef TYPE_ABSTRACT_BEAN_DEFINITION_AND_REFERENCE = ClassTypeDef.of(AbstractInitializableBeanDefinitionAndReference.class);
private static final Method METHOD_OPTIONAL_EMPTY = ReflectionUtils.getRequiredMethod(Optional.class, "empty");
private static final ClassTypeDef TYPE_OPTIONAL = ClassTypeDef.of(Optional.class);
private static final Method METHOD_OPTIONAL_OF = ReflectionUtils.getRequiredMethod(Optional.class, "of", Object.class);
private static final String METHOD_NAME_INSTANTIATE = "instantiate";
private static final Method METHOD_BEAN_CONSTRUCTOR_INSTANTIATE = ReflectionUtils.getRequiredMethod(
BeanConstructor.class,
METHOD_NAME_INSTANTIATE,
Object[].class
);
private static final Method METHOD_DESCRIPTOR_CONSTRUCTOR_INSTANTIATE = ReflectionUtils.getRequiredMethod(ConstructorInterceptorChain.class, METHOD_NAME_INSTANTIATE,
BeanResolutionContext.class,
BeanContext.class,
List.class,
BeanDefinition.class,
BeanConstructor.class,
int.class,
Object[].class
);
private static final Method METHOD_GET_BEAN = ReflectionUtils.getRequiredInternalMethod(DefaultBeanContext.class, "getBean", BeanResolutionContext.class, Class.class, Qualifier.class);
private static final Method COLLECTION_TO_ARRAY = ReflectionUtils.getRequiredInternalMethod(Collection.class, "toArray", Object[].class);
private static final Method DISPOSE_INTERCEPTOR_METHOD =
ReflectionUtils.getRequiredInternalMethod(MethodInterceptorChain.class, "dispose",
BeanResolutionContext.class,
BeanContext.class,
BeanDefinition.class,
ExecutableMethod.class,
Object.class);
private static final Method INITIALIZE_INTERCEPTOR_METHOD =
ReflectionUtils.getRequiredInternalMethod(MethodInterceptorChain.class, "initialize",
BeanResolutionContext.class,
BeanContext.class,
BeanDefinition.class,
ExecutableMethod.class,
Object.class);
private static final Method SET_FIELD_WITH_REFLECTION_METHOD =
ReflectionUtils.getRequiredMethod(AbstractInitializableBeanDefinition.class, "setFieldWithReflection", BeanResolutionContext.class, BeanContext.class, int.class, Object.class, Object.class);
private static final Method INVOKE_WITH_REFLECTION_METHOD =
ReflectionUtils.getRequiredMethod(AbstractInitializableBeanDefinition.class, "invokeMethodWithReflection", BeanResolutionContext.class, BeanContext.class, int.class, Object.class, Object[].class);
private static final Method IS_METHOD_RESOLVED =
ReflectionUtils.getRequiredMethod(AbstractInitializableBeanDefinition.class, "isMethodResolved", int.class, Object[].class);
private static final ClassTypeDef TYPE_REFLECTION_UTILS = ClassTypeDef.of(ReflectionUtils.class);
private static final Method GET_FIELD_WITH_REFLECTION_METHOD =
ReflectionUtils.getRequiredInternalMethod(ReflectionUtils.class, "getField", Class.class, String.class, Object.class);
private static final Method METHOD_INVOKE_INACCESSIBLE_METHOD =
ReflectionUtils.getRequiredInternalMethod(ReflectionUtils.class, "invokeInaccessibleMethod", Object.class, Method.class, Object[].class);
private static final Optional<Constructor<AbstractInitializableBeanDefinitionAndReference>> BEAN_DEFINITION_CLASS_CONSTRUCTOR1 = ReflectionUtils.findConstructor(
AbstractInitializableBeanDefinitionAndReference.class,
Class.class, // beanType
AbstractInitializableBeanDefinition.MethodOrFieldReference.class, // constructor
AnnotationMetadata.class, // annotationMetadata
AbstractInitializableBeanDefinition.MethodReference[].class, // methodInjection
AbstractInitializableBeanDefinition.FieldReference[].class, // fieldInjection
AbstractInitializableBeanDefinition.AnnotationReference[].class, // annotationInjection
ExecutableMethodsDefinition.class, // executableMethodsDefinition
Map.class, // typeArgumentsMap
AbstractInitializableBeanDefinition.PrecalculatedInfo.class // precalculated info
);
private static final Optional<Constructor<AbstractInitializableBeanDefinitionAndReference>> BEAN_DEFINITION_CLASS_CONSTRUCTOR2 = ReflectionUtils.findConstructor(
AbstractInitializableBeanDefinitionAndReference.class,
Class.class, // beanType
AbstractInitializableBeanDefinition.MethodOrFieldReference.class, // constructor
AnnotationMetadata.class, // annotationMetadata
AbstractInitializableBeanDefinition.MethodReference[].class, // methodInjection
AbstractInitializableBeanDefinition.FieldReference[].class, // fieldInjection
AbstractInitializableBeanDefinition.AnnotationReference[].class, // annotationInjection
ExecutableMethodsDefinition.class, // executableMethodsDefinition
Map.class, // typeArgumentsMap
AbstractInitializableBeanDefinition.PrecalculatedInfo.class, // precalculated info
Condition[].class, // pre conditions
Condition[].class, // post conditions
Throwable.class // failed initialization
);
private static final Constructor<?> PRECALCULATED_INFO_CONSTRUCTOR = ReflectionUtils.getRequiredInternalConstructor(AbstractInitializableBeanDefinition.PrecalculatedInfo.class,
Optional.class, // scope
boolean.class, // isAbstract
boolean.class, // isIterable
boolean.class, // isSingleton
boolean.class, // isPrimary
boolean.class, // isConfigurationProperties
boolean.class, // isContainerType
boolean.class, // requiresMethodProcessing,
boolean.class // hasEvaluatedExpressions
);
private static final String FIELD_CONSTRUCTOR = "$CONSTRUCTOR";
private static final String FIELD_EXECUTABLE_METHODS = "$EXEC";
private static final String FIELD_INJECTION_METHODS = "$INJECTION_METHODS";
private static final String FIELD_INJECTION_FIELDS = "$INJECTION_FIELDS";
private static final String FIELD_ANNOTATION_INJECTIONS = "$ANNOTATION_INJECTIONS";
private static final String FIELD_TYPE_ARGUMENTS = "$TYPE_ARGUMENTS";
private static final String FIELD_INNER_CLASSES = "$INNER_CONFIGURATION_CLASSES";
private static final String FIELD_EXPOSED_TYPES = "$EXPOSED_TYPES";
private static final String FIELD_FAILED_INITIALIZATION = "$FAILURE";
private static final String FIELD_PRECALCULATED_INFO = "$INFO";
private static final String FIELD_PRE_START_CONDITIONS = "$PRE_CONDITIONS";
private static final String FIELD_POST_START_CONDITIONS = "$POST_CONDITIONS";
private static final Constructor<?> METHOD_REFERENCE_CONSTRUCTOR = ReflectionUtils.getRequiredInternalConstructor(AbstractInitializableBeanDefinition.MethodReference.class,
Class.class, // declaringType,
String.class, // methodName
Argument[].class, // arguments
AnnotationMetadata.class// annotationMetadata
);
private static final Constructor<?> METHOD_REFERENCE_CONSTRUCTOR_POST_PRE = ReflectionUtils.getRequiredInternalConstructor(AbstractInitializableBeanDefinition.MethodReference.class,
Class.class, // declaringType,
String.class, // methodName
Argument[].class, // arguments
AnnotationMetadata.class, // annotationMetadata
boolean.class, // isPostConstructMethod
boolean.class // isPreDestroyMethod,
);
private static final Constructor<?> FIELD_REFERENCE_CONSTRUCTOR = ReflectionUtils.getRequiredInternalConstructor(AbstractInitializableBeanDefinition.FieldReference.class, Class.class, Argument.class);
private static final Constructor<?> ANNOTATION_REFERENCE_CONSTRUCTOR = ReflectionUtils.getRequiredInternalConstructor(AbstractInitializableBeanDefinition.AnnotationReference.class, Argument.class);
private static final Method METHOD_QUALIFIER_FOR_ARGUMENT =
ReflectionUtils.getRequiredMethod(Qualifiers.class, "forArgument", Argument.class);
private static final Method METHOD_QUALIFIER_BY_NAME = ReflectionUtils.getRequiredMethod(Qualifiers.class, "byName", String.class);
private static final Method METHOD_QUALIFIER_BY_ANNOTATION =
ReflectionUtils.getRequiredMethod(Qualifiers.class, "byAnnotationSimple", AnnotationMetadata.class, String.class);
private static final Method METHOD_QUALIFIER_BY_REPEATABLE_ANNOTATION =
ReflectionUtils.getRequiredMethod(Qualifiers.class, "byRepeatableAnnotation", AnnotationMetadata.class, String.class);
private static final Method METHOD_QUALIFIER_BY_QUALIFIERS =
ReflectionUtils.getRequiredMethod(Qualifiers.class, "byQualifiers", Qualifier[].class);
private static final Method METHOD_QUALIFIER_BY_INTERCEPTOR_BINDING =
ReflectionUtils.getRequiredMethod(Qualifiers.class, "byInterceptorBinding", AnnotationMetadata.class);
private static final Method METHOD_QUALIFIER_BY_TYPE = ReflectionUtils.getRequiredMethod(Qualifiers.class, "byType", Class[].class);
private static final Method METHOD_BEAN_RESOLUTION_CONTEXT_MARK_FACTORY = ReflectionUtils.getRequiredMethod(BeanResolutionContext.class, "markDependentAsFactory");
private static final Method METHOD_PROXY_TARGET_TYPE = ReflectionUtils.getRequiredInternalMethod(ProxyBeanDefinition.class, "getTargetDefinitionType");
private static final Method METHOD_PROXY_TARGET_CLASS = ReflectionUtils.getRequiredInternalMethod(ProxyBeanDefinition.class, "getTargetType");
private static final ClassTypeDef TYPE_QUALIFIERS = ClassTypeDef.of(Qualifiers.class);
private static final ClassTypeDef TYPE_QUALIFIER = ClassTypeDef.of(Qualifier.class);
private static final String MESSAGE_ONLY_SINGLE_CALL_PERMITTED = "Only a single call to visitBeanFactoryMethod(..) is permitted";
private static final int INJECT_METHOD_BEAN_RESOLUTION_CONTEXT_PARAM = 0;
private static final int INJECT_METHOD_BEAN_CONTEXT_PARAM = 1;
private static final int INSTANTIATE_METHOD_BEAN_RESOLUTION_CONTEXT_PARAM = 0;
private static final int INSTANTIATE_METHOD_BEAN_CONTEXT_PARAM = 1;
private static final Method METHOD_BEAN_CONTEXT_GET_CONVERSION_SERVICE = ReflectionUtils.getRequiredMethod(ConversionServiceProvider.class, "getConversionService");
private static final Method METHOD_INVOKE_INTERNAL =
ReflectionUtils.getRequiredInternalMethod(AbstractExecutableMethod.class, "invokeInternal", Object.class, Object[].class);
private static final Method METHOD_INITIALIZE =
ReflectionUtils.getRequiredInternalMethod(InitializingBeanDefinition.class, "initialize", BeanResolutionContext.class, BeanContext.class, Object.class);
private static final Method METHOD_DISPOSE =
ReflectionUtils.getRequiredInternalMethod(DisposableBeanDefinition.class, "dispose", BeanResolutionContext.class, BeanContext.class, Object.class);
private static final Method DESTROY_INJECT_SCOPED_BEANS_METHOD = ReflectionUtils.getRequiredInternalMethod(BeanResolutionContext.class, "destroyInjectScopedBeans");
private static final Method CHECK_IF_SHOULD_LOAD_METHOD = ReflectionUtils.getRequiredMethod(AbstractInitializableBeanDefinition.class,
"checkIfShouldLoad",
BeanResolutionContext.class,
BeanContext.class);
private static final Method GET_MAP_METHOD = ReflectionUtils.getRequiredMethod(Map.class, "get", Object.class);
private static final Method LOAD_REFERENCE_METHOD = ReflectionUtils.getRequiredMethod(BeanDefinitionReference.class, "load");
private static final Method IS_CONTEXT_SCOPE_METHOD = ReflectionUtils.getRequiredMethod(BeanDefinitionReference.class, "isContextScope");
private static final Method IS_PROXIED_BEAN_METHOD = ReflectionUtils.getRequiredMethod(BeanDefinitionReference.class, "isProxiedBean");
private static final Method IS_ENABLED_METHOD = ReflectionUtils.getRequiredMethod(BeanContextConditional.class, "isEnabled", BeanContext.class);
private static final Method IS_ENABLED2_METHOD = ReflectionUtils.getRequiredMethod(BeanContextConditional.class, "isEnabled", BeanContext.class, BeanResolutionContext.class);
private static final Method GET_INTERCEPTED_TYPE_METHOD = ReflectionUtils.getRequiredMethod(AdvisedBeanType.class, "getInterceptedType");
private static final Method DO_INSTANTIATE_METHOD = ReflectionUtils.getRequiredMethod(AbstractInitializableBeanDefinition.class, "doInstantiate", BeanResolutionContext.class, BeanContext.class, Map.class);
private static final Method INSTANTIATE_METHOD = ReflectionUtils.getRequiredMethod(InstantiatableBeanDefinition.class, "instantiate", BeanResolutionContext.class, BeanContext.class);
private static final Method COLLECTION_UTILS_ENUM_SET_METHOD = ReflectionUtils.getRequiredMethod(CollectionUtils.class, "enumSet", Enum[].class);
private static final Method IS_INNER_CONFIGURATION_METHOD = ReflectionUtils.getRequiredMethod(AbstractInitializableBeanDefinition.class, "isInnerConfiguration", Class.class);
private static final Method CONTAINS_METHOD = ReflectionUtils.getRequiredMethod(Collection.class, "contains", Object.class);
private static final Method GET_EXPOSED_TYPES_METHOD = ReflectionUtils.getRequiredMethod(AbstractInitializableBeanDefinition.class, "getExposedTypes");
private static final Method GET_ORDER_METHOD = ReflectionUtils.getRequiredMethod(Ordered.class, "getOrder");
private static final Constructor<HashSet> HASH_SET_COLLECTION_CONSTRUCTOR = ReflectionUtils.getRequiredInternalConstructor(HashSet.class, Collection.class);
private static final Method ARRAYS_AS_LIST_METHOD = ReflectionUtils.getRequiredMethod(Arrays.class, "asList", Object[].class);
private static final Method COLLECTIONS_SINGLETON_METHOD = ReflectionUtils.getRequiredMethod(Collections.class, "singleton", Object.class);
private static final Method OPTIONAL_IS_PRESENT_METHOD = ReflectionUtils.getRequiredMethod(Optional.class, "isPresent");
private static final Method OPTIONAL_GET_METHOD = ReflectionUtils.getRequiredMethod(Optional.class, "get");
private static final Method DURATION_TO_MILLIS_METHOD = ReflectionUtils.getRequiredMethod(Duration.class, "toMillis");
private static final Method PROVIDER_GET_ANNOTATION_METADATA_METHOD = ReflectionUtils.getRequiredMethod(AnnotationMetadataProvider.class, "getAnnotationMetadata");
private static final Method IS_PROXY_TARGET_METHOD = ReflectionUtils.getRequiredMethod(BeanDefinitionReference.class, "isProxyTarget");
private static final Method GET_CONFIGURATION_PATH_METHOD = ReflectionUtils.getRequiredInternalMethod(BeanResolutionContext.class, "getConfigurationPath");
private static final Constructor<AbstractExecutableMethod> ABSTRACT_EXECUTABLE_METHOD_CONSTRUCTOR = ReflectionUtils.getRequiredInternalConstructor(AbstractExecutableMethod.class, Class.class, String.class);
private static final Method GET_TYPE_PARAMETERS_METHOD = ReflectionUtils.getRequiredInternalMethod(TypeVariableResolver.class, "getTypeParameters");
private static final Method ARGUMENT_OF_METHOD = ReflectionUtils.getRequiredInternalMethod(Argument.class, "of", Class.class);
private final String beanFullClassName;
private final String beanDefinitionName;
private final TypeDef beanTypeDef;
private final Map<String, MethodDef> loadTypeMethods = new LinkedHashMap<>();
private final String packageName;
private final String beanSimpleClassName;
private final ClassTypeDef beanDefinitionTypeDef;
private final boolean isInterface;
private final boolean isAbstract;
private final boolean isConfigurationProperties;
private final Element beanProducingElement;
private final ClassElement beanTypeElement;
private final VisitorContext visitorContext;
private final List<String> beanTypeInnerClasses;
private final EvaluatedExpressionProcessor evaluatedExpressionProcessor;
private boolean beanFinalized = false;
private ClassTypeDef superType = TYPE_ABSTRACT_BEAN_DEFINITION_AND_REFERENCE;
private boolean superBeanDefinition = false;
private boolean isSuperFactory = false;
private final AnnotationMetadata annotationMetadata;
private boolean preprocessMethods = false;
private Map<String, Map<String, ClassElement>> typeArguments;
@Nullable
private String interceptedType;
private final List<FieldVisitData> fieldInjectionPoints = new ArrayList<>(2);
private final List<MethodVisitData> methodInjectionPoints = new ArrayList<>(2);
private final List<MethodVisitData> postConstructMethodVisits = new ArrayList<>(2);
private final List<MethodVisitData> preDestroyMethodVisits = new ArrayList<>(2);
private final List<MethodVisitData> allMethodVisits = new ArrayList<>(2);
private final Map<ClassElement, List<AnnotationVisitData>> annotationInjectionPoints = new LinkedHashMap<>(2);
private final Map<String, Boolean> isLifeCycleCache = new HashMap<>(2);
private ExecutableMethodsDefinitionWriter executableMethodsDefinitionWriter;
private Object constructor; // MethodElement or FieldElement
private boolean disabled = false;
private final boolean keepConfPropInjectPoints;
private boolean proxiedBean = false;
private boolean isProxyTarget = false;
private String proxyBeanDefinitionName, proxyBeanTypeName;
private final OriginatingElements originatingElements;
private final ClassDef.ClassDefBuilder classDefBuilder;
private BuildMethodDefinition buildMethodDefinition;
private final List<InjectMethodCommand> injectCommands = new ArrayList<>();
private ConfigBuilderInjectCommand configBuilderInjectCommand;
private boolean validated;
private final Function<String, ExpressionDef> loadClassValueExpressionFn;
/**
* Creates a bean definition writer.
*
* @param classElement The class element
* @param visitorContext The visitor context
*/
public BeanDefinitionWriter(ClassElement classElement,
VisitorContext visitorContext) {
this(classElement, OriginatingElements.of(classElement), visitorContext, null);
}
/**
* Creates a bean definition writer.
*
* @param classElement The class element
* @param originatingElements The originating elements
* @param visitorContext The visitor context
*/
public BeanDefinitionWriter(ClassElement classElement,
OriginatingElements originatingElements,
VisitorContext visitorContext) {
this(classElement, originatingElements, visitorContext, null);
}
/**
* Creates a bean definition writer.
*
* @param beanProducingElement The bean producing element
* @param originatingElements The originating elements
* @param visitorContext The visitor context
* @param uniqueIdentifier An optional unique identifier to include in the bean name
*/
public BeanDefinitionWriter(Element beanProducingElement,
OriginatingElements originatingElements,
VisitorContext visitorContext,
@Nullable Integer uniqueIdentifier) {
this.originatingElements = originatingElements;
this.beanProducingElement = beanProducingElement;
if (beanProducingElement instanceof ClassElement classElement) {
autoApplyNamedToBeanProducingElement(classElement);
if (classElement.isPrimitive()) {
throw new IllegalArgumentException("Primitive beans can only be created from factories");
}
this.beanTypeElement = classElement;
this.packageName = classElement.getPackageName();
this.isInterface = classElement.isInterface();
this.isAbstract = classElement.isAbstract();
this.beanFullClassName = classElement.getName();
this.beanSimpleClassName = classElement.getSimpleName();
this.beanDefinitionName = getBeanDefinitionName(packageName, beanSimpleClassName);
} else if (beanProducingElement instanceof MethodElement factoryMethodElement) {
autoApplyNamedToBeanProducingElement(beanProducingElement);
final ClassElement producedElement = factoryMethodElement.getGenericReturnType();
this.beanTypeElement = producedElement;
this.packageName = producedElement.getPackageName();
this.isInterface = producedElement.isInterface();
this.isAbstract = false;
this.beanFullClassName = producedElement.getName();
this.beanSimpleClassName = producedElement.getSimpleName();
String upperCaseMethodName = NameUtils.capitalize(factoryMethodElement.getName());
if (uniqueIdentifier == null) {
throw new IllegalArgumentException("Factory methods require passing a unique identifier");
}
final ClassElement declaringType = factoryMethodElement.getOwningType();
this.beanDefinitionName = declaringType.getPackageName() + "." + prefixClassName(declaringType.getSimpleName()) + "$" + upperCaseMethodName + uniqueIdentifier + CLASS_SUFFIX;
} else if (beanProducingElement instanceof PropertyElement factoryPropertyElement) {
autoApplyNamedToBeanProducingElement(beanProducingElement);
final ClassElement producedElement = factoryPropertyElement.getGenericType();
this.beanTypeElement = producedElement;
this.packageName = producedElement.getPackageName();
this.isInterface = producedElement.isInterface();
this.isAbstract = beanProducingElement.isAbstract();
this.beanFullClassName = producedElement.getName();
this.beanSimpleClassName = producedElement.getSimpleName();
String upperCaseMethodName = NameUtils.capitalize(factoryPropertyElement.getName());
if (uniqueIdentifier == null) {
throw new IllegalArgumentException("Factory methods require passing a unique identifier");
}
final ClassElement declaringType = factoryPropertyElement.getOwningType();
this.beanDefinitionName = declaringType.getPackageName() + "." + prefixClassName(declaringType.getSimpleName()) + "$" + upperCaseMethodName + uniqueIdentifier + CLASS_SUFFIX;
} else if (beanProducingElement instanceof FieldElement factoryMethodElement) {
autoApplyNamedToBeanProducingElement(beanProducingElement);
final ClassElement producedElement = factoryMethodElement.getGenericField();
this.beanTypeElement = producedElement;
this.packageName = producedElement.getPackageName();
this.isInterface = producedElement.isInterface();
this.isAbstract = false;
this.beanFullClassName = producedElement.getName();
this.beanSimpleClassName = producedElement.getSimpleName();
String fieldName = NameUtils.capitalize(factoryMethodElement.getName());
if (uniqueIdentifier == null) {
throw new IllegalArgumentException("Factory fields require passing a unique identifier");
}
final ClassElement declaringType = factoryMethodElement.getOwningType();
this.beanDefinitionName = declaringType.getPackageName() + "." + prefixClassName(declaringType.getSimpleName()) + "$" + fieldName + uniqueIdentifier + CLASS_SUFFIX;
} else if (beanProducingElement instanceof BeanElementBuilder beanElementBuilder) {
this.beanTypeElement = beanElementBuilder.getBeanType();
this.packageName = this.beanTypeElement.getPackageName();
this.isInterface = this.beanTypeElement.isInterface();
this.isAbstract = beanElementBuilder.getProducingElement() instanceof ClassElement && this.beanTypeElement.isAbstract();
this.beanFullClassName = this.beanTypeElement.getName();
this.beanSimpleClassName = this.beanTypeElement.getSimpleName();
if (uniqueIdentifier == null) {
throw new IllegalArgumentException("Beans produced by addAssociatedBean(..) require passing a unique identifier");
}
final Element originatingElement = beanElementBuilder.getOriginatingElement();
if (originatingElement instanceof ClassElement originatingClass) {
this.beanDefinitionName = getAssociatedBeanName(uniqueIdentifier, originatingClass);
} else if (originatingElement instanceof MethodElement methodElement) {
ClassElement originatingClass = methodElement.getDeclaringType();
this.beanDefinitionName = getAssociatedBeanName(uniqueIdentifier, originatingClass);
} else {
throw new IllegalArgumentException("Unsupported originating element");
}
} else {
throw new IllegalArgumentException("Unsupported element type: " + beanProducingElement.getClass().getName());
}
this.annotationMetadata = beanProducingElement.getTargetAnnotationMetadata();
this.beanDefinitionTypeDef = ClassTypeDef.of(beanDefinitionName);
this.beanTypeDef = TypeDef.erasure(beanTypeElement);
this.isConfigurationProperties = isConfigurationProperties(annotationMetadata);
validateExposedTypes(annotationMetadata, visitorContext);
this.visitorContext = visitorContext;
this.evaluatedExpressionProcessor = new EvaluatedExpressionProcessor(visitorContext, getOriginatingElement());
evaluatedExpressionProcessor.processEvaluatedExpressions(this.annotationMetadata,
beanTypeElement.getName().contains(BeanDefinitionVisitor.PROXY_SUFFIX) ? null : beanTypeElement);
beanTypeInnerClasses = beanTypeElement.getEnclosedElements(ElementQuery.of(ClassElement.class))
.stream()
.filter(this::isConfigurationProperties)
.map(Element::getName)
.toList();
String prop = visitorContext.getOptions().get(OMIT_CONFPROP_INJECTION_POINTS);
keepConfPropInjectPoints = prop == null || !prop.equals("true");
TypeDef argumentType;
if (beanTypeDef instanceof TypeDef.Primitive primitive) {
argumentType = primitive.wrapperType();
} else if (beanTypeDef instanceof TypeDef.Array array) {
argumentType = array;
} else {
argumentType = ClassTypeDef.of(beanTypeElement);
}
classDefBuilder = ClassDef.builder(beanDefinitionName)
.synthetic()
.addModifiers(Modifier.PUBLIC)
.addAnnotation(AnnotationDef.builder(Generated.class).addMember("service", BeanDefinitionReference.class.getName()).build())
.superclass(TypeDef.parameterized(superType, argumentType));
loadClassValueExpressionFn = AnnotationMetadataGenUtils.createLoadClassValueExpressionFn(beanDefinitionTypeDef, loadTypeMethods);
}
/**
* Mark to generate proxy methods.
*
* @param proxyBeanDefinitionName The definition name
* @param proxyBeanTypeName The proxy bean name
*/
public void generateProxyReference(String proxyBeanDefinitionName, String proxyBeanTypeName) {
Objects.requireNonNull(proxyBeanDefinitionName);
Objects.requireNonNull(proxyBeanTypeName);
this.proxyBeanDefinitionName = proxyBeanDefinitionName;
this.proxyBeanTypeName = proxyBeanTypeName;
}
@Override
public boolean isEnabled() {
return !disabled;
}
/**
* Returns {@link ExecutableMethodsDefinitionWriter} of one exists.
*
* @return An instance of {@link ExecutableMethodsDefinitionWriter}
*/
@Nullable
public ExecutableMethodsDefinitionWriter getExecutableMethodsWriter() {
return executableMethodsDefinitionWriter;
}
@NonNull
private String getAssociatedBeanName(@NonNull Integer uniqueIdentifier, ClassElement originatingClass) {
return originatingClass.getPackageName() + "." + prefixClassName(originatingClass.getSimpleName()) + prefixClassName(beanSimpleClassName) + uniqueIdentifier + CLASS_SUFFIX;
}
private void autoApplyNamedToBeanProducingElement(Element beanProducingElement) {
final AnnotationMetadata annotationMetadata = beanProducingElement.getAnnotationMetadata();
if (!annotationMetadata.hasAnnotation(EachProperty.class) && !annotationMetadata.hasAnnotation(EachBean.class)) {
autoApplyNamedIfPresent(beanProducingElement, annotationMetadata);
}
}
private void validateExposedTypes(AnnotationMetadata annotationMetadata, VisitorContext visitorContext) {
if (annotationMetadata instanceof AnnotationMetadataHierarchy) {
annotationMetadata = annotationMetadata.getDeclaredMetadata();
}
final String[] types = annotationMetadata
.stringValues(Bean.class, "typed");
if (ArrayUtils.isNotEmpty(types) && !beanTypeElement.isProxy()) {
for (String name : types) {
final ClassElement exposedType = visitorContext.getClassElement(name).orElse(null);
if (exposedType == null) {
visitorContext.fail("Bean defines an exposed type [" + name + "] that is not on the classpath", beanProducingElement);
} else if (!beanTypeElement.isAssignable(exposedType)) {
visitorContext.fail("Bean defines an exposed type [" + name + "] that is not implemented by the bean type", beanProducingElement);
}
}
}
}
@NonNull
private static String getBeanDefinitionName(String packageName, String className) {
return packageName + "." + prefixClassName(className) + CLASS_SUFFIX;
}
private static String prefixClassName(String className) {
if (className.startsWith("$")) {
return className;
}
return "$" + className;
}
@NonNull
@Override
public ClassElement[] getTypeArguments() {
if (hasTypeArguments()) {
final Map<String, ClassElement> args = this.typeArguments.get(this.getBeanTypeName());
if (CollectionUtils.isNotEmpty(args)) {
return args.values().toArray(ClassElement.ZERO_CLASS_ELEMENTS);
}
}
return BeanDefinitionVisitor.super.getTypeArguments();
}
@Override
@NonNull
public Map<String, ClassElement> getTypeArgumentMap() {
if (hasTypeArguments()) {
Map<String, ClassElement> args = this.typeArguments.get(this.getBeanTypeName());
if (CollectionUtils.isNotEmpty(args)) {
return Collections.unmodifiableMap(args);
}
}
return Collections.emptyMap();
}
/**
* @return The name of the bean definition reference class.
*/
@Override
@NonNull
public String getBeanDefinitionReferenceClassName() {
throw new IllegalStateException("Not supported!");
}
/**
* @return The data for any post construct methods that were visited
*/
public List<MethodVisitData> getPostConstructMethodVisits() {
return Collections.unmodifiableList(postConstructMethodVisits);
}
@Override
public boolean isInterface() {
return isInterface;
}
@Override
public boolean isSingleton() {
return annotationMetadata.hasDeclaredStereotype(AnnotationUtil.SINGLETON);
}
@Override
public void visitBeanDefinitionInterface(Class<? extends BeanDefinition> interfaceType) {
this.classDefBuilder.addSuperinterface(TypeDef.of(interfaceType));
}
@Override
public void visitSuperBeanDefinition(String name) {
this.superBeanDefinition = true;
this.superType = ClassTypeDef.of(name);
classDefBuilder.superclass(superType);
}
@Override
public void visitSuperBeanDefinitionFactory(String beanName) {
this.superBeanDefinition = false;
this.isSuperFactory = true;
}
@Override
public String getBeanTypeName() {
return beanFullClassName;
}
@Override
public void setValidated(boolean validated) {
if (validated) {
if (!this.validated) {
classDefBuilder.addSuperinterface(ClassTypeDef.of(ValidatedBeanDefinition.class));
this.validated = true;
}
} else {
if (this.validated) {
throw new IllegalStateException("Bean definition " + beanTypeDef + " already marked for validation");
}
}
}
@Override
public void setInterceptedType(String typeName) {
if (typeName != null) {
classDefBuilder.addSuperinterface(TypeDef.of(AdvisedBeanType.class));
}
this.interceptedType = typeName;
}
@Override
public Optional<String> getInterceptedType() {
return Optional.ofNullable(interceptedType);
}
@Override
public boolean isValidated() {
return validated;
}
@Override
public String getBeanDefinitionName() {
return beanDefinitionName;
}
@Override
public Element getOriginatingElement() {
Element[] originatingElements = getOriginatingElements();
if (ArrayUtils.isNotEmpty(originatingElements)) {
return originatingElements[0];
}
return null;
}
/**
* <p>In the case where the produced class is produced by a factory method annotated with
* {@link Bean} this method should be called.</p>
*
* @param factoryClass The factory class
* @param factoryMethod The factory method
*/
@Override
public void visitBeanFactoryMethod(ClassElement factoryClass,
MethodElement factoryMethod) {
if (constructor != null) {
throw new IllegalStateException(MESSAGE_ONLY_SINGLE_CALL_PERMITTED);
} else {
constructor = factoryMethod;
// now prepare the implementation of the build method. See BeanFactory interface
visitBuildFactoryMethodDefinition(factoryClass, factoryMethod, factoryMethod.getParameters());
}
}
/**
* <p>In the case where the produced class is produced by a factory method annotated with
* {@link Bean} this method should be called.</p>
*
* @param factoryClass The factory class
* @param factoryMethod The factory method
* @param parameters The parameters
*/
@Override
public void visitBeanFactoryMethod(ClassElement factoryClass,
MethodElement factoryMethod,
ParameterElement[] parameters) {
if (constructor != null) {
throw new IllegalStateException(MESSAGE_ONLY_SINGLE_CALL_PERMITTED);
} else {
constructor = factoryMethod;
// now prepare the implementation of the build method. See BeanFactory interface
visitBuildFactoryMethodDefinition(factoryClass, factoryMethod, parameters);
}
}
/**
* <p>In the case where the produced class is produced by a factory field annotated with
* {@link Bean} this method should be called.</p>
*
* @param factoryClass The factory class
* @param factoryField The factory field
*/
@Override
public void visitBeanFactoryField(ClassElement factoryClass, FieldElement factoryField) {
if (constructor != null) {
throw new IllegalStateException(MESSAGE_ONLY_SINGLE_CALL_PERMITTED);
} else {
constructor = factoryField;
autoApplyNamedIfPresent(factoryField, factoryField.getAnnotationMetadata());
// now prepare the implementation of the build method. See BeanFactory interface
visitBuildFactoryMethodDefinition(factoryClass, factoryField);
}
}
/**
* Visits the constructor used to create the bean definition.
*
* @param constructor The constructor
* @param requiresReflection Whether invoking the constructor requires reflection
* @param visitorContext The visitor context
*/
@Override
public void visitBeanDefinitionConstructor(MethodElement constructor,
boolean requiresReflection,
VisitorContext visitorContext) {
if (this.constructor == null) {
this.constructor = constructor;
// now prepare the implementation of the build method. See BeanFactory interface
visitBuildConstructorDefinition(constructor, requiresReflection);
evaluatedExpressionProcessor.processEvaluatedExpressions(constructor.getAnnotationMetadata(), null);
for (ParameterElement parameter : constructor.getParameters()) {
evaluatedExpressionProcessor.processEvaluatedExpressions(parameter.getAnnotationMetadata(), null);
}
}
}
@Override
public void visitDefaultConstructor(AnnotationMetadata annotationMetadata, VisitorContext visitorContext) {
if (this.constructor == null) {
ClassElement bean = ClassElement.of(((ClassTypeDef) beanTypeDef).getName());
MethodElement defaultConstructor = MethodElement.of(
bean,
annotationMetadata,
bean,
bean,
"<init>"
);
constructor = defaultConstructor;
// now prepare the implementation of the build method. See BeanFactory interface
visitBuildConstructorDefinition(defaultConstructor, false);
}
}
/**
* Finalize the bean definition to the given output stream.
*/
@SuppressWarnings("Duplicates")
@Override
public void visitBeanDefinitionEnd() {
if (executableMethodsDefinitionWriter != null) {
// Make sure the methods are written and annotation defaults are contributed
executableMethodsDefinitionWriter.visitDefinitionEnd();
}
processAllBeanElementVisitors();
evaluatedExpressionProcessor.registerExpressionForBuildTimeInit(classDefBuilder);
MethodDef getOrderMethod = getGetOrder();
if (getOrderMethod != null) {
classDefBuilder.addMethod(getOrderMethod);
}
if (interceptedType != null) {
classDefBuilder.addMethod(
getGetInterceptedType(TypeDef.of(interceptedType))
);
}
classDefBuilder.addMethod(
MethodDef.override(
LOAD_REFERENCE_METHOD
).build((aThis, methodParameters) -> aThis.type().instantiate().returning())
);
if (annotationMetadata.hasDeclaredAnnotation(Context.class)) {
classDefBuilder.addMethod(
MethodDef.override(
IS_CONTEXT_SCOPE_METHOD
).build((aThis, methodParameters) -> ExpressionDef.trueValue().returning())
);
}
if (proxiedBean || superType != TYPE_ABSTRACT_BEAN_DEFINITION_AND_REFERENCE) {
classDefBuilder.addMethod(
MethodDef.override(
IS_PROXIED_BEAN_METHOD
).build((aThis, methodParameters) -> ExpressionDef.constant(proxiedBean).returning())
);
}
if (isProxyTarget || superType != TYPE_ABSTRACT_BEAN_DEFINITION_AND_REFERENCE) {
classDefBuilder.addMethod(
MethodDef.override(
IS_PROXY_TARGET_METHOD
).build((aThis, methodParameters) -> ExpressionDef.constant(isProxyTarget).returning())
);
}
if (!annotationMetadata.hasStereotype(Requires.class)) {
classDefBuilder.addMethod(
MethodDef.override(
IS_ENABLED_METHOD
).build((aThis, methodParameters) -> ExpressionDef.trueValue().returning())
);
classDefBuilder.addMethod(
MethodDef.override(
IS_ENABLED2_METHOD
).build((aThis, methodParameters) -> ExpressionDef.trueValue().returning())
);
}
if (proxyBeanDefinitionName != null) {
classDefBuilder.addMethod(
MethodDef.override(
METHOD_PROXY_TARGET_TYPE
).build((aThis, methodParameters)
-> ExpressionDef.constant(ClassTypeDef.of(proxyBeanDefinitionName)).returning())
);
classDefBuilder.addMethod(
MethodDef.override(
METHOD_PROXY_TARGET_CLASS
).build((aThis, methodParameters)
-> ExpressionDef.constant(ClassTypeDef.of(proxyBeanTypeName)).returning())
);
}
classDefBuilder.addMethod(
getBuildMethod(buildMethodDefinition)
);
if (!injectCommands.isEmpty()) {
classDefBuilder.addMethod(
getInjectMethod(injectCommands)
);
}
if (buildMethodDefinition.postConstruct != null) {
// for "super bean definition" we only add code to trigger "initialize"
if (!superBeanDefinition || buildMethodDefinition.postConstruct.intercepted) {
classDefBuilder.addSuperinterface(TypeDef.of(InitializingBeanDefinition.class));
// Create a new method that will be invoked by the intercepted chain
MethodDef targetInitializeMethod = buildInitializeMethod(buildMethodDefinition.postConstruct, MethodDef.builder("initialize$intercepted")
.addModifiers(Modifier.PUBLIC)
.addParameters(BeanResolutionContext.class, BeanContext.class, Object.class)
.returns(Object.class));
classDefBuilder.addMethod(
targetInitializeMethod
);
// Original initialize method is invoking the interceptor chain
classDefBuilder.addMethod(
MethodDef.override(METHOD_INITIALIZE).build((aThis, methodParameters) -> {
ClassTypeDef executableMethodInterceptor = createExecutableMethodInterceptor(targetInitializeMethod, "InitializeInterceptor");
return interceptAndReturn(aThis, methodParameters, executableMethodInterceptor, INITIALIZE_INTERCEPTOR_METHOD);
})
);
}
}
if (buildMethodDefinition.preDestroy != null) {
classDefBuilder.addSuperinterface(TypeDef.of(DisposableBeanDefinition.class));
if (buildMethodDefinition.preDestroy.intercepted) {
// Create a new method that will be invoked by the intercepted chain
MethodDef targetDisposeMethod = buildDisposeMethod(buildMethodDefinition.preDestroy, MethodDef.builder("dispose$intercepted")
.addModifiers(Modifier.PUBLIC)
.addParameters(BeanResolutionContext.class, BeanContext.class, Object.class)
.returns(Object.class));
classDefBuilder.addMethod(
targetDisposeMethod
);
// Original dispose method is invoking the interceptor chain
classDefBuilder.addMethod(
MethodDef.override(METHOD_DISPOSE).build((aThis, methodParameters) -> {
ClassTypeDef executableMethodInterceptor = createExecutableMethodInterceptor(targetDisposeMethod, "DisposeInterceptor");
return interceptAndReturn(aThis, methodParameters, executableMethodInterceptor, DISPOSE_INTERCEPTOR_METHOD);
})
);
} else {
classDefBuilder.addMethod(
buildDisposeMethod(buildMethodDefinition.preDestroy, MethodDef.override(METHOD_DISPOSE))
);
}
}
StaticBlock staticBlock = getStaticInitializer();
classDefBuilder.addStaticInitializer(staticBlock.statement);
addConstructor(staticBlock);
loadTypeMethods.values().forEach(classDefBuilder::addMethod);
this.beanFinalized = true;
}
private MethodDef getGetInterceptedType(TypeDef interceptedType) {
return MethodDef.override(GET_INTERCEPTED_TYPE_METHOD)
.build((aThis, methodParameters) -> ExpressionDef.constant(interceptedType).returning());
}
private MethodDef getBuildMethod(BuildMethodDefinition buildMethodDefinition) {
boolean isParametrized = isParametrized(buildMethodDefinition.getParameters());
MethodDef.MethodDefBuilder buildMethodBuilder;
if (isParametrized) {
buildMethodBuilder = MethodDef.override(DO_INSTANTIATE_METHOD);
classDefBuilder.addSuperinterface(TypeDef.of(ParametrizedInstantiatableBeanDefinition.class));
} else {
buildMethodBuilder = MethodDef.override(INSTANTIATE_METHOD);
}
return buildMethodBuilder.build((aThis, methodParameters) -> StatementDef.multi(
invokeCheckIfShouldLoadIfNecessary(aThis, methodParameters),
buildInstance(
aThis,
methodParameters,
buildMethodDefinition,
instance -> onBeanInstance(aThis, methodParameters, buildMethodDefinition, instance),
isParametrized
)
));
}
private MethodDef getInjectMethod(List<InjectMethodCommand> injectCommands) {
return MethodDef.override(INJECT_BEAN_METHOD)
.build((aThis, methodParameters) -> {
return methodParameters.get(2).cast(beanTypeDef).newLocal("beanInstance", instanceVar -> {
InjectMethodSignature injectMethodSignature = new InjectMethodSignature(aThis, methodParameters, instanceVar);
List<StatementDef> statements = new ArrayList<>();
boolean hasInjectPoint = false;
for (InjectMethodCommand injectCommand : injectCommands) {
statements.add(getInjectStatement(injectCommand, injectMethodSignature));
hasInjectPoint |= injectCommand.hasInjectScope();
}
List<StatementDef> returnStatements = new ArrayList<>();
if (hasInjectPoint) {
returnStatements.add(destroyInjectScopeBeansIfNecessary(methodParameters));
}
returnStatements.add(instanceVar.returning());
statements.addAll(returnStatements);
if (isConfigurationProperties) {
return aThis.invoke(
CONTAINS_PROPERTIES_METHOD,
injectMethodSignature.beanResolutionContext,
injectMethodSignature.beanContext
).ifTrue(
StatementDef.multi(statements),
StatementDef.multi(returnStatements)
);
}
return StatementDef.multi(statements);
});
});
}
private StatementDef getInjectStatement(InjectMethodCommand injectionPoint, InjectMethodSignature injectMethodSignature) {
if (injectionPoint instanceof SetterInjectionInjectCommand setterInjectionInjectCommand) {
return setSetterValue(
injectMethodSignature,
setterInjectionInjectCommand.declaringType,
setterInjectionInjectCommand.methodElement,
setterInjectionInjectCommand.annotationMetadata,
setterInjectionInjectCommand.requiresReflection,
setterInjectionInjectCommand.isOptional
);
}
if (injectionPoint instanceof InjectFieldInjectCommand injectFieldInjectCommand) {
return injectField(
injectMethodSignature,
injectFieldInjectCommand.declaringType,
injectFieldInjectCommand.fieldElement,
injectFieldInjectCommand.fieldElement.getAnnotationMetadata(),
injectFieldInjectCommand.requiresReflection
);
}
if (injectionPoint instanceof InjectMethodInjectCommand injectMethodInjectCommand) {
return injectMethod(
injectMethodInjectCommand.methodElement,
injectMethodInjectCommand.requiresReflection,
injectMethodSignature.aThis,
injectMethodSignature.methodParameters,
injectMethodSignature.instanceVar,
injectMethodInjectCommand.methodIndex
);
}
if (injectionPoint instanceof InjectFieldValueInjectCommand injectFieldValueInjectCommand) {
return setFieldValue(
injectMethodSignature,
injectFieldValueInjectCommand.declaringType,
injectFieldValueInjectCommand.fieldElement,
injectFieldValueInjectCommand.requiresReflection,
injectFieldValueInjectCommand.isOptional
);
}
if (injectionPoint instanceof ConfigMethodBuilderInjectPointCommand configBuilderMethodInjectPoint) {
String factoryMethod = configBuilderMethodInjectPoint.configBuilderState.getAnnotationMetadata()
.stringValue(ConfigurationBuilder.class, "factoryMethod").orElse(null);
ClassTypeDef builderType = ClassTypeDef.of(configBuilderMethodInjectPoint.type);
if (StringUtils.isNotEmpty(factoryMethod)) {
return builderType.invokeStatic(factoryMethod, builderType).newLocal("builder" + NameUtils.capitalize(configBuilderMethodInjectPoint.methodName), builderVar -> {
List<StatementDef> statements =
getBuilderMethodStatements(injectMethodSignature, configBuilderMethodInjectPoint.builderPoints, builderVar);
String propertyName = NameUtils.getPropertyNameForGetter(configBuilderMethodInjectPoint.methodName);
String setterName = NameUtils.setterNameFor(propertyName);
statements.add(injectMethodSignature.instanceVar
.invoke(setterName, TypeDef.VOID, builderVar));
return StatementDef.multi(statements);
});
} else {
return injectMethodSignature.instanceVar
.invoke(configBuilderMethodInjectPoint.methodName, builderType)
.newLocal("builder" + NameUtils.capitalize(configBuilderMethodInjectPoint.methodName), builderVar -> StatementDef.multi(
getBuilderMethodStatements(injectMethodSignature, configBuilderMethodInjectPoint.builderPoints, builderVar)
));
}
}
if (injectionPoint instanceof ConfigFieldBuilderInjectCommand configBuilderFieldInjectPoint) {
String factoryMethod = configBuilderFieldInjectPoint.configBuilderState.getAnnotationMetadata()
.stringValue(ConfigurationBuilder.class, "factoryMethod").orElse(null);
ClassTypeDef builderType = ClassTypeDef.of(configBuilderFieldInjectPoint.type);
if (StringUtils.isNotEmpty(factoryMethod)) {
return builderType.invokeStatic(factoryMethod, builderType).newLocal("builder" + NameUtils.capitalize(configBuilderFieldInjectPoint.field), builderVar -> {
List<StatementDef> statements = getBuilderMethodStatements(injectMethodSignature, configBuilderFieldInjectPoint.builderPoints, builderVar);
statements.add(injectMethodSignature.instanceVar
.field(configBuilderFieldInjectPoint.field, builderType)
.put(builderVar));
return StatementDef.multi(statements);
});
} else {
return injectMethodSignature.instanceVar
.field(configBuilderFieldInjectPoint.field, builderType)
.newLocal("builder" + NameUtils.capitalize(configBuilderFieldInjectPoint.field), builderVar -> StatementDef.multi(
getBuilderMethodStatements(injectMethodSignature, configBuilderFieldInjectPoint.builderPoints, builderVar)
));
}
}
throw new IllegalStateException();
}
private List<StatementDef> getBuilderMethodStatements(InjectMethodSignature injectMethodSignature, List<ConfigBuilderPointInjectCommand> points, VariableDef builderVar) {
List<StatementDef> statements = new ArrayList<>();
for (ConfigBuilderPointInjectCommand builderPoint : points) {
statements.add(
getConfigBuilderPointStatement(injectMethodSignature, builderVar, builderPoint)
);
}
return statements;
}
private StatementDef getConfigBuilderPointStatement(InjectMethodSignature injectMethodSignature,
VariableDef builderVar,
ConfigBuilderPointInjectCommand builderPoint) {
if (builderPoint instanceof ConfigBuilderMethodInjectCommand configBuilderMethodInjectPoint) {
return visitConfigBuilderMethodInternal(
injectMethodSignature,
configBuilderMethodInjectPoint.propertyName,
configBuilderMethodInjectPoint.returnType,
configBuilderMethodInjectPoint.methodName,
configBuilderMethodInjectPoint.paramType,
configBuilderMethodInjectPoint.generics,
false,
configBuilderMethodInjectPoint.path,
builderVar
);
}
if (builderPoint instanceof ConfigBuilderMethodDurationInjectCommand configBuilderMethodDurationInjectPoint) {
return visitConfigBuilderMethodInternal(
injectMethodSignature,
configBuilderMethodDurationInjectPoint.propertyName,
configBuilderMethodDurationInjectPoint.returnType,
configBuilderMethodDurationInjectPoint.methodName,
ClassElement.of(Duration.class),
Map.of(),
true,
configBuilderMethodDurationInjectPoint.path,
builderVar
);
}
throw new IllegalStateException();
}
private StatementDef setFieldValue(InjectMethodSignature injectMethodSignature,
TypedElement declaringType,
FieldElement fieldElement,
boolean requiresReflection,
boolean isOptional) {
AnnotationMetadata annotationMetadata = fieldElement.getAnnotationMetadata();
StatementDef setFieldValueStatement = setFieldValue(injectMethodSignature, fieldElement, isOptional, declaringType, requiresReflection, annotationMetadata);
if (isOptional) {
return getPropertyContainsCheck(
injectMethodSignature,
fieldElement.getType(),
fieldElement.getName(),
annotationMetadata
).ifTrue(setFieldValueStatement);
}
return setFieldValueStatement;
}
private StatementDef setFieldValue(InjectMethodSignature injectMethodSignature,
FieldElement fieldElement,
boolean isOptional,
TypedElement declaringType,
boolean requiresReflection,
AnnotationMetadata annotationMetadata) {
if (isInnerType(fieldElement.getGenericType())) {
return injectField(injectMethodSignature, declaringType, fieldElement, annotationMetadata, requiresReflection);
}
if (!isConfigurationProperties || requiresReflection) {
boolean isRequired = fieldElement
.booleanValue(AnnotationUtil.INJECT, AnnotationUtil.MEMBER_REQUIRED)
.orElse(true);
return visitFieldInjectionPointInternal(
injectMethodSignature,
declaringType,
fieldElement,
annotationMetadata,
requiresReflection,
GET_VALUE_FOR_FIELD,
isOptional,
false,
isRequired
);
}
fieldInjectionPoints.add(new FieldVisitData(declaringType, fieldElement, annotationMetadata, false));
int fieldIndex = fieldInjectionPoints.size() - 1;
ExpressionDef value;
Optional<String> property = annotationMetadata.stringValue(Property.class, "name");
if (property.isPresent()) {
value = getInvokeGetPropertyValueForField(injectMethodSignature, fieldElement, annotationMetadata, property.get(), fieldIndex);
} else {
Optional<String> valueValue = annotationMetadata.stringValue(Value.class);
if (valueValue.isPresent()) {
value = getInvokeGetPropertyPlaceholderValueForField(injectMethodSignature, fieldElement, annotationMetadata, valueValue.get(), fieldIndex);
} else {
// ???
value = ExpressionDef.nullValue();
}
}
return injectMethodSignature.instanceVar.field(fieldElement).put(value);
}
private StatementDef onBeanInstance(VariableDef.This aThis,
List<VariableDef.MethodParameter> methodParameters,
BuildMethodDefinition buildMethodDefinition,
ExpressionDef beanInstance) {
boolean needsInjectMethod = !injectCommands.isEmpty() || superBeanDefinition;
boolean needsInjectScope = hasInjectScope(buildMethodDefinition.getParameters());
boolean needsPostConstruct = buildMethodDefinition.postConstruct != null;
if (!needsInjectScope && !needsInjectMethod && !needsPostConstruct) {
return beanInstance.returning();
}
return beanInstance.newLocal("instance", instanceVar -> {
List<StatementDef> statements = new ArrayList<>();
if (needsInjectMethod) {
statements.add(
aThis.invoke(INJECT_BEAN_METHOD, methodParameters.get(0), methodParameters.get(1), instanceVar)
);
}
if (needsInjectScope) {
statements.add(
destroyInjectScopeBeansIfNecessary(methodParameters)
);
}
if (needsPostConstruct) {
statements.add(
aThis.invoke(METHOD_INITIALIZE,
methodParameters.get(0),
methodParameters.get(1),
instanceVar
).returning()
);
} else {
statements.add(instanceVar.returning());
}
return StatementDef.multi(statements);
});
}
private MethodDef buildDisposeMethod(BuildMethodLifecycleDefinition def, MethodDef.MethodDefBuilder override) {
return buildLifeCycleMethod(override, PRE_DESTROY_METHOD, def);
}
private MethodDef buildInitializeMethod(BuildMethodLifecycleDefinition def, MethodDef.MethodDefBuilder override) {
return buildLifeCycleMethod(override, POST_CONSTRUCT_METHOD, def);
}
private MethodDef buildLifeCycleMethod(MethodDef.MethodDefBuilder methodDefBuilder,
Method superMethod,
BuildMethodLifecycleDefinition lifeCycleDefinition) {
return methodDefBuilder.build((aThis, methodParameters) -> {
return aThis.invoke(superMethod, methodParameters).cast(beanTypeDef).newLocal("beanInstance", beanInstance -> {
List<StatementDef> statements = new ArrayList<>();
boolean hasInjectScope = false;
for (InjectMethodBuildCommand injectionPoint : lifeCycleDefinition.injectionPoints) {
statements.add(injectMethod(injectionPoint.methodElement, injectionPoint.requiresReflection, aThis, methodParameters, beanInstance, injectionPoint.methodIndex));
if (!hasInjectScope) {
for (ParameterElement parameter : injectionPoint.methodElement.getSuspendParameters()) {
if (hasInjectScope(parameter)) {
hasInjectScope = true;
break;
}
}
}
}
if (hasInjectScope) {
statements.add(
destroyInjectScopeBeansIfNecessary(methodParameters)
);
}
statements.add(beanInstance.returning());
return StatementDef.multi(statements);
});
});
}
private StatementDef buildInstance(VariableDef.This aThis,
List<VariableDef.MethodParameter> methodParameters,
BuildMethodDefinition buildMethodDefinition,
Function<ExpressionDef, StatementDef> onBeanInstance,
boolean isParametrized) {
StatementDef.DefineAndAssign[] constructorDef = new StatementDef.DefineAndAssign[] { null };
Supplier<VariableDef> constructorDefSupplier = new Supplier<VariableDef>() {
@Override
public VariableDef get() {
if (constructorDef[0] == null) {
Class<?> constructorType;
if (constructor instanceof MethodElement) {
constructorType = AbstractInitializableBeanDefinition.MethodReference.class;
} else {
constructorType = AbstractInitializableBeanDefinition.FieldReference.class;
}
constructorDef[0] = aThis.type()
.getStaticField(FIELD_CONSTRUCTOR, ClassTypeDef.of(AbstractInitializableBeanDefinition.MethodOrFieldReference.class))
.cast(constructorType)
.newLocal("constructorDef");
}
return constructorDef[0].variable();
}
};
if (buildMethodDefinition instanceof FactoryBuildMethodDefinition factoryBuildMethodDefinition) {
if (factoryBuildMethodDefinition.parameters.length > 0) {
List<? extends ExpressionDef> values = getConstructorArgumentValues(aThis, methodParameters,
List.of(buildMethodDefinition.getParameters()), isParametrized, constructorDefSupplier);
StatementDef statement = buildFactoryGet(aThis, methodParameters, onBeanInstance, factoryBuildMethodDefinition, values);
if (constructorDef[0] != null) {
return StatementDef.multi(
constructorDef[0],
statement
);
}
return statement;
}
return buildFactoryGet(aThis, methodParameters, onBeanInstance, factoryBuildMethodDefinition, List.of());
}
if (buildMethodDefinition instanceof ConstructorBuildMethodDefinition constructorBuildMethodDefinition) {
if (constructorBuildMethodDefinition.constructor.hasParameters()) {
List<? extends ExpressionDef> values = getConstructorArgumentValues(aThis, methodParameters,
List.of(buildMethodDefinition.getParameters()), isParametrized, constructorDefSupplier);
StatementDef statement = buildConstructorInstantiate(aThis, methodParameters, onBeanInstance, constructorBuildMethodDefinition, values);
if (constructorDef[0] != null) {
return StatementDef.multi(
constructorDef[0],
statement
);
}
return statement;
}
return buildConstructorInstantiate(aThis, methodParameters, onBeanInstance, constructorBuildMethodDefinition, List.of());
}
throw new IllegalStateException("Unknown build method definition: " + buildMethodDefinition);
}
private StatementDef buildConstructorInstantiate(VariableDef.This aThis,
List<VariableDef.MethodParameter> methodParameters,
Function<ExpressionDef, StatementDef> onBeanInstance,
ConstructorBuildMethodDefinition constructorBuildMethodDefinition,
List<? extends ExpressionDef> values) {
List<ParameterElement> parameters = List.of(constructorBuildMethodDefinition.constructor.getSuspendParameters());
if (isConstructorIntercepted(constructorBuildMethodDefinition.constructor)) {
ClassTypeDef factoryInterceptor = createConstructorInterceptor(constructorBuildMethodDefinition);
return onBeanInstance.apply(
invokeConstructorChain(
aThis,
methodParameters,
factoryInterceptor.instantiate(CONSTRUCTOR_ABSTRACT_CONSTRUCTOR_IP, aThis),
TypeDef.OBJECT.array().instantiate(values),
parameters)
);
}
return onBeanInstance.apply(
initializeBean(aThis, methodParameters, constructorBuildMethodDefinition, values)
);
}
private StatementDef buildFactoryGet(VariableDef.This aThis,
List<VariableDef.MethodParameter> methodParameters,
Function<ExpressionDef, StatementDef> onBeanInstance,
FactoryBuildMethodDefinition factoryBuildMethodDefinition, List<? extends ExpressionDef> values) {
return withGetFactoryBean(methodParameters, factoryBuildMethodDefinition, factoryVar -> {
List<ParameterElement> parameters = List.of(factoryBuildMethodDefinition.parameters);
if (isConstructorIntercepted(factoryBuildMethodDefinition.factoryElement)) {
ClassTypeDef factoryInterceptor = createFactoryInterceptor(factoryBuildMethodDefinition);
return onBeanInstance.apply(
invokeConstructorChain(
aThis,
methodParameters,
factoryInterceptor.instantiate(
List.of(
ClassTypeDef.of(BeanDefinition.class),
factoryVar.type()
), aThis, factoryVar),
TypeDef.OBJECT.array().instantiate(values),
parameters)
);
}
return onBeanInstance.apply(
getBeanFromFactory(factoryBuildMethodDefinition, factoryVar, values)
);
});
}
private ExpressionDef getBeanFromFactory(FactoryBuildMethodDefinition factoryBuildMethodDefinition,
ExpressionDef factoryVar,
List<? extends ExpressionDef> values) {
ClassTypeDef factoryType = ClassTypeDef.of(factoryBuildMethodDefinition.factoryClass);
Element factoryElement = factoryBuildMethodDefinition.factoryElement;
if (factoryElement instanceof MethodElement methodElement) {
if (methodElement.isReflectionRequired()) {
return TYPE_REFLECTION_UTILS.invokeStatic(
METHOD_INVOKE_INACCESSIBLE_METHOD,
methodElement.isStatic() ? ExpressionDef.nullValue() : factoryVar,
DispatchWriter.getTypeUtilsGetRequiredMethod(factoryType, methodElement),
TypeDef.OBJECT.array().instantiate(values)
);
}
if (methodElement.isStatic()) {
return factoryType.invokeStatic(methodElement, values);
}
return factoryVar.invoke(methodElement, values);
}
FieldElement fieldElement = (FieldElement) factoryElement;
if (fieldElement.isReflectionRequired()) {
return TYPE_REFLECTION_UTILS.invokeStatic(
GET_FIELD_WITH_REFLECTION_METHOD,
ExpressionDef.constant(factoryType),
ExpressionDef.constant(fieldElement.getName()),
fieldElement.isStatic() ? ExpressionDef.nullValue() : factoryVar
);
}
if (fieldElement.isStatic()) {
return factoryType.getStaticField(factoryElement.getName(), beanTypeDef);
}
return factoryVar.field(factoryElement.getName(), beanTypeDef);
}
private ExpressionDef initializeBean(VariableDef.This aThis,
List<VariableDef.MethodParameter> methodParameters,
ConstructorBuildMethodDefinition constructorBuildMethodDefinition,
List<? extends ExpressionDef> values) {
MethodElement constructor = constructorBuildMethodDefinition.constructor;
List<ExpressionDef> hasValuesExpressions;
if (values == null) {
hasValuesExpressions = null;
} else {
hasValuesExpressions = new ArrayList<>();
ParameterElement[] parameters = constructorBuildMethodDefinition.getParameters();
for (int i = 0; i < values.size(); i++) {
ExpressionDef value = values.get(i);
ParameterElement parameter = parameters[i];
if (parameter.hasAnnotation(Property.class)) {
hasValuesExpressions.add(
getContainsPropertyCheck(aThis, methodParameters, parameter)
);
} else {
hasValuesExpressions.add(value.isNonNull());
}
}
}
return MethodGenUtils.invokeBeanConstructor(constructor, constructorBuildMethodDefinition.requiresReflection, true, values, hasValuesExpressions);
}
private ExpressionDef getContainsPropertyCheck(VariableDef.This aThis,
List<VariableDef.MethodParameter> methodParameters,
ParameterElement parameterElement) {
String propertyName = parameterElement.stringValue(Property.class, "name").orElseThrow();
return aThis.invoke(
isMultiValueProperty(parameterElement.getType()) ? CONTAINS_PROPERTIES_VALUE_METHOD : CONTAINS_PROPERTY_VALUE_METHOD,
methodParameters.get(0),
methodParameters.get(1),
ExpressionDef.constant(propertyName)
);
}
private StatementDef withGetFactoryBean(List<VariableDef.MethodParameter> parameters,
FactoryBuildMethodDefinition factoryBuildMethodDefinition,
Function<ExpressionDef, StatementDef> fn) {
if (factoryBuildMethodDefinition.factoryElement.isStatic()) {
return fn.apply(ExpressionDef.nullValue());
}
// for Factory beans first we need to look up the factory bean
// before invoking the method to instantiate
// the below code looks up the factory bean.
TypeDef factoryTypeDef = TypeDef.erasure(factoryBuildMethodDefinition.factoryClass);
ExpressionDef argumentExpression = ClassTypeDef.of(Argument.class).invokeStatic(ArgumentExpUtils.METHOD_CREATE_ARGUMENT_SIMPLE,
ExpressionDef.constant(factoryTypeDef),
ExpressionDef.constant("factory")
);
return StatementDef.multi(
parameters.get(1).cast(DefaultBeanContext.class)
.invoke(METHOD_GET_BEAN,
// load the first argument of the method (the BeanResolutionContext) to be passed to the method
parameters.get(0),
// second argument is the bean type
ExpressionDef.constant(factoryTypeDef),
// third argument is the qualifier for the factory if any
getQualifier(factoryBuildMethodDefinition.factoryClass, argumentExpression)
).cast(factoryTypeDef).newLocal("factoryBean", factoryBeanVar -> StatementDef.multi(
parameters.get(0).invoke(METHOD_BEAN_RESOLUTION_CONTEXT_MARK_FACTORY),
fn.apply(factoryBeanVar)
))
);
}
private ClassTypeDef createConstructorInterceptor(ConstructorBuildMethodDefinition constructorBuildMethodDefinition) {
String interceptedConstructorWriterName = "ConstructorInterceptor";
ClassDef.ClassDefBuilder innerClassBuilder = ClassDef.builder(interceptedConstructorWriterName)
.synthetic()
.addModifiers(Modifier.FINAL)
.superclass(ClassTypeDef.of(AbstractBeanDefinitionBeanConstructor.class))
.addAnnotation(Generated.class);
innerClassBuilder.addMethod(
MethodDef.constructor()
.addModifiers(Modifier.PUBLIC)
.addParameters(CONSTRUCTOR_ABSTRACT_CONSTRUCTOR_IP.getParameterTypes())
.build((aThis, methodParameters)
-> aThis.superRef().invokeConstructor(CONSTRUCTOR_ABSTRACT_CONSTRUCTOR_IP, methodParameters.get(0)))
);
innerClassBuilder.addMethod(
MethodDef.override(METHOD_BEAN_CONSTRUCTOR_INSTANTIATE)
.build((aThis, methodParameters) -> {
ParameterElement[] parameters = constructorBuildMethodDefinition.constructor.getSuspendParameters();
List<ExpressionDef> values = IntStream.range(0, parameters.length)
.<ExpressionDef>mapToObj(index -> methodParameters.get(0).arrayElement(index).cast(TypeDef.erasure(parameters[index].getType())))
.toList();
return MethodGenUtils.invokeBeanConstructor(constructorBuildMethodDefinition.constructor, true, values)
.returning();
})
);
classDefBuilder.addInnerType(innerClassBuilder.build());
return ClassTypeDef.of(beanDefinitionName + "$" + interceptedConstructorWriterName);
}
private ClassTypeDef createFactoryInterceptor(FactoryBuildMethodDefinition factoryBuildMethodDefinition) {
String interceptedConstructorWriterName = "ConstructorInterceptor";
ClassDef.ClassDefBuilder innerClassBuilder = ClassDef.builder(interceptedConstructorWriterName)
.synthetic()
.addModifiers(Modifier.FINAL)
.superclass(ClassTypeDef.of(AbstractBeanDefinitionBeanConstructor.class))
.addAnnotation(Generated.class);
// for factory methods we have to store the factory instance in a field and modify the constructor pass the factory instance
ClassTypeDef factoryType = ClassTypeDef.of(factoryBuildMethodDefinition.factoryClass);
FieldDef factoryField = FieldDef.builder("$factory", factoryType)
.addModifiers(Modifier.PRIVATE, Modifier.FINAL)
.build();
innerClassBuilder.addField(factoryField);
innerClassBuilder.addMethod(
MethodDef.constructor()
.addModifiers(Modifier.PROTECTED)
.addParameters(CONSTRUCTOR_ABSTRACT_CONSTRUCTOR_IP.getParameterTypes())
.addParameters(factoryType)
.build((aThis, methodParameters)
-> StatementDef.multi(
aThis.superRef().invokeConstructor(CONSTRUCTOR_ABSTRACT_CONSTRUCTOR_IP, methodParameters.get(0)),
aThis.field(factoryField).put(methodParameters.get(1))
))
);
// now we need to implement the invoke method to execute the actual instantiation
innerClassBuilder.addMethod(
MethodDef.override(METHOD_BEAN_CONSTRUCTOR_INSTANTIATE)
.build((aThis, methodParameters) -> {
List<ExpressionDef> values = IntStream.range(0, factoryBuildMethodDefinition.parameters.length)
.<ExpressionDef>mapToObj(index -> methodParameters.get(0)
.arrayElement(index)
.cast(TypeDef.erasure(factoryBuildMethodDefinition.parameters[index].getType())))
.toList();
return getBeanFromFactory(factoryBuildMethodDefinition, aThis.field(factoryField), values).returning();
})
);
classDefBuilder.addInnerType(innerClassBuilder.build());
return ClassTypeDef.of(beanDefinitionName + "$" + interceptedConstructorWriterName);
}
private StaticBlock getStaticInitializer() {
List<StatementDef> statements = new ArrayList<>();
FieldDef annotationMetadataField = AnnotationMetadataGenUtils.createAnnotationMetadataFieldAndInitialize(annotationMetadata, loadClassValueExpressionFn);
classDefBuilder.addField(annotationMetadataField);
FieldDef failedInitializationField = FieldDef.builder(FIELD_FAILED_INITIALIZATION, Throwable.class)
.addModifiers(Modifier.PRIVATE, Modifier.FINAL, Modifier.STATIC)
.build();
classDefBuilder.addField(failedInitializationField);
List<StatementDef> initStatements = new ArrayList<>();
List<StatementDef> failStatements = new ArrayList<>();
FieldDef constructorRefField = FieldDef.builder(FIELD_CONSTRUCTOR, AbstractInitializableBeanDefinition.MethodOrFieldReference.class)
.addModifiers(Modifier.PRIVATE, Modifier.FINAL, Modifier.STATIC)
.build();
classDefBuilder.addField(constructorRefField);
initStatements.add(beanDefinitionTypeDef.getStaticField(constructorRefField).put(getConstructorRef()));
FieldDef injectionMethodsField = null;
FieldDef injectionFieldsField = null;
FieldDef annotationInjectionsFieldType = null;
FieldDef typeArgumentsField = null;
FieldDef executableMethodsField = null;
boolean hasMethodInjection = !superBeanDefinition && !allMethodVisits.isEmpty();
if (hasMethodInjection) {
TypeDef.Array methodReferenceArray = ClassTypeDef.of(AbstractInitializableBeanDefinition.MethodReference.class).array();
injectionMethodsField = FieldDef.builder(FIELD_INJECTION_METHODS, methodReferenceArray)
.addModifiers(Modifier.PRIVATE, Modifier.FINAL, Modifier.STATIC)
.build();
classDefBuilder.addField(injectionMethodsField);
initStatements.add(beanDefinitionTypeDef.getStaticField(injectionMethodsField)
.put(methodReferenceArray.instantiate(allMethodVisits.stream()
.map(md -> getNewMethodReference(md.beanType, md.methodElement, md.annotationMetadata, md.postConstruct, md.preDestroy))
.toList())));
failStatements.add(beanDefinitionTypeDef.getStaticField(injectionMethodsField).put(ExpressionDef.nullValue()));
}
boolean hasFieldInjection = !fieldInjectionPoints.isEmpty();
if (hasFieldInjection) {
TypeDef.Array fieldReferenceArray = ClassTypeDef.of(AbstractInitializableBeanDefinition.FieldReference.class).array();
injectionFieldsField = FieldDef.builder(FIELD_INJECTION_FIELDS, fieldReferenceArray)
.addModifiers(Modifier.PRIVATE, Modifier.FINAL, Modifier.STATIC)
.build();
classDefBuilder.addField(injectionFieldsField);
initStatements.add(beanDefinitionTypeDef.getStaticField(injectionFieldsField)
.put(fieldReferenceArray.instantiate(fieldInjectionPoints.stream()
.map(fd -> getNewFieldReference(fd.beanType, fd.fieldElement, fd.annotationMetadata))
.toList())));
failStatements.add(beanDefinitionTypeDef.getStaticField(injectionFieldsField).put(ExpressionDef.nullValue()));
}
boolean hasAnnotationInjection = !annotationInjectionPoints.isEmpty();
if (hasAnnotationInjection) {
TypeDef.Array annotationInjectionsFieldArray = ClassTypeDef.of(AbstractInitializableBeanDefinition.AnnotationReference.class).array();
annotationInjectionsFieldType = FieldDef.builder(FIELD_ANNOTATION_INJECTIONS, annotationInjectionsFieldArray)
.addModifiers(Modifier.PRIVATE, Modifier.FINAL, Modifier.STATIC)
.build();
classDefBuilder.addField(annotationInjectionsFieldType);
initStatements.add(beanDefinitionTypeDef.getStaticField(annotationInjectionsFieldType)
.put(annotationInjectionsFieldArray.instantiate(annotationInjectionPoints.keySet().stream()
.map(this::getNewAnnotationReference)
.toList())));
failStatements.add(beanDefinitionTypeDef.getStaticField(annotationInjectionsFieldType).put(ExpressionDef.nullValue()));
}
boolean hasTypeArguments = !superBeanDefinition && hasTypeArguments();
if (hasTypeArguments) {
typeArgumentsField = FieldDef.builder(FIELD_TYPE_ARGUMENTS, Map.class)
.addModifiers(Modifier.PRIVATE, Modifier.FINAL, Modifier.STATIC)
.build();
classDefBuilder.addField(typeArgumentsField);
initStatements.add(beanDefinitionTypeDef.getStaticField(typeArgumentsField)
.put(GenUtils.stringMapOf(
typeArguments, true, null, el -> ArgumentExpUtils.pushTypeArgumentElements(
annotationMetadata,
beanDefinitionTypeDef,
ClassElement.of(beanDefinitionName),
el,
loadClassValueExpressionFn
))
));
failStatements.add(beanDefinitionTypeDef.getStaticField(typeArgumentsField).put(ExpressionDef.nullValue()));
}
boolean hasExecutableMethods = executableMethodsDefinitionWriter != null;
if (hasExecutableMethods) {
ClassTypeDef execType = executableMethodsDefinitionWriter.getClassTypeDef();
executableMethodsField = FieldDef.builder(FIELD_EXECUTABLE_METHODS, execType)
.addModifiers(Modifier.PRIVATE, Modifier.FINAL, Modifier.STATIC)
.build();
classDefBuilder.addField(executableMethodsField);
initStatements.add(beanDefinitionTypeDef.getStaticField(executableMethodsField).put(execType.instantiate()));
failStatements.add(beanDefinitionTypeDef.getStaticField(executableMethodsField).put(ExpressionDef.nullValue()));
}
ClassTypeDef precalculatedInfoType = ClassTypeDef.of(AbstractInitializableBeanDefinition.PrecalculatedInfo.class);
FieldDef precalculatedInfoField = FieldDef.builder(FIELD_PRECALCULATED_INFO, precalculatedInfoType)
.addModifiers(Modifier.PRIVATE, Modifier.FINAL, Modifier.STATIC)
.build();
classDefBuilder.addField(precalculatedInfoField);
String scope = annotationMetadata.getAnnotationNameByStereotype(AnnotationUtil.SCOPE).orElse(null);
statements.add(
beanDefinitionTypeDef.getStaticField(precalculatedInfoField)
.put(
precalculatedInfoType.instantiate(
PRECALCULATED_INFO_CONSTRUCTOR,
// 1: `Optional` scope
scope == null ? TYPE_OPTIONAL.invokeStatic(METHOD_OPTIONAL_EMPTY)
: TYPE_OPTIONAL.invokeStatic(METHOD_OPTIONAL_OF, ExpressionDef.constant(scope)),
// 2: `boolean` isAbstract
ExpressionDef.constant(isAbstract),
// 3: `boolean` isIterable
ExpressionDef.constant(isIterable(annotationMetadata)),
// 4: `boolean` isSingleton
ExpressionDef.constant(isSingleton(scope)),
// 5: `boolean` isPrimary
ExpressionDef.constant(annotationMetadata.hasDeclaredStereotype(Primary.class)),
// 6: `boolean` isConfigurationProperties
ExpressionDef.constant(isConfigurationProperties),
// 7: isContainerType
ExpressionDef.constant(isContainerType()),
// 8: preprocessMethods
ExpressionDef.constant(preprocessMethods),
// 9: hasEvaluatedExpressions
ExpressionDef.constant(evaluatedExpressionProcessor.hasEvaluatedExpressions())
)
)
);
statements.add(
StatementDef.doTry(
StatementDef.multi(
initStatements
)
).doCatch(Throwable.class, exceptionVar -> StatementDef.multi(
beanDefinitionTypeDef.getStaticField(failedInitializationField).put(exceptionVar),
StatementDef.multi(failStatements)
))
);
statements.add(addInnerConfigurationMethod());
statements.add(addGetExposedTypes());
FieldDef preStartConditionsField = null;
FieldDef postStartConditionsField = null;
List<AnnotationValue<Requires>> requirements = annotationMetadata.getAnnotationValuesByType(Requires.class);
if (!requirements.isEmpty()) {
TypeDef.Array conditionsArrayType = ClassTypeDef.of(Condition.class).array();
preStartConditionsField = FieldDef.builder(FIELD_PRE_START_CONDITIONS, conditionsArrayType)
.addModifiers(Modifier.PRIVATE, Modifier.FINAL, Modifier.STATIC)
.build();
postStartConditionsField = FieldDef.builder(FIELD_POST_START_CONDITIONS, conditionsArrayType)
.addModifiers(Modifier.PRIVATE, Modifier.FINAL, Modifier.STATIC)
.build();
classDefBuilder.addField(preStartConditionsField);
classDefBuilder.addField(postStartConditionsField);
statements.add(addConditions(requirements, preStartConditionsField, postStartConditionsField));
}
// Defaults can be contributed by other static initializers, it should be at the end
AnnotationMetadataGenUtils.addAnnotationDefaults(statements, annotationMetadata, loadClassValueExpressionFn);
return new StaticBlock(
StatementDef.multi(statements),
annotationMetadataField,
failedInitializationField,
constructorRefField,
injectionMethodsField,
injectionFieldsField,
annotationInjectionsFieldType,
typeArgumentsField,
executableMethodsField,
precalculatedInfoField,
preStartConditionsField,
postStartConditionsField
);
}
private ExpressionDef getConstructorRef() {
if (constructor instanceof MethodElement methodElement) {
ParameterElement[] parameters = methodElement.getParameters();
List<ParameterElement> parameterList = Arrays.asList(parameters);
applyDefaultNamedToParameters(parameterList);
return getNewMethodReference(methodElement.getDeclaringType(), methodElement, methodElement.getAnnotationMetadata(), false, false);
} else if (constructor instanceof FieldElement fieldConstructor) {
return getNewFieldReference(fieldConstructor.getDeclaringType(), fieldConstructor, fieldConstructor.getAnnotationMetadata());
} else {
throw new IllegalArgumentException("Unexpected constructor: " + constructor);
}
}
private StatementDef addConditions(List<AnnotationValue<Requires>> requirements, FieldDef preStartConditionsField, FieldDef postStartConditionsField) {
List<Condition> preConditions = new ArrayList<>();
List<Condition> postConditions = new ArrayList<>();
if (requirements.isEmpty()) {
return StatementDef.multi();
}
List<AnnotationValue<Requires>> dynamicRequirements = new ArrayList<>();
for (AnnotationValue<Requires> requirement : requirements) {
if (requirement.getValues().values().stream().anyMatch(value -> value instanceof EvaluatedExpressionReference)) {
dynamicRequirements.add(requirement);
continue;
}
MatchesConditionUtils.createConditions(requirement, preConditions, postConditions);
}
if (!dynamicRequirements.isEmpty()) {
MutableAnnotationMetadata annotationMetadata = new MutableAnnotationMetadata();
for (AnnotationValue<Requires> requirement : requirements) {
annotationMetadata.addRepeatable(Requirements.class.getName(), requirement);
}
postConditions.add(new MatchesDynamicCondition(annotationMetadata));
}
Function<Condition, ExpressionDef> writer = new Function<>() {
@Override
public ExpressionDef apply(Condition condition) {
if (condition instanceof MatchesPropertyCondition matchesPropertyCondition) {
return newRecord(
matchesPropertyCondition.getClass(),
ExpressionDef.constant(matchesPropertyCondition.property()),
ExpressionDef.constant(matchesPropertyCondition.value()),
ExpressionDef.constant(matchesPropertyCondition.defaultValue()),
ExpressionDef.constant(matchesPropertyCondition.condition())
);
} else if (condition instanceof MatchesAbsenceOfBeansCondition matchesAbsenceOfBeansCondition) {
return newRecord(
matchesAbsenceOfBeansCondition.getClass(),
getAnnotationClassValues(matchesAbsenceOfBeansCondition.missingBeans())
);
} else if (condition instanceof MatchesPresenceOfBeansCondition matchesPresenceOfBeansCondition) {
return newRecord(
matchesPresenceOfBeansCondition.getClass(),
getAnnotationClassValues(matchesPresenceOfBeansCondition.beans())
);
} else if (condition instanceof MatchesAbsenceOfClassesCondition matchesAbsenceOfClassesCondition) {
return newRecord(
matchesAbsenceOfClassesCondition.getClass(),
getAnnotationClassValues(matchesAbsenceOfClassesCondition.classes())
);
} else if (condition instanceof MatchesPresenceOfClassesCondition matchesPresenceOfClassesCondition) {
return newRecord(
matchesPresenceOfClassesCondition.getClass(),
getAnnotationClassValues(matchesPresenceOfClassesCondition.classes())
);
} else if (condition instanceof MatchesPresenceOfEntitiesCondition matchesPresenceOfEntitiesCondition) {
return newRecord(
matchesPresenceOfEntitiesCondition.getClass(),
getAnnotationClassValues(matchesPresenceOfEntitiesCondition.classes())
);
} else if (condition instanceof MatchesAbsenceOfClassNamesCondition matchesAbsenceOfClassNamesCondition) {
return newRecord(
matchesAbsenceOfClassNamesCondition.getClass(),
ExpressionDef.constant(matchesAbsenceOfClassNamesCondition.classes())
);
} else if (condition instanceof MatchesConfigurationCondition matchesConfigurationCondition) {
return newRecord(
matchesConfigurationCondition.getClass(),
ExpressionDef.constant(matchesConfigurationCondition.configurationName()),
ExpressionDef.constant(matchesConfigurationCondition.minimumVersion())
);
} else if (condition instanceof MatchesCurrentNotOsCondition matchesCurrentNotOsCondition) {
return newRecord(
matchesCurrentNotOsCondition.getClass(),
ClassTypeDef.of(CollectionUtils.class)
.invokeStatic(
COLLECTION_UTILS_ENUM_SET_METHOD,
ClassTypeDef.of(Requires.Family.class).array().instantiate(
matchesCurrentNotOsCondition.notOs().stream().map(ExpressionDef::constant).toList()
)
)
);
} else if (condition instanceof MatchesCurrentOsCondition currentOsCondition) {
return newRecord(
currentOsCondition.getClass(),
ClassTypeDef.of(CollectionUtils.class)
.invokeStatic(
COLLECTION_UTILS_ENUM_SET_METHOD,
ClassTypeDef.of(Requires.Family.class).array().instantiate(
currentOsCondition.os().stream().map(ExpressionDef::constant).toList()
)
)
);
} else if (condition instanceof MatchesCustomCondition matchesCustomCondition) {
return newRecord(
matchesCustomCondition.getClass(),
getAnnotationClassValue(matchesCustomCondition.customConditionClass())
);
} else if (condition instanceof MatchesEnvironmentCondition matchesEnvironmentCondition) {
return newRecord(
matchesEnvironmentCondition.getClass(),
ExpressionDef.constant(matchesEnvironmentCondition.env())
);
} else if (condition instanceof MatchesMissingPropertyCondition matchesMissingPropertyCondition) {
return newRecord(
matchesMissingPropertyCondition.getClass(),
ExpressionDef.constant(matchesMissingPropertyCondition.property())
);
} else if (condition instanceof MatchesNotEnvironmentCondition matchesNotEnvironmentCondition) {
return newRecord(
matchesNotEnvironmentCondition.getClass(),
ExpressionDef.constant(matchesNotEnvironmentCondition.env())
);
} else if (condition instanceof MatchesPresenceOfResourcesCondition matchesPresenceOfResourcesCondition) {
return newRecord(
matchesPresenceOfResourcesCondition.getClass(),
ExpressionDef.constant(matchesPresenceOfResourcesCondition.resourcePaths())
);
} else if (condition instanceof MatchesSdkCondition matchesSdkCondition) {
return newRecord(
matchesSdkCondition.getClass(),
ExpressionDef.constant(matchesSdkCondition.sdk()),
ExpressionDef.constant(matchesSdkCondition.version())
);
} else if (condition instanceof MatchesDynamicCondition matchesDynamicCondition) {
return newRecord(
matchesDynamicCondition.getClass(),
getAnnotationMetadataExpression(matchesDynamicCondition.annotationMetadata())
);
} else {
throw new IllegalStateException("Unsupported condition type: " + condition.getClass().getName());
}
}
private ExpressionDef getAnnotationClassValues(AnnotationClassValue<?>[] classValues) {
return ClassTypeDef.of(AnnotationClassValue.class)
.array()
.instantiate(Arrays.stream(classValues).map(this::getAnnotationClassValue).toList());
}
private ExpressionDef getAnnotationClassValue(AnnotationClassValue<?> annotationClassValue) {
return loadClassValueExpressionFn.apply(annotationClassValue.getName());
}
private ExpressionDef newRecord(Class<?> classType, ExpressionDef... values) {
return ClassTypeDef.of(classType).instantiate(classType.getConstructors()[0], values);
}
};
TypeDef.Array conditionsArrayType = ClassTypeDef.of(Condition.class).array();
return StatementDef.multi(
beanDefinitionTypeDef.getStaticField(preStartConditionsField).put(
conditionsArrayType.instantiate(preConditions.stream().map(writer).toList())
),
beanDefinitionTypeDef.getStaticField(postStartConditionsField).put(
conditionsArrayType.instantiate(postConditions.stream().map(writer).toList())
)
);
}
private void processAllBeanElementVisitors() {
for (BeanElementVisitor<?> visitor : VISITORS) {
if (visitor.isEnabled() && visitor.supports(this)) {
try {
this.disabled = visitor.visitBeanElement(this, visitorContext) == null;
if (disabled) {
break;
}
} catch (Exception e) {
visitorContext.fail(
"Error occurred visiting BeanElementVisitor of type [" + visitor.getClass().getName() + "]: " + e.getMessage(),
this
);
break;
}
}
}
}
private StatementDef addInnerConfigurationMethod() {
if (isConfigurationProperties && !beanTypeInnerClasses.isEmpty()) {
FieldDef innerClassesField = FieldDef.builder(FIELD_INNER_CLASSES, Set.class)
.addModifiers(Modifier.PRIVATE, Modifier.FINAL, Modifier.STATIC)
.build();
classDefBuilder.addField(innerClassesField);
classDefBuilder.addMethod(
MethodDef.override(IS_INNER_CONFIGURATION_METHOD)
.build((aThis, methodParameters) -> aThis.type().getStaticField(innerClassesField)
.invoke(CONTAINS_METHOD, methodParameters.get(0))
.returning())
);
return beanDefinitionTypeDef.getStaticField(innerClassesField).put(
getClassesAsSetExpression(beanTypeInnerClasses.toArray(EMPTY_STRING_ARRAY))
);
}
return StatementDef.multi();
}
private StatementDef addGetExposedTypes() {
if (annotationMetadata.hasDeclaredAnnotation(Bean.class.getName())) {
final String[] exposedTypes = annotationMetadata.stringValues(Bean.class.getName(), "typed");
if (exposedTypes.length > 0) {
FieldDef exposedTypesField = FieldDef.builder(FIELD_EXPOSED_TYPES, Set.class)
.addModifiers(Modifier.PRIVATE, Modifier.FINAL, Modifier.STATIC)
.build();
classDefBuilder.addField(exposedTypesField);
classDefBuilder.addMethod(
MethodDef.override(GET_EXPOSED_TYPES_METHOD)
.build((aThis, methodParameters) -> aThis.type().getStaticField(exposedTypesField).returning())
);
return beanDefinitionTypeDef.getStaticField(exposedTypesField).put(getClassesAsSetExpression(exposedTypes));
}
}
return StatementDef.multi();
}
@Nullable
private MethodDef getGetOrder() {
int order = OrderUtil.getOrder(annotationMetadata);
if (order != 0) {
return MethodDef.override(GET_ORDER_METHOD)
.build((aThis, methodParameters) -> TypeDef.Primitive.INT.constant(order).returning());
}
return null;
}
private ExpressionDef getClassesAsSetExpression(String[] classes) {
if (classes.length > 1) {
return ClassTypeDef.of(HashSet.class)
.instantiate(
HASH_SET_COLLECTION_CONSTRUCTOR,
ClassTypeDef.of(Arrays.class)
.invokeStatic(
ARRAYS_AS_LIST_METHOD,
getArrayOfClasses(classes)
)
);
}
return ClassTypeDef.of(Collections.class)
.invokeStatic(
COLLECTIONS_SINGLETON_METHOD,
asClassExpression(classes[0])
);
}
private boolean hasTypeArguments() {
return typeArguments != null && !typeArguments.isEmpty() && typeArguments.entrySet().stream().anyMatch(e -> !e.getValue().isEmpty());
}
private boolean isSingleton(String scope) {
if (beanProducingElement instanceof FieldElement && beanProducingElement.isFinal()) {
// final fields can't change so effectively singleton
return true;
}
if (scope != null) {
return scope.equals(Singleton.class.getName());
} else {
final AnnotationMetadata annotationMetadata;
if (beanProducingElement instanceof ClassElement) {
annotationMetadata = getAnnotationMetadata();
} else {
annotationMetadata = beanProducingElement.getDeclaredMetadata();
}
return annotationMetadata.stringValue(DefaultScope.class)
.map(t -> t.equals(Singleton.class.getName()))
.orElse(false);
}
}
/**
* @return The bytes of the class
*/
public byte[] toByteArray() {
if (!beanFinalized) {
throw new IllegalStateException("Bean definition not finalized. Call visitBeanDefinitionEnd() first.");
}
return new ByteCodeWriter().write(classDefBuilder.build());
}
@Override
public void accept(ClassWriterOutputVisitor visitor) throws IOException {
if (disabled) {
return;
}
visitor.visitServiceDescriptor(
BeanDefinitionReference.class,
beanDefinitionName,
getOriginatingElement()
);
write(visitor, classDefBuilder.build());
try {
if (executableMethodsDefinitionWriter != null) {
executableMethodsDefinitionWriter.accept(visitor);
}
} catch (RuntimeException e) {
Throwable cause = e.getCause();
if (cause instanceof IOException exception) {
throw exception;
} else {
throw e;
}
}
evaluatedExpressionProcessor.writeEvaluatedExpressions(visitor);
}
private void write(ClassWriterOutputVisitor visitor, ObjectDef objectDef) throws IOException {
try (OutputStream out = visitor.visitClass(objectDef.getName(), getOriginatingElements())) {
out.write(new ByteCodeWriter().write(objectDef));
}
for (ObjectDef innerType : objectDef.getInnerTypes()) {
write(visitor, innerType);
}
}
@Override
public void visitSetterValue(
TypedElement declaringType,
MethodElement methodElement,
AnnotationMetadata annotationMetadata,
boolean requiresReflection,
boolean isOptional) {
injectCommands.add(new SetterInjectionInjectCommand(declaringType, methodElement, annotationMetadata, requiresReflection, isOptional));
}
private StatementDef setSetterValue(InjectMethodSignature injectMethodSignature,
TypedElement declaringType,
MethodElement methodElement,
AnnotationMetadata annotationMetadata,
boolean requiresReflection,
boolean isOptional) {
if (!requiresReflection) {
ParameterElement parameter = methodElement.getParameters()[0];
StatementDef setValueStatement = setSetterValue(injectMethodSignature, declaringType, methodElement, annotationMetadata, parameter);
if (isOptional) {
return getPropertyContainsCheck(
injectMethodSignature,
parameter.getType(),
parameter.getName(),
annotationMetadata
).ifTrue(setValueStatement);
}
return setValueStatement;
}
final MethodVisitData methodVisitData = new MethodVisitData(
declaringType,
methodElement,
false,
annotationMetadata);
methodInjectionPoints.add(methodVisitData);
allMethodVisits.add(methodVisitData);
return StatementDef.multi();
}
private StatementDef setSetterValue(InjectMethodSignature injectMethodSignature,
TypedElement declaringType,
MethodElement methodElement,
AnnotationMetadata annotationMetadata,
ParameterElement parameter) {
ClassElement genericType = parameter.getGenericType();
if (isConfigurationProperties && isValueType(annotationMetadata)) {
int methodIndex = -1;
if (keepConfPropInjectPoints) {
final MethodVisitData methodVisitData = new MethodVisitData(
declaringType,
methodElement,
false,
annotationMetadata);
methodInjectionPoints.add(methodVisitData);
allMethodVisits.add(methodVisitData);
methodIndex = allMethodVisits.size() - 1;
}
Function<ExpressionDef, StatementDef> onValue = value -> injectMethodSignature
.instanceVar.invoke(methodElement, value);
Optional<String> valueValue = annotationMetadata.stringValue(Value.class);
if (isInnerType(genericType)) {
boolean isArray = genericType.isArray();
boolean isCollection = genericType.isAssignable(Collection.class);
if (isCollection || isArray) {
ClassElement typeArgument = genericType.isArray() ? genericType.fromArray() : genericType.getFirstTypeArgument().orElse(null);
if (typeArgument != null && !typeArgument.isPrimitive()) {
return getInvokeGetBeansOfTypeForSetter(injectMethodSignature, methodElement.getName(), parameter, annotationMetadata, onValue, methodIndex);
}
return onValue.apply(
getInvokeGetBeanForSetter(injectMethodSignature, methodElement.getName(), parameter, annotationMetadata, methodIndex)
);
}
return onValue.apply(
getInvokeGetBeanForSetter(injectMethodSignature, methodElement.getName(), parameter, annotationMetadata, methodIndex)
);
}
Optional<String> property = annotationMetadata.stringValue(Property.class, "name");
if (property.isPresent()) {
return onValue.apply(
getInvokeGetPropertyValueForSetter(injectMethodSignature, methodElement.getName(), parameter, property.get(), annotationMetadata, methodIndex)
);
}
if (valueValue.isPresent()) {
return onValue.apply(
getInvokeGetPropertyPlaceholderValueForSetter(injectMethodSignature, methodElement.getName(), parameter, valueValue.get(), annotationMetadata, methodIndex)
);
}
throw new IllegalStateException();
} else {
final MethodVisitData methodVisitData = new MethodVisitData(
declaringType,
methodElement,
false,
annotationMetadata);
methodInjectionPoints.add(methodVisitData);
allMethodVisits.add(methodVisitData);
return injectMethod(
methodElement,
false,
injectMethodSignature.aThis,
injectMethodSignature.methodParameters,
injectMethodSignature.instanceVar,
allMethodVisits.size() - 1
);
}
}
@Override
public void visitPostConstructMethod(TypedElement declaringType,
MethodElement methodElement,
boolean requiresReflection,
VisitorContext visitorContext) {
buildMethodDefinition.postConstruct(false);
// for "super bean definitions" we just delegate to super
if (!superBeanDefinition || isInterceptedLifeCycleByType(this.annotationMetadata, "POST_CONSTRUCT")) {
MethodVisitData methodVisitData = new MethodVisitData(declaringType, methodElement, requiresReflection, methodElement.getAnnotationMetadata(), true, false);
postConstructMethodVisits.add(methodVisitData);
allMethodVisits.add(methodVisitData);
buildMethodDefinition.postConstruct.injectionPoints.add(new
InjectMethodBuildCommand(
declaringType,
methodElement,
requiresReflection,
allMethodVisits.size() - 1
)
);
}
}
@Override
public void visitPreDestroyMethod(TypedElement declaringType,
MethodElement methodElement,
boolean requiresReflection,
VisitorContext visitorContext) {
// for "super bean definitions" we just delegate to super
if (!superBeanDefinition || isInterceptedLifeCycleByType(this.annotationMetadata, "PRE_DESTROY")) {
buildMethodDefinition.preDestroy(false);
MethodVisitData methodVisitData = new MethodVisitData(declaringType, methodElement, requiresReflection, methodElement.getAnnotationMetadata(), false, true);
preDestroyMethodVisits.add(methodVisitData);
allMethodVisits.add(methodVisitData);
buildMethodDefinition.preDestroy.injectionPoints.add(new InjectMethodBuildCommand(
declaringType,
methodElement,
requiresReflection,
allMethodVisits.size() - 1
));
}
}
@Override
public void visitMethodInjectionPoint(TypedElement declaringType,
MethodElement methodElement,
boolean requiresReflection,
VisitorContext visitorContext) {
MethodVisitData methodVisitData = new MethodVisitData(declaringType, methodElement, requiresReflection, methodElement.getAnnotationMetadata());
evaluatedExpressionProcessor.processEvaluatedExpressions(methodElement.getAnnotationMetadata(), this.beanTypeElement);
methodInjectionPoints.add(methodVisitData);
allMethodVisits.add(methodVisitData);
injectCommands.add(new InjectMethodInjectCommand(
declaringType,
methodElement,
requiresReflection,
visitorContext,
allMethodVisits.size() - 1)
);
}
@Override
public int visitExecutableMethod(TypedElement declaringBean,
MethodElement methodElement, VisitorContext visitorContext) {
return visitExecutableMethod(
declaringBean,
methodElement,
null,
null
);
}
/**
* Visit a method that is to be made executable allow invocation of said method without reflection.
*
* @param declaringType The declaring type of the method. Either a Class or a string representing the
* name of the type
* @param methodElement The method element
* @param interceptedProxyClassName The intercepted proxy class name
* @param interceptedProxyBridgeMethodName The intercepted proxy bridge method name
* @return The index of a new method.
*/
public int visitExecutableMethod(TypedElement declaringType,
MethodElement methodElement,
String interceptedProxyClassName,
String interceptedProxyBridgeMethodName) {
if (executableMethodsDefinitionWriter == null) {
executableMethodsDefinitionWriter = new ExecutableMethodsDefinitionWriter(
evaluatedExpressionProcessor,
annotationMetadata,
beanDefinitionName,
getBeanDefinitionName(),
originatingElements
);
}
return executableMethodsDefinitionWriter.visitExecutableMethod(declaringType, methodElement, interceptedProxyClassName, interceptedProxyBridgeMethodName);
}
@Override
public String toString() {
return "BeanDefinitionWriter{" +
"beanFullClassName='" + beanFullClassName + '\'' +
'}';
}
@Override
public String getPackageName() {
return packageName;
}
@Override
public String getBeanSimpleName() {
return beanSimpleClassName;
}
@Override
public AnnotationMetadata getAnnotationMetadata() {
return annotationMetadata;
}
@Override
public void visitConfigBuilderField(
ClassElement type,
String field,
AnnotationMetadata annotationMetadata,
ConfigurationMetadataBuilder metadataBuilder,
boolean isInterface) {
ConfigBuilderState state = new ConfigBuilderState(type, field, false, annotationMetadata, isInterface);
configBuilderInjectCommand = new ConfigFieldBuilderInjectCommand(type, field, annotationMetadata, metadataBuilder, isInterface, state, new ArrayList<>());
injectCommands.add(configBuilderInjectCommand);
}
@Override
public void visitConfigBuilderMethod(
ClassElement type,
String methodName,
AnnotationMetadata annotationMetadata,
ConfigurationMetadataBuilder metadataBuilder,
boolean isInterface) {
ConfigBuilderState state = new ConfigBuilderState(type, methodName, true, annotationMetadata, isInterface);
configBuilderInjectCommand = new ConfigMethodBuilderInjectPointCommand(type, methodName, annotationMetadata, metadataBuilder, isInterface, state, new ArrayList<>());
injectCommands.add(configBuilderInjectCommand);
}
@Override
public void visitConfigBuilderDurationMethod(
String propertyName,
ClassElement returnType,
String methodName,
String path) {
configBuilderInjectCommand.builderPoints().add(new ConfigBuilderMethodDurationInjectCommand(propertyName, returnType, methodName, path));
}
@Override
public void visitConfigBuilderMethod(
String propertyName,
ClassElement returnType,
String methodName,
ClassElement paramType,
Map<String, ClassElement> generics,
String path) {
configBuilderInjectCommand.builderPoints().add(new ConfigBuilderMethodInjectCommand(propertyName, returnType, methodName, paramType, generics, path));
}
@Override
public void visitConfigBuilderEnd() {
configBuilderInjectCommand = null;
}
@Override
public void setRequiresMethodProcessing(boolean shouldPreProcess) {
this.preprocessMethods = shouldPreProcess;
}
@Override
public void visitTypeArguments(Map<String, Map<String, ClassElement>> typeArguments) {
this.typeArguments = typeArguments;
}
@Override
public boolean requiresMethodProcessing() {
return this.preprocessMethods;
}
@Override
public void visitFieldInjectionPoint(
TypedElement declaringType,
FieldElement fieldElement,
boolean requiresReflection,
VisitorContext visitorContext) {
injectCommands.add(new InjectFieldInjectCommand(declaringType, fieldElement, requiresReflection));
}
private StatementDef injectField(InjectMethodSignature injectMethodSignature,
TypedElement declaringType,
FieldElement fieldElement,
AnnotationMetadata annotationMetadata,
boolean requiresReflection) {
boolean isRequired = fieldElement
.booleanValue(AnnotationUtil.INJECT, AnnotationUtil.MEMBER_REQUIRED)
.orElse(true);
boolean requiresGenericType = false;
Method methodToInvoke;
final ClassElement genericType = fieldElement.getGenericType();
boolean isArray = genericType.isArray();
boolean isCollection = genericType.isAssignable(Collection.class);
boolean isMap = isInjectableMap(genericType);
if (isMap) {
requiresGenericType = true;
methodToInvoke = GET_MAP_OF_TYPE_FOR_FIELD;
} else if (isCollection || isArray) {
requiresGenericType = true;
ClassElement typeArgument = genericType.isArray() ? genericType.fromArray() : genericType.getFirstTypeArgument().orElse(null);
if (typeArgument != null && !typeArgument.isPrimitive()) {
if (typeArgument.isAssignable(BeanRegistration.class)) {
methodToInvoke = GET_BEAN_REGISTRATIONS_FOR_FIELD;
} else {
methodToInvoke = GET_BEANS_OF_TYPE_FOR_FIELD;
}
} else {
requiresGenericType = false;
methodToInvoke = GET_BEAN_FOR_FIELD;
}
} else if (genericType.isAssignable(Stream.class)) {
requiresGenericType = true;
methodToInvoke = GET_STREAM_OF_TYPE_FOR_FIELD;
} else if (genericType.isAssignable(Optional.class)) {
requiresGenericType = true;
methodToInvoke = FIND_BEAN_FOR_FIELD;
} else if (genericType.isAssignable(BeanRegistration.class)) {
requiresGenericType = true;
methodToInvoke = GET_BEAN_REGISTRATION_FOR_FIELD;
} else {
methodToInvoke = GET_BEAN_FOR_FIELD;
}
return visitFieldInjectionPointInternal(
injectMethodSignature,
declaringType,
fieldElement,
annotationMetadata,
requiresReflection,
methodToInvoke,
isArray,
requiresGenericType,
isRequired
);
}
private static boolean isInjectableMap(ClassElement genericType) {
boolean typeMatches = Stream.of(Map.class, HashMap.class, LinkedHashMap.class, TreeMap.class)
.anyMatch(t -> genericType.getName().equals(t.getName()));
if (typeMatches) {
Map<String, ClassElement> typeArgs = genericType.getTypeArguments();
if (typeArgs.size() == 2) {
ClassElement k = typeArgs.get("K");
return k != null && k.isAssignable(CharSequence.class);
}
}
return false;
}
private boolean isInnerType(ClassElement genericType) {
String type;
if (genericType.isContainerType()) {
type = genericType.getFirstTypeArgument().map(Element::getName).orElse("");
} else if (genericType.isArray()) {
type = genericType.fromArray().getName();
} else {
type = genericType.getName();
}
return beanTypeInnerClasses.contains(type);
}
@Override
public void visitAnnotationMemberPropertyInjectionPoint(TypedElement annotationMemberBeanType,
String annotationMemberProperty,
@Nullable String requiredValue,
@Nullable String notEqualsValue) {
ClassElement annotationMemberClassElement = annotationMemberBeanType.getType();
MethodElement memberPropertyGetter = annotationMemberClassElement.getBeanProperties()
.stream()
.filter(property -> property.getSimpleName().equals(annotationMemberProperty))
.findFirst()
.flatMap(PropertyElement::getReadMethod)
.orElse(null);
if (memberPropertyGetter == null) {
final String[] readPrefixes = annotationMemberBeanType.getAnnotationMetadata()
.getValue(AccessorsStyle.class, "readPrefixes", String[].class)
.orElse(new String[]{AccessorsStyle.DEFAULT_READ_PREFIX});
memberPropertyGetter = annotationMemberClassElement.getEnclosedElement(
ElementQuery.ALL_METHODS
.onlyAccessible(beanTypeElement)
.onlyInstance()
.filter(m -> annotationMemberProperty.equals(NameUtils.getPropertyNameForGetter(m.getName(), readPrefixes)) && !m.hasParameters())
).orElse(null);
}
if (memberPropertyGetter == null) {
visitorContext.fail("Bean property [" + annotationMemberProperty + "] is not available on bean ["
+ annotationMemberBeanType.getName() + "]", annotationMemberBeanType);
} else {
annotationInjectionPoints.computeIfAbsent(annotationMemberClassElement, type -> new ArrayList<>(2))
.add(new AnnotationVisitData(annotationMemberBeanType, annotationMemberProperty, memberPropertyGetter, requiredValue, notEqualsValue));
}
}
@Override
public void visitFieldValue(TypedElement declaringType,
FieldElement fieldElement,
boolean requiresReflection,
boolean isOptional) {
injectCommands.add(new InjectFieldValueInjectCommand(declaringType, fieldElement, requiresReflection, isOptional));
}
private ExpressionDef getInvokeGetPropertyValueForField(InjectMethodSignature injectMethodSignature,
FieldElement fieldElement,
AnnotationMetadata annotationMetadata,
String value,
int fieldIndex) {
annotationMetadata = MutableAnnotationMetadata.of(annotationMetadata);
removeAnnotations(annotationMetadata, PropertySource.class.getName(), Property.class.getName());
return injectMethodSignature.aThis
.invoke(
GET_PROPERTY_VALUE_FOR_FIELD,
injectMethodSignature.beanResolutionContext,
injectMethodSignature.beanContext,
getFieldArgument(fieldElement, annotationMetadata, fieldIndex),
ExpressionDef.constant(value),
ExpressionDef.constant(getCliPrefix(fieldElement.getName()))
).cast(TypeDef.erasure(fieldElement.getType()));
}
private ExpressionDef getInvokeGetPropertyPlaceholderValueForField(InjectMethodSignature injectMethodSignature,
FieldElement fieldElement,
AnnotationMetadata annotationMetadata,
String value,
int fieldIndex) {
annotationMetadata = MutableAnnotationMetadata.of(annotationMetadata);
removeAnnotations(annotationMetadata, PropertySource.class.getName(), Property.class.getName());
return injectMethodSignature.aThis
.invoke(
GET_PROPERTY_PLACEHOLDER_VALUE_FOR_FIELD,
injectMethodSignature.beanResolutionContext,
injectMethodSignature.beanContext,
getFieldArgument(fieldElement, annotationMetadata, fieldIndex),
ExpressionDef.constant(value)
).cast(TypeDef.erasure(fieldElement.getType()));
}
private StatementDef visitConfigBuilderMethodInternal(
InjectMethodSignature injectMethodSignature,
String propertyName,
ClassElement returnType,
String methodName,
ClassElement paramType,
Map<String, ClassElement> generics,
boolean isDurationWithTimeUnit,
String propertyPath,
VariableDef builderVar) {
boolean zeroArgs = paramType == null;
// Optional optional = AbstractBeanDefinition.getValueForPath(...)
return getGetValueForPathCall(injectMethodSignature, paramType, propertyName, propertyPath, zeroArgs, generics)
.newLocal("optional" + NameUtils.capitalize(propertyPath.replace('.', '_')), optionalVar -> {
return optionalVar.invoke(OPTIONAL_IS_PRESENT_METHOD)
.ifTrue(
optionalVar.invoke(OPTIONAL_GET_METHOD).newLocal("value", valueVar -> {
if (zeroArgs) {
return valueVar.cast(boolean.class).ifTrue(
StatementDef.doTry(
builderVar.invoke(methodName, TypeDef.erasure(returnType))
).doCatch(NoSuchMethodError.class, exceptionVar -> StatementDef.multi())
);
}
List<ExpressionDef> values = new ArrayList<>();
List<TypeDef> parameterTypes = new ArrayList<>();
if (isDurationWithTimeUnit) {
parameterTypes.add(TypeDef.Primitive.LONG);
ClassTypeDef timeInitType = ClassTypeDef.of(TimeUnit.class);
parameterTypes.add(timeInitType);
values.add(
valueVar.cast(ClassTypeDef.of(Duration.class))
.invoke(DURATION_TO_MILLIS_METHOD)
);
values.add(
timeInitType.getStaticField("MILLISECONDS", timeInitType)
);
} else {
TypeDef paramTypeDef = TypeDef.erasure(paramType);
parameterTypes.add(paramTypeDef);
values.add(valueVar.cast(paramTypeDef));
}
return StatementDef.doTry(
builderVar.invoke(methodName, parameterTypes, TypeDef.erasure(returnType), values)
).doCatch(NoSuchMethodError.class, exceptionVar -> StatementDef.multi());
})
);
});
}
private ExpressionDef getGetValueForPathCall(InjectMethodSignature injectMethodSignature,
ClassElement propertyType,
String propertyName,
String propertyPath,
boolean zeroArgs,
Map<String, ClassElement> generics) {
return injectMethodSignature.aThis
.invoke(
GET_VALUE_FOR_PATH,
injectMethodSignature.beanResolutionContext,
injectMethodSignature.beanContext,
zeroArgs ? ClassTypeDef.of(Argument.class).invokeStatic(
ArgumentExpUtils.METHOD_CREATE_ARGUMENT_SIMPLE,
ExpressionDef.constant(TypeDef.of(Boolean.class)),
ExpressionDef.constant("factory")
) : ArgumentExpUtils.buildArgumentWithGenerics(
annotationMetadata,
beanDefinitionTypeDef,
propertyName,
propertyType,
generics,
new HashSet<>(),
loadClassValueExpressionFn
),
ExpressionDef.constant(propertyPath)
);
}
@Internal
private ExpressionDef getValueBypassingBeanContext(ClassElement type, List<VariableDef.MethodParameter> methodParameters) {
// Used in instantiate and inject methods
if (type.isAssignable(BeanResolutionContext.class)) {
return methodParameters.get(INSTANTIATE_METHOD_BEAN_RESOLUTION_CONTEXT_PARAM);
}
if (type.isAssignable(BeanContext.class)) {
return methodParameters.get(INSTANTIATE_METHOD_BEAN_CONTEXT_PARAM);
}
if (visitorContext.getClassElement(ConversionService.class).orElseThrow().equals(type)) {
// We only want to assign to exact `ConversionService` classes not to classes extending `ConversionService`
return methodParameters.get(INSTANTIATE_METHOD_BEAN_CONTEXT_PARAM)
.invoke(METHOD_BEAN_CONTEXT_GET_CONVERSION_SERVICE);
}
if (type.isAssignable(ConfigurationPath.class)) {
return methodParameters.get(INSTANTIATE_METHOD_BEAN_RESOLUTION_CONTEXT_PARAM)
.invoke(GET_CONFIGURATION_PATH_METHOD);
}
return null;
}
private StatementDef visitFieldInjectionPointInternal(InjectMethodSignature injectMethodSignature,
TypedElement declaringType,
FieldElement fieldElement,
AnnotationMetadata annotationMetadata,
boolean requiresReflection,
Method methodToInvoke,
boolean isArray,
boolean requiresGenericType,
boolean isRequired) {
evaluatedExpressionProcessor.processEvaluatedExpressions(annotationMetadata, null);
autoApplyNamedIfPresent(fieldElement, annotationMetadata);
fieldInjectionPoints.add(new FieldVisitData(declaringType, fieldElement, annotationMetadata, requiresReflection));
int fieldIndex = fieldInjectionPoints.size() - 1;
ExpressionDef valueExpression = getValueBypassingBeanContext(fieldElement.getGenericField(), injectMethodSignature.methodParameters);
if (valueExpression == null) {
List<ExpressionDef> valueExpressions = new ArrayList<>(
List.of(
injectMethodSignature.beanResolutionContext,
injectMethodSignature.beanContext,
ExpressionDef.constant(fieldIndex)
)
);
if (requiresGenericType) {
valueExpressions.add(
resolveFieldArgumentGenericType(fieldElement.getGenericType(), fieldIndex)
);
}
valueExpressions.add(
getQualifier(fieldElement, resolveFieldArgument(fieldIndex))
);
valueExpression = injectMethodSignature.aThis
.invoke(methodToInvoke, valueExpressions);
if (isArray && requiresGenericType) {
valueExpression = convertToArray(fieldElement.getType().fromArray(), valueExpression);
}
valueExpression = valueExpression.cast(TypeDef.erasure(fieldElement.getType()));
}
if (!isRequired) {
return valueExpression.newLocal("value", valueVar ->
valueVar.ifNonNull(
putField(fieldElement, requiresReflection, injectMethodSignature, valueVar, fieldIndex)
));
}
return putField(fieldElement, requiresReflection, injectMethodSignature, valueExpression, fieldIndex);
}
private StatementDef putField(FieldElement fieldElement,
boolean requiresReflection,
InjectMethodSignature injectMethodSignature,
ExpressionDef valueExpression,
int fieldIndex) {
if (requiresReflection) {
return injectMethodSignature.aThis
.invoke(
SET_FIELD_WITH_REFLECTION_METHOD,
injectMethodSignature.beanResolutionContext,
injectMethodSignature.beanContext,
ExpressionDef.constant(fieldIndex),
injectMethodSignature.instanceVar,
valueExpression
);
}
return injectMethodSignature.instanceVar.field(fieldElement).put(valueExpression);
}
private ExpressionDef getPropertyContainsCheck(InjectMethodSignature injectMethodSignature,
ClassElement propertyType,
String propertyName,
AnnotationMetadata annotationMetadata) {
String propertyValue = annotationMetadata.stringValue(Property.class, "name").orElse(propertyName);
ExpressionDef.InvokeInstanceMethod containsProperty = injectMethodSignature.aThis.invoke(
isMultiValueProperty(propertyType) ? CONTAINS_PROPERTIES_VALUE_METHOD : CONTAINS_PROPERTY_VALUE_METHOD,
injectMethodSignature.beanResolutionContext,
injectMethodSignature.beanContext,
ExpressionDef.constant(propertyValue) // property name
);
String cliProperty = getCliPrefix(propertyName);
if (cliProperty == null) {
return containsProperty.isTrue();
}
return containsProperty.isTrue().or(
injectMethodSignature.aThis.invoke(
CONTAINS_PROPERTY_VALUE_METHOD,
injectMethodSignature.beanResolutionContext,
injectMethodSignature.beanContext,
ExpressionDef.constant(cliProperty) // property name
).isTrue()
);
}
private String getCliPrefix(String propertyName) {
if (isConfigurationProperties && this.annotationMetadata.isPresent(ConfigurationProperties.class, "cliPrefix")) {
return this.annotationMetadata.stringValue(ConfigurationProperties.class, "cliPrefix").map(val -> val + propertyName).orElse(null);
}
return null;
}
private boolean isMultiValueProperty(ClassElement type) {
return type.isAssignable(Map.class) || type.isAssignable(Collection.class) || isConfigurationProperties(type);
}
private ExpressionDef getQualifier(Element element, ExpressionDef argumentExpression) {
return getQualifier(element, () -> argumentExpression);
}
private ExpressionDef getQualifier(Element element, Supplier<ExpressionDef> argumentExpressionSupplier) {
final List<String> qualifierNames = element.getAnnotationNamesByStereotype(AnnotationUtil.QUALIFIER);
if (!qualifierNames.isEmpty()) {
if (qualifierNames.size() == 1) {
// simple qualifier
final String annotationName = qualifierNames.iterator().next();
return getQualifierForAnnotation(element, annotationName, argumentExpressionSupplier.get());
}
// composite qualifier
return TYPE_QUALIFIERS.invokeStatic(
METHOD_QUALIFIER_BY_QUALIFIERS,
TYPE_QUALIFIER.array().instantiate(
qualifierNames.stream().map(name -> getQualifierForAnnotation(element, name, argumentExpressionSupplier.get())).toList()
)
);
}
if (element.hasAnnotation(AnnotationUtil.ANN_INTERCEPTOR_BINDING_QUALIFIER)) {
return TYPE_QUALIFIERS.invokeStatic(
METHOD_QUALIFIER_BY_INTERCEPTOR_BINDING,
getAnnotationMetadataFromProvider(argumentExpressionSupplier.get())
);
}
String[] byType = element.hasDeclaredAnnotation(io.micronaut.context.annotation.Type.NAME) ? element.stringValues(io.micronaut.context.annotation.Type.NAME) : null;
if (byType != null && byType.length > 0) {
return TYPE_QUALIFIERS.invokeStatic(
METHOD_QUALIFIER_BY_TYPE,
TypeDef.CLASS.array().instantiate(Arrays.stream(byType).map(this::asClassExpression).toList())
);
}
return ExpressionDef.nullValue();
}
private ExpressionDef getAnnotationMetadataFromProvider(ExpressionDef argumentExpression) {
return argumentExpression.invoke(PROVIDER_GET_ANNOTATION_METADATA_METHOD);
}
private ExpressionDef getQualifierForAnnotation(Element element,
String annotationName,
ExpressionDef argumentExpression) {
if (annotationName.equals(Primary.NAME)) {
// primary is the same as no qualifier
return ExpressionDef.nullValue();
}
if (annotationName.equals(AnnotationUtil.NAMED)) {
final String n = element.stringValue(AnnotationUtil.NAMED).orElse(element.getName());
if (!n.contains("$")) {
return TYPE_QUALIFIERS.invokeStatic(METHOD_QUALIFIER_BY_NAME, ExpressionDef.constant(n));
}
return TYPE_QUALIFIERS.invokeStatic(METHOD_QUALIFIER_FOR_ARGUMENT, argumentExpression);
}
if (annotationName.equals(Any.NAME)) {
return ClassTypeDef.of(AnyQualifier.class).getStaticField("INSTANCE", ClassTypeDef.of(AnyQualifier.class));
}
final String repeatableContainerName = element.findRepeatableAnnotation(annotationName).orElse(null);
if (repeatableContainerName != null) {
return TYPE_QUALIFIERS.invokeStatic(
METHOD_QUALIFIER_BY_REPEATABLE_ANNOTATION,
getAnnotationMetadataFromProvider(argumentExpression),
ExpressionDef.constant(repeatableContainerName)
);
}
return TYPE_QUALIFIERS.invokeStatic(
METHOD_QUALIFIER_BY_ANNOTATION,
getAnnotationMetadataFromProvider(argumentExpression),
ExpressionDef.constant(annotationName)
);
}
private ExpressionDef getArrayOfClasses(String[] byType) {
return TypeDef.CLASS.array().instantiate(Arrays.stream(byType).map(this::asClassExpression).toList());
}
private ExpressionDef.Constant asClassExpression(String type) {
return ExpressionDef.constant(TypeDef.of(type));
}
private ExpressionDef convertToArray(ClassElement arrayType, ExpressionDef value) {
return value
.cast(TypeDef.of(Collection.class))
.invoke(COLLECTION_TO_ARRAY, ClassTypeDef.of(arrayType).array().instantiate());
}
private void autoApplyNamedIfPresent(Element element, AnnotationMetadata annotationMetadata) {
if (annotationMetadata.hasAnnotation(AnnotationUtil.NAMED) || annotationMetadata.hasStereotype(AnnotationUtil.NAMED)) {
autoApplyNamed(element);
}
}
private void autoApplyNamed(Element element) {
if (element.stringValue(AnnotationUtil.NAMED).isEmpty()) {
element.annotate(AnnotationUtil.NAMED, (builder) -> {
final String name;
if (element instanceof ClassElement) {
name = NameUtils.decapitalize(element.getSimpleName());
} else {
if (element instanceof MethodElement) {
final String n = element.getName();
if (NameUtils.isGetterName(n)) {
name = NameUtils.getPropertyNameForGetter(n);
} else {
name = n;
}
} else {
name = element.getName();
}
}
builder.value(name);
});
}
}
private StatementDef injectMethod(MethodElement methodElement,
boolean requiresReflection,
VariableDef.This aThis,
List<VariableDef.MethodParameter> parameters,
VariableDef instanceVar,
int methodIndex) {
final List<ParameterElement> argumentTypes = Arrays.asList(methodElement.getParameters());
applyDefaultNamedToParameters(argumentTypes);
for (ParameterElement value : argumentTypes) {
evaluatedExpressionProcessor.processEvaluatedExpressions(value.getAnnotationMetadata(), null);
}
return injectStatement(aThis, parameters, methodElement, requiresReflection, instanceVar, methodIndex);
}
private StatementDef injectStatement(VariableDef.This aThis,
List<VariableDef.MethodParameter> parameters,
MethodElement methodElement,
boolean requiresReflection,
VariableDef instanceVar,
int methodIndex) {
final List<ParameterElement> argumentTypes = Arrays.asList(methodElement.getParameters());
boolean isRequiredInjection = InjectionPoint.isInjectionRequired(methodElement);
List<ExpressionDef> invocationValues = IntStream.range(0, argumentTypes.size())
.mapToObj(index -> getBeanForMethodParameter(aThis, parameters, index, argumentTypes.get(index), methodIndex))
.toList();
if (!isRequiredInjection && methodElement.hasParameters()) {
// store parameter values in local object[]
return TypeDef.OBJECT.array().instantiate(invocationValues).newLocal("values", valuesVar -> {
// invoke isMethodResolved with method parameters
List<? extends ExpressionDef> values = IntStream.range(0, argumentTypes.size())
.mapToObj(index -> valuesVar.arrayElement(index).cast(TypeDef.erasure(argumentTypes.get(index).getType())))
.toList();
return aThis.invoke(
IS_METHOD_RESOLVED,
ExpressionDef.constant(methodIndex),
valuesVar
).ifTrue(
instanceVar.invoke(methodElement, values)
);
});
}
if (!requiresReflection) {
return instanceVar.invoke(methodElement, invocationValues);
}
return aThis.invoke(
INVOKE_WITH_REFLECTION_METHOD,
parameters.get(INJECT_METHOD_BEAN_RESOLUTION_CONTEXT_PARAM),
parameters.get(INJECT_METHOD_BEAN_CONTEXT_PARAM),
ExpressionDef.constant(methodIndex),
instanceVar,
TypeDef.OBJECT.array().instantiate(invocationValues)
);
}
private StatementDef destroyInjectScopeBeansIfNecessary(List<VariableDef.MethodParameter> parameters) {
return parameters.get(0).invoke(DESTROY_INJECT_SCOPED_BEANS_METHOD);
}
private ExpressionDef getBeanForMethodParameter(VariableDef.This aThis,
List<VariableDef.MethodParameter> methodParameters,
int i,
ParameterElement entry,
int methodIndex) {
AnnotationMetadata argMetadata = entry.getAnnotationMetadata();
ExpressionDef expressionDef = getValueBypassingBeanContext(entry.getGenericType(), methodParameters);
if (expressionDef != null) {
return expressionDef;
}
boolean requiresGenericType = false;
final ClassElement genericType = entry.getGenericType();
Method methodToInvoke;
boolean isCollection = genericType.isAssignable(Collection.class);
boolean isMap = isInjectableMap(genericType);
boolean isArray = genericType.isArray();
if (isValueType(argMetadata) && !isInnerType(entry.getGenericType())) {
Optional<String> property = argMetadata.stringValue(Property.class, "name");
if (property.isPresent()) {
return getInvokeGetPropertyValueForMethod(aThis, methodParameters, i, entry, property.get(), methodIndex);
} else {
if (entry.getAnnotationMetadata().getValue(Value.class, EvaluatedExpressionReference.class).isPresent()) {
return getInvokeGetEvaluatedExpressionValueForMethodArgument(aThis, i, entry, methodIndex);
} else {
Optional<String> valueValue = entry.getAnnotationMetadata().stringValue(Value.class);
if (valueValue.isPresent()) {
return getInvokeGetPropertyPlaceholderValueForMethod(aThis, methodParameters, i, entry, valueValue.get(), methodIndex);
}
}
return ExpressionDef.nullValue();
}
} else if (isCollection || isArray) {
requiresGenericType = true;
ClassElement typeArgument = genericType.isArray() ? genericType.fromArray() : genericType.getFirstTypeArgument().orElse(null);
if (typeArgument != null && !typeArgument.isPrimitive()) {
if (typeArgument.isAssignable(BeanRegistration.class)) {
methodToInvoke = GET_BEAN_REGISTRATIONS_FOR_METHOD_ARGUMENT;
} else {
methodToInvoke = GET_BEANS_OF_TYPE_FOR_METHOD_ARGUMENT;
}
} else {
methodToInvoke = GET_BEAN_FOR_METHOD_ARGUMENT;
requiresGenericType = false;
}
} else if (isMap) {
requiresGenericType = true;
methodToInvoke = GET_MAP_OF_TYPE_FOR_METHOD_ARGUMENT;
} else if (genericType.isAssignable(Stream.class)) {
requiresGenericType = true;
methodToInvoke = GET_STREAM_OF_TYPE_FOR_METHOD_ARGUMENT;
} else if (genericType.isAssignable(Optional.class)) {
requiresGenericType = true;
methodToInvoke = FIND_BEAN_FOR_METHOD_ARGUMENT;
} else if (genericType.isAssignable(BeanRegistration.class)) {
requiresGenericType = true;
methodToInvoke = GET_BEAN_REGISTRATION_FOR_METHOD_ARGUMENT;
} else {
methodToInvoke = GET_BEAN_FOR_METHOD_ARGUMENT;
}
List<ExpressionDef> values = new ArrayList<>(
List.of(
// 1st argument load BeanResolutionContext
methodParameters.get(0),
// 2nd argument load BeanContext
methodParameters.get(1),
// 3rd argument the method index
ExpressionDef.constant(methodIndex),
// 4th argument the argument index
ExpressionDef.constant(i)
)
);
// invoke getBeanForField
if (requiresGenericType) {
values.add(
resolveMethodArgumentGenericType(genericType, methodIndex, i)
);
}
ExpressionDef argumentExpression = resolveMethodArgument(methodIndex, i);
values.add(
getQualifier(entry, argumentExpression)
);
ExpressionDef result = aThis.invoke(methodToInvoke, values);
if (isArray && requiresGenericType) {
result = convertToArray(genericType.fromArray(), result);
}
// cast the return value to the correct type
return result.cast(TypeDef.erasure(entry.getType()));
}
private ExpressionDef getInvokeGetPropertyValueForMethod(VariableDef.This aThis,
List<VariableDef.MethodParameter> methodParameters,
int i,
ParameterElement entry,
String value,
int methodIndex) {
return aThis.invoke(
GET_PROPERTY_VALUE_FOR_METHOD_ARGUMENT,
// 1st argument load BeanResolutionContext
methodParameters.get(0),
// 2nd argument load BeanContext
methodParameters.get(1),
// 3rd argument the method index
ExpressionDef.constant(methodIndex),
// 4th argument the argument index
ExpressionDef.constant(i),
// 5th property value
ExpressionDef.constant(value),
// 6 cli property name
ExpressionDef.constant(getCliPrefix(entry.getName()))
).cast(TypeDef.erasure(entry.getType()));
}
private ExpressionDef getInvokeGetEvaluatedExpressionValueForMethodArgument(VariableDef.This aThis,
int i,
ParameterElement entry,
int methodIndex) {
return aThis.invoke(
GET_EVALUATED_EXPRESSION_VALUE_FOR_METHOD_ARGUMENT,
// 1st argument the method index
ExpressionDef.constant(methodIndex),
// 2nd argument the argument index
ExpressionDef.constant(i)
).cast(TypeDef.erasure(entry.getType()));
}
private ExpressionDef getInvokeGetPropertyPlaceholderValueForMethod(VariableDef.This aThis,
List<VariableDef.MethodParameter> methodParameters,
int i,
ParameterElement entry,
String value,
int methodIndex) {
return aThis.invoke(
GET_PROPERTY_PLACEHOLDER_VALUE_FOR_METHOD_ARGUMENT,
// 1st argument load BeanResolutionContext
methodParameters.get(0),
// 2nd argument load BeanContext
methodParameters.get(1),
// 3rd argument the method index
ExpressionDef.constant(methodIndex),
// 4th argument the argument index
ExpressionDef.constant(i),
// 5th property value
ExpressionDef.constant(value)
).cast(TypeDef.erasure(entry.getType()));
}
private ExpressionDef getInvokeGetPropertyValueForSetter(InjectMethodSignature injectMethodSignature,
String setterName,
ParameterElement entry,
String value,
AnnotationMetadata annotationMetadata,
int methodIndex) {
annotationMetadata = MutableAnnotationMetadata.of(annotationMetadata);
removeAnnotations(annotationMetadata, PropertySource.class.getName(), Property.class.getName());
return injectMethodSignature.aThis.invoke(
GET_PROPERTY_VALUE_FOR_SETTER,
// 1st argument load BeanResolutionContext
injectMethodSignature.beanResolutionContext,
// 2nd argument load BeanContext
injectMethodSignature.beanContext,
// 3rd argument the method name
ExpressionDef.constant(setterName),
// 4th argument the argument
getMethodArgument(entry, annotationMetadata, methodIndex),
// 5th property value
ExpressionDef.constant(value),
// 6 cli property name
ExpressionDef.constant(getCliPrefix(entry.getName()))
).cast(TypeDef.erasure(entry.getType()));
}
private ExpressionDef getMethodArgument(ParameterElement entry, AnnotationMetadata annotationMetadata, int methodIndex) {
return keepConfPropInjectPoints ? resolveMethodArgument(methodIndex, 0) : ArgumentExpUtils.pushCreateArgument(
this.annotationMetadata,
ClassElement.of(beanFullClassName),
beanDefinitionTypeDef,
entry.getName(),
entry.getGenericType(),
annotationMetadata,
entry.getGenericType().getTypeArguments(),
loadClassValueExpressionFn
);
}
private ExpressionDef getFieldArgument(FieldElement fieldElement, AnnotationMetadata annotationMetadata, int fieldIndex) {
if (!keepConfPropInjectPoints) {
return ArgumentExpUtils.pushCreateArgument(
this.annotationMetadata,
ClassElement.of(beanFullClassName),
beanDefinitionTypeDef,
fieldElement.getName(),
fieldElement.getGenericType(),
annotationMetadata,
fieldElement.getGenericType().getTypeArguments(),
loadClassValueExpressionFn
);
}
return resolveFieldArgument(fieldIndex);
}
private ExpressionDef getInvokeGetBeanForSetter(InjectMethodSignature injectMethodSignature,
String setterName,
ParameterElement entry,
AnnotationMetadata annotationMetadata,
int methodIndex) {
annotationMetadata = MutableAnnotationMetadata.of(annotationMetadata);
removeAnnotations(annotationMetadata, PropertySource.class.getName(), Property.class.getName());
return injectMethodSignature.aThis.invoke(
GET_BEAN_FOR_SETTER,
// 1st argument load BeanResolutionContext
injectMethodSignature.beanResolutionContext,
// 2nd argument load BeanContext
injectMethodSignature.beanContext,
// 3rd argument the method name
ExpressionDef.constant(setterName),
// 4th argument the argument
getMethodArgument(entry, annotationMetadata, methodIndex),
// push qualifier
getQualifier(entry.getGenericType(), getMethodArgument(entry, annotationMetadata, methodIndex))
).cast(TypeDef.erasure(entry.getType()));
}
private StatementDef getInvokeGetBeansOfTypeForSetter(InjectMethodSignature injectMethodSignature,
String setterName,
ParameterElement entry,
AnnotationMetadata annotationMetadata,
Function<ExpressionDef, StatementDef> onValue,
int methodIndex) {
annotationMetadata = MutableAnnotationMetadata.of(annotationMetadata);
removeAnnotations(annotationMetadata, PropertySource.class.getName(), Property.class.getName());
// 4th argument the argument
ClassElement genericType = entry.getGenericType();
return getMethodArgument(entry, annotationMetadata, methodIndex).newLocal("argument", argumentVar -> {
ExpressionDef value = injectMethodSignature.aThis.invoke(
GET_BEANS_OF_TYPE_FOR_SETTER,
// 1st argument load BeanResolutionContext
injectMethodSignature.beanResolutionContext,
// 2nd argument load BeanContext
injectMethodSignature.beanContext,
// 3rd argument the method name
ExpressionDef.constant(setterName),
// 4th argument the argument
argumentVar,
// generic type
resolveGenericType(argumentVar, genericType),
// push qualifier
getQualifier(entry.getGenericType(), argumentVar)
).cast(TypeDef.erasure(entry.getType()));
return onValue.apply(value);
});
}
private ExpressionDef resolveGenericType(VariableDef argumentVar, ClassElement genericType) {
ExpressionDef argumentExpression = resolveArgumentGenericType(genericType);
if (argumentExpression == null) {
argumentExpression = resolveFirstTypeArgument(argumentVar);
return resolveInnerTypeArgumentIfNeeded(argumentExpression, genericType);
}
return argumentExpression;
}
private ExpressionDef getInvokeGetPropertyPlaceholderValueForSetter(InjectMethodSignature injectMethodSignature,
String setterName,
ParameterElement entry,
String value,
AnnotationMetadata annotationMetadata,
int methodIndex) {
annotationMetadata = MutableAnnotationMetadata.of(annotationMetadata);
removeAnnotations(annotationMetadata, PropertySource.class.getName(), Property.class.getName());
return injectMethodSignature.aThis
.invoke(
GET_PROPERTY_PLACEHOLDER_VALUE_FOR_SETTER,
// 1st argument load BeanResolutionContext
injectMethodSignature.beanResolutionContext,
// 2nd argument load BeanContext
injectMethodSignature.beanContext,
// 3rd argument the method name
ExpressionDef.constant(setterName),
// 4th argument the argument
getMethodArgument(entry, annotationMetadata, methodIndex),
// 5th property value
ExpressionDef.constant(value),
// 6 cli property name
ExpressionDef.constant(getCliPrefix(entry.getName())
).cast(TypeDef.erasure(entry.getType())));
}
private void removeAnnotations(AnnotationMetadata annotationMetadata, String... annotationNames) {
if (annotationMetadata instanceof MutableAnnotationMetadata mutableAnnotationMetadata) {
for (String annotation : annotationNames) {
mutableAnnotationMetadata.removeAnnotation(annotation);
}
}
}
private void applyDefaultNamedToParameters(List<ParameterElement> argumentTypes) {
for (ParameterElement parameterElement : argumentTypes) {
final AnnotationMetadata annotationMetadata = parameterElement.getAnnotationMetadata();
autoApplyNamedIfPresent(parameterElement, annotationMetadata);
}
}
@SuppressWarnings("MagicNumber")
private ClassTypeDef createExecutableMethodInterceptor(MethodDef interceptMethod, String name) {
// if there is method interception in place we need to construct an inner executable method class that invokes the "initialize"
// method and apply interception
ClassDef.ClassDefBuilder innerClassBuilder = ClassDef.builder(name)
.synthetic()
.addModifiers(Modifier.FINAL)
.superclass(ClassTypeDef.of(AbstractExecutableMethod.class))
.addAnnotation(Generated.class);
FieldDef fieldBeanDef = FieldDef.builder("$beanDef", beanDefinitionTypeDef)
.addModifiers(Modifier.PRIVATE, Modifier.FINAL)
.build();
FieldDef fieldResContext = FieldDef.builder("$resolutionContext", BeanResolutionContext.class)
.addModifiers(Modifier.PRIVATE, Modifier.FINAL)
.build();
FieldDef fieldBeanContext = FieldDef.builder("$beanContext", BeanContext.class)
.addModifiers(Modifier.PRIVATE, Modifier.FINAL)
.build();
FieldDef fieldBean = FieldDef.builder("$bean", beanTypeDef)
.addModifiers(Modifier.PRIVATE, Modifier.FINAL)
.build();
innerClassBuilder.addField(fieldBeanDef);
innerClassBuilder.addField(fieldResContext);
innerClassBuilder.addField(fieldBeanContext);
innerClassBuilder.addField(fieldBean);
// constructor will be AbstractExecutableMethod(BeanDefinition, BeanResolutionContext, BeanContext, T beanType)
innerClassBuilder.addMethod(
MethodDef.constructor()
.addParameters(beanDefinitionTypeDef)
.addParameters(BeanResolutionContext.class, BeanContext.class)
.addParameters(beanTypeDef)
.build((aThis, methodParameters) -> StatementDef.multi(
aThis.superRef().invokeConstructor(
ABSTRACT_EXECUTABLE_METHOD_CONSTRUCTOR,
ExpressionDef.constant(beanTypeDef),
ExpressionDef.constant(interceptMethod.getName())
),
aThis.field(fieldBeanDef).put(methodParameters.get(0)),
aThis.field(fieldResContext).put(methodParameters.get(1)),
aThis.field(fieldBeanContext).put(methodParameters.get(2)),
aThis.field(fieldBean).put(methodParameters.get(3))
))
);
innerClassBuilder.addMethod(
MethodDef.override(PROVIDER_GET_ANNOTATION_METADATA_METHOD)
.build((aThis, methodParameters) ->
beanDefinitionTypeDef.getStaticField(AnnotationMetadataGenUtils.FIELD_ANNOTATION_METADATA)
.returning())
);
innerClassBuilder.addMethod(
MethodDef.override(METHOD_INVOKE_INTERNAL)
.build((aThis, methodParameters) ->
aThis.field(fieldBeanDef).invoke(interceptMethod,
aThis.field(fieldResContext),
aThis.field(fieldBeanContext),
aThis.field(fieldBean)
).returning())
);
classDefBuilder.addInnerType(innerClassBuilder.build());
return ClassTypeDef.of(beanDefinitionName + "$" + name);
}
private StatementDef interceptAndReturn(VariableDef.This aThis,
List<VariableDef.MethodParameter> methodParameters,
ClassTypeDef innerTypeDef,
Method interceptorMethod) {
ExpressionDef localInstance = methodParameters.get(2).cast(beanTypeDef);
// now instantiate the inner class
ExpressionDef executableMethodInstance = innerTypeDef.instantiate(
// 1st argument: pass outer class instance to constructor
aThis,
// 2nd argument: resolution context
methodParameters.get(0),
// 3rd argument: bean context
methodParameters.get(1),
// 4th argument: bean instance
localInstance
);
// now invoke MethodInterceptorChain.initialize or dispose
return ClassTypeDef.of(MethodInterceptorChain.class)
.invokeStatic(
interceptorMethod,
List.of(
// 1st argument: resolution context
methodParameters.get(0),
// 2nd argument: bean context
methodParameters.get(1),
// 3rd argument: this definition
aThis,
// 4th argument: executable method instance
executableMethodInstance,
// 5th argument: the bean instance
localInstance
)
).cast(beanTypeDef).returning();
}
private void visitBuildFactoryMethodDefinition(ClassElement factoryClass, Element factoryElement, ParameterElement... parameters) {
if (buildMethodDefinition == null) {
buildMethodDefinition = new FactoryBuildMethodDefinition(factoryClass, factoryElement, parameters);
onBuild(factoryElement, parameters);
}
}
private void visitBuildConstructorDefinition(MethodElement constructor, boolean requiresReflection) {
if (buildMethodDefinition == null) {
buildMethodDefinition = new ConstructorBuildMethodDefinition(constructor, requiresReflection);
onBuild(constructor, constructor.getParameters());
}
}
private void onBuild(Element factoryElement, ParameterElement[] parameters) {
evaluatedExpressionProcessor.processEvaluatedExpressions(factoryElement.getAnnotationMetadata(), null);
for (ParameterElement parameterElement : parameters) {
evaluatedExpressionProcessor.processEvaluatedExpressions(parameterElement.getAnnotationMetadata(), null);
}
if (isInterceptedLifeCycleByType(this.annotationMetadata, "POST_CONSTRUCT")) {
buildMethodDefinition.postConstruct(true);
}
if (!superBeanDefinition && isInterceptedLifeCycleByType(this.annotationMetadata, "PRE_DESTROY")) {
buildMethodDefinition.preDestroy(true);
}
}
@Nullable
private StatementDef invokeCheckIfShouldLoadIfNecessary(VariableDef.This aThis, List<VariableDef.MethodParameter> parameters) {
AnnotationValue<Requires> requiresAnnotation = annotationMetadata.getAnnotation(Requires.class);
if (requiresAnnotation != null
&& requiresAnnotation.stringValue(RequiresCondition.MEMBER_BEAN).isPresent()
&& requiresAnnotation.stringValue(RequiresCondition.MEMBER_BEAN_PROPERTY).isPresent()) {
MethodDef checkIfShouldLoad = buildCheckIfShouldLoadMethod();
classDefBuilder.addMethod(
checkIfShouldLoad
);
return aThis.invoke(checkIfShouldLoad, parameters);
}
return StatementDef.multi();
}
private MethodDef buildCheckIfShouldLoadMethod() {
return MethodDef.override(CHECK_IF_SHOULD_LOAD_METHOD)
.build((aThis, methodParameters) -> {
List<ClassElement> injectedTypes = new ArrayList<>(annotationInjectionPoints.keySet());
List<StatementDef> statements = new ArrayList<>();
for (int index = 0; index < injectedTypes.size(); index++) {
ClassElement injectedType = injectedTypes.get(index);
List<AnnotationVisitData> annotationVisitData = annotationInjectionPoints.get(injectedType);
if (annotationVisitData.isEmpty()) {
continue;
}
AnnotationVisitData data = annotationVisitData.get(0);
ExpressionDef beanExpression = getBeanForAnnotation(aThis, methodParameters, index, data.memberBeanType);
if (annotationVisitData.size() == 1) {
statements.add(
checkInjectedBean(aThis, data, beanExpression.invoke(data.memberPropertyGetter))
);
} else {
statements.add(
beanExpression.newLocal("beanInstance" + index, beanInstanceVar -> StatementDef.multi(
annotationVisitData.stream().
<StatementDef>map(d -> checkInjectedBean(
aThis,
d,
beanInstanceVar.invoke(d.memberPropertyGetter)
)
).toList()
))
);
}
}
return StatementDef.multi(statements);
});
}
private ExpressionDef.InvokeInstanceMethod checkInjectedBean(VariableDef.This aThis, AnnotationVisitData data, ExpressionDef valueExpression) {
return aThis
.invoke(
CHECK_INJECTED_BEAN_PROPERTY_VALUE,
ExpressionDef.constant(data.memberPropertyName),
valueExpression,
ExpressionDef.constant(data.requiredValue),
ExpressionDef.constant(data.notEqualsValue)
);
}
private ExpressionDef.Cast getBeanForAnnotation(VariableDef.This aThis,
List<VariableDef.MethodParameter> methodParameters,
int currentTypeIndex,
TypedElement memberType) {
return aThis.invoke(
GET_BEAN_FOR_ANNOTATION,
// 1st argument load BeanResolutionContext
methodParameters.get(0),
// 2nd argument load BeanContext
methodParameters.get(1),
// 3rd argument the injected bean index
ExpressionDef.constant(currentTypeIndex),
// push qualifier
getQualifier(memberType, resolveAnnotationArgument(0))
).cast(TypeDef.erasure(memberType));
}
private ExpressionDef invokeConstructorChain(VariableDef.This aThis,
List<VariableDef.MethodParameter> methodParameters,
ExpressionDef beanConstructor,
ExpressionDef constructorValue,
List<ParameterElement> parameters) {
return ClassTypeDef.of(ConstructorInterceptorChain.class)
.invokeStatic(
METHOD_DESCRIPTOR_CONSTRUCTOR_INSTANTIATE,
// 1st argument: The resolution context
methodParameters.get(0),
// 2nd argument: The bean context
methodParameters.get(1),
// 3rd argument: The interceptors if present
StringUtils.isNotEmpty(interceptedType) ?
constructorValue.arrayElement(AopProxyWriter.findInterceptorsListParameterIndex(parameters)).cast(List.class)
: ExpressionDef.nullValue(),
// 4th argument: the bean definition
aThis,
// 5th argument: The constructor
beanConstructor,
// 6th argument: additional proxy parameters count
interceptedType != null ? ExpressionDef.constant(AopProxyWriter.ADDITIONAL_PARAMETERS_COUNT) : ExpressionDef.constant(0),
// 7th argument: load the Object[] for the parameters
constructorValue
);
}
private boolean isConstructorIntercepted(Element constructor) {
// a constructor is intercepted when this bean is an advised type but not proxied
// and any AROUND_CONSTRUCT annotations are present
AnnotationMetadataHierarchy annotationMetadata = new AnnotationMetadataHierarchy(this.annotationMetadata, constructor.getAnnotationMetadata());
final String interceptType = "AROUND_CONSTRUCT";
// for beans that are @Around(proxyTarget=true) only the constructor of the proxy target should be intercepted. Beans returned from factories are always proxyTarget=true
return isInterceptedLifeCycleByType(annotationMetadata, interceptType);
}
private boolean isInterceptedLifeCycleByType(AnnotationMetadata annotationMetadata, String interceptType) {
return isLifeCycleCache.computeIfAbsent(interceptType, s -> {
if (this.beanTypeElement.isAssignable("io.micronaut.aop.Interceptor")) {
// interceptor beans cannot have lifecycle methods intercepted
return false;
}
final Element originatingElement = getOriginatingElements()[0];
final boolean isFactoryMethod = (originatingElement instanceof MethodElement && !(originatingElement instanceof ConstructorElement));
final boolean isProxyTarget = annotationMetadata.booleanValue(AnnotationUtil.ANN_AROUND, "proxyTarget").orElse(false) || isFactoryMethod;
// for beans that are @Around(proxyTarget = false) only the generated AOP impl should be intercepted
final boolean isAopType = StringUtils.isNotEmpty(interceptedType);
final boolean isConstructorInterceptionCandidate = (isProxyTarget && !isAopType) || (isAopType && !isProxyTarget);
final boolean hasAroundConstruct;
final AnnotationValue<Annotation> interceptorBindings
= annotationMetadata.getAnnotation(AnnotationUtil.ANN_INTERCEPTOR_BINDINGS);
List<AnnotationValue<Annotation>> interceptorBindingAnnotations;
if (interceptorBindings != null) {
interceptorBindingAnnotations = interceptorBindings.getAnnotations(AnnotationMetadata.VALUE_MEMBER);
hasAroundConstruct = interceptorBindingAnnotations
.stream()
.anyMatch(av -> av.stringValue("kind").map(k -> k.equals(interceptType)).orElse(false));
} else {
interceptorBindingAnnotations = Collections.emptyList();
hasAroundConstruct = false;
}
if (isConstructorInterceptionCandidate) {
return hasAroundConstruct;
} else if (hasAroundConstruct) {
AnnotationMetadata typeMetadata = annotationMetadata;
if (!isSuperFactory && typeMetadata instanceof AnnotationMetadataHierarchy hierarchy) {
typeMetadata = hierarchy.getRootMetadata();
final AnnotationValue<Annotation> av =
typeMetadata.getAnnotation(AnnotationUtil.ANN_INTERCEPTOR_BINDINGS);
if (av != null) {
interceptorBindingAnnotations = av.getAnnotations(AnnotationMetadata.VALUE_MEMBER);
} else {
interceptorBindingAnnotations = Collections.emptyList();
}
}
// if no other AOP advice is applied
return interceptorBindingAnnotations
.stream()
.noneMatch(av -> av.stringValue("kind").map(k -> k.equals("AROUND")).orElse(false));
} else {
return false;
}
});
}
private List<? extends ExpressionDef> getConstructorArgumentValues(VariableDef.This aThis,
List<VariableDef.MethodParameter> methodParameters,
List<ParameterElement> parameters,
boolean isParametrized,
Supplier<VariableDef> constructorMethodVarSupplier) {
List<ExpressionDef> values = new ArrayList<>();
for (int i = 0; i < parameters.size(); i++) {
ParameterElement parameter = parameters.get(i);
values.add(
getConstructorArgument(aThis, methodParameters, parameter, i, isParametrized, constructorMethodVarSupplier)
);
}
return values;
}
private static boolean hasInjectScope(ParameterElement[] parameters) {
for (ParameterElement parameter : parameters) {
if (hasInjectScope(parameter)) {
return true;
}
}
return false;
}
private static boolean hasInjectScope(AnnotationMetadata annotationMetadata) {
return annotationMetadata.hasDeclaredAnnotation(InjectScope.class);
}
private ExpressionDef getConstructorArgument(VariableDef.This aThis,
List<VariableDef.MethodParameter> methodParameters,
ParameterElement parameter,
int index,
boolean isParametrized,
Supplier<VariableDef> constructorMethodVarSupplier) {
AnnotationMetadata annotationMetadata = parameter.getAnnotationMetadata();
if (isAnnotatedWithParameter(annotationMetadata) && isParametrized) {
// load the args
return methodParameters.get(2)
.invoke(
GET_MAP_METHOD,
ExpressionDef.constant(parameter.getName())
);
}
ExpressionDef expression = getValueBypassingBeanContext(parameter.getGenericType(), methodParameters);
if (expression != null) {
return expression;
}
boolean hasGenericType = false;
boolean isArray;
Method methodToInvoke;
final ClassElement genericType = parameter.getGenericType();
if (isValueType(annotationMetadata) && !isInnerType(genericType)) {
Optional<String> property = parameter.stringValue(Property.class, "name");
if (property.isPresent()) {
return getInvokeGetPropertyValueForConstructor(aThis, methodParameters, index, parameter, property.get());
}
if (parameter.getValue(Value.class, EvaluatedExpressionReference.class).isPresent()) {
return getInvokeGetEvaluatedExpressionValueForConstructorArgument(aThis, index, parameter);
}
Optional<String> valueValue = parameter.stringValue(Value.class);
if (valueValue.isPresent()) {
return getInvokeGetPropertyPlaceholderValueForConstructor(aThis, methodParameters, index, parameter, valueValue.get());
}
return ExpressionDef.nullValue();
}
isArray = genericType.isArray();
if (genericType.isAssignable(Collection.class) || isArray) {
hasGenericType = true;
ClassElement typeArgument = genericType.isArray() ? genericType.fromArray() : genericType.getFirstTypeArgument().orElse(null);
if (typeArgument != null && !typeArgument.isPrimitive()) {
if (typeArgument.isAssignable(BeanRegistration.class)) {
methodToInvoke = GET_BEAN_REGISTRATIONS_FOR_CONSTRUCTOR_ARGUMENT;
} else {
methodToInvoke = GET_BEANS_OF_TYPE_FOR_CONSTRUCTOR_ARGUMENT;
}
} else {
methodToInvoke = GET_BEAN_FOR_CONSTRUCTOR_ARGUMENT;
hasGenericType = false;
}
} else if (isInjectableMap(genericType)) {
hasGenericType = true;
methodToInvoke = GET_MAP_OF_TYPE_FOR_CONSTRUCTOR_ARGUMENT;
} else if (genericType.isAssignable(Stream.class)) {
hasGenericType = true;
methodToInvoke = GET_STREAM_OF_TYPE_FOR_CONSTRUCTOR_ARGUMENT;
} else if (genericType.isAssignable(Optional.class)) {
hasGenericType = true;
methodToInvoke = FIND_BEAN_FOR_CONSTRUCTOR_ARGUMENT;
} else if (genericType.isAssignable(BeanRegistration.class)) {
hasGenericType = true;
methodToInvoke = GET_BEAN_REGISTRATION_FOR_CONSTRUCTOR_ARGUMENT;
} else {
methodToInvoke = GET_BEAN_FOR_CONSTRUCTOR_ARGUMENT;
}
List<ExpressionDef> values = new ArrayList<>();
// load the first two arguments of the method (the BeanResolutionContext and the BeanContext) to be passed to the method
values.add(methodParameters.get(0));
values.add(methodParameters.get(1));
// pass the index of the method as the third argument
values.add(ExpressionDef.constant(index));
if (hasGenericType) {
values.add(
resolveConstructorArgumentGenericType(parameter.getGenericType(), index, constructorMethodVarSupplier)
);
}
// push qualifier
values.add(
getQualifier(parameter, () -> resolveConstructorArgument(index, constructorMethodVarSupplier.get()))
);
ExpressionDef result = aThis.superRef().invoke(methodToInvoke, values);
if (isArray && hasGenericType) {
result = convertToArray(parameter.getGenericType().fromArray(), result);
}
return result.cast(TypeDef.erasure(parameter.getType()));
}
private ExpressionDef getInvokeGetPropertyValueForConstructor(VariableDef.This aThis,
List<VariableDef.MethodParameter> methodParameters,
int i, ParameterElement entry, String value) {
return aThis.superRef().invoke(
GET_PROPERTY_VALUE_FOR_CONSTRUCTOR_ARGUMENT,
// 1st argument load BeanResolutionContext
methodParameters.get(0),
// 2nd argument load BeanContext
methodParameters.get(1),
// 4th argument the argument index
ExpressionDef.constant(i),
// 5th property value
ExpressionDef.constant(value),
// 6 cli property name
ExpressionDef.constant(getCliPrefix(entry.getName()))
).cast(TypeDef.erasure(entry.getType()));
}
private ExpressionDef getInvokeGetPropertyPlaceholderValueForConstructor(VariableDef.This aThis,
List<VariableDef.MethodParameter> methodParameters,
int i, ParameterElement entry, String value) {
return aThis.superRef().invoke(
GET_PROPERTY_PLACEHOLDER_VALUE_FOR_CONSTRUCTOR_ARGUMENT,
// 1st argument load BeanResolutionContext
methodParameters.get(0),
// 2nd argument load BeanContext
methodParameters.get(1),
// 4th argument the argument index
ExpressionDef.constant(i),
// 5th property value
ExpressionDef.constant(value)
).cast(TypeDef.erasure(entry.getType()));
}
private ExpressionDef getInvokeGetEvaluatedExpressionValueForConstructorArgument(VariableDef.This aThis,
int i, ParameterElement entry) {
return aThis.superRef()
.invoke(GET_EVALUATED_EXPRESSION_VALUE_FOR_CONSTRUCTOR_ARGUMENT, ExpressionDef.constant(i))
.cast(TypeDef.erasure(entry.getType()));
}
private ExpressionDef resolveConstructorArgumentGenericType(ClassElement type, int argumentIndex, Supplier<VariableDef> constructorMethodVarSupplier) {
ExpressionDef expressionDef = resolveArgumentGenericType(type);
if (expressionDef != null) {
return expressionDef;
}
ExpressionDef argumentExpression = resolveConstructorArgument(argumentIndex, constructorMethodVarSupplier.get());
if (type.isAssignable(Map.class)) {
argumentExpression = resolveSecondTypeArgument(argumentExpression);
} else {
argumentExpression = resolveFirstTypeArgument(argumentExpression);
}
return resolveInnerTypeArgumentIfNeeded(argumentExpression, type);
}
private ExpressionDef resolveConstructorArgument(int argumentIndex, VariableDef constructorMethodVar) {
return constructorMethodVar
.field("arguments", ClassTypeDef.of(Argument.class).array())
.arrayElement(argumentIndex);
}
private ExpressionDef resolveMethodArgumentGenericType(ClassElement type, int methodIndex, int argumentIndex) {
ExpressionDef expressionDef = resolveArgumentGenericType(type);
if (expressionDef != null) {
return expressionDef;
}
expressionDef = resolveMethodArgument(methodIndex, argumentIndex);
if (type.isAssignable(Map.class)) {
expressionDef = resolveSecondTypeArgument(expressionDef);
} else {
expressionDef = resolveFirstTypeArgument(expressionDef);
}
return resolveInnerTypeArgumentIfNeeded(expressionDef, type);
}
private ExpressionDef resolveMethodArgument(int methodIndex, int argumentIndex) {
return beanDefinitionTypeDef.
getStaticField(FIELD_INJECTION_METHODS, ClassTypeDef.of(AbstractInitializableBeanDefinition.MethodReference.class).array())
.arrayElement(methodIndex)
.field("arguments", ClassTypeDef.of(Argument.class).array())
.arrayElement(argumentIndex);
}
private ExpressionDef resolveFieldArgumentGenericType(ClassElement type, int fieldIndex) {
ExpressionDef argumentExpression = resolveArgumentGenericType(type);
if (argumentExpression != null) {
return argumentExpression;
}
argumentExpression = resolveFieldArgument(fieldIndex);
if (type.isAssignable(Map.class)) {
argumentExpression = resolveSecondTypeArgument(argumentExpression);
} else {
argumentExpression = resolveFirstTypeArgument(argumentExpression);
}
return resolveInnerTypeArgumentIfNeeded(argumentExpression, type);
}
private ExpressionDef resolveAnnotationArgument(int index) {
return beanDefinitionTypeDef.getStaticField(FIELD_ANNOTATION_INJECTIONS, TypeDef.of(AbstractInitializableBeanDefinition.AnnotationReference[].class))
.arrayElement(index)
.field("argument", TypeDef.of(Argument.class));
}
private ExpressionDef resolveFieldArgument(int fieldIndex) {
return beanDefinitionTypeDef.getStaticField(FIELD_INJECTION_FIELDS, TypeDef.of(AbstractInitializableBeanDefinition.FieldReference[].class))
.arrayElement(fieldIndex)
.field("argument", TypeDef.of(Argument.class));
}
@Nullable
private ExpressionDef resolveArgumentGenericType(ClassElement type) {
if (type.isArray()) {
if (!type.getTypeArguments().isEmpty() && isInternalGenericTypeContainer(type.fromArray())) {
// skip for arrays of BeanRegistration
return null;
}
final ClassElement componentType = type.fromArray();
if (componentType.isPrimitive()) {
return ArgumentExpUtils.TYPE_ARGUMENT.getStaticField(
componentType.getName().toUpperCase(Locale.ENGLISH),
ArgumentExpUtils.TYPE_ARGUMENT
);
}
return ArgumentExpUtils.TYPE_ARGUMENT.invokeStatic(
ArgumentExpUtils.METHOD_CREATE_ARGUMENT_SIMPLE,
ExpressionDef.constant(TypeDef.erasure(componentType)),
ExpressionDef.nullValue()
);
} else if (type.getTypeArguments().isEmpty()) {
return ExpressionDef.nullValue();
}
return null;
}
private ExpressionDef resolveInnerTypeArgumentIfNeeded(ExpressionDef argumentExpression, ClassElement type) {
if (isInternalGenericTypeContainer(type.getFirstTypeArgument().orElse(null))) {
return resolveFirstTypeArgument(argumentExpression);
}
return argumentExpression;
}
private boolean isInternalGenericTypeContainer(@Nullable ClassElement type) {
return type != null && type.isAssignable(BeanRegistration.class);
}
private ExpressionDef resolveFirstTypeArgument(ExpressionDef argumentExpression) {
return argumentExpression.invoke(GET_TYPE_PARAMETERS_METHOD).arrayElement(0);
}
private ExpressionDef resolveSecondTypeArgument(ExpressionDef argumentExpression) {
return argumentExpression.invoke(GET_TYPE_PARAMETERS_METHOD).arrayElement(1);
}
private boolean isValueType(AnnotationMetadata annotationMetadata) {
if (annotationMetadata != null) {
return annotationMetadata.hasDeclaredStereotype(Value.class) || annotationMetadata.hasDeclaredStereotype(Property.class);
}
return false;
}
private boolean isAnnotatedWithParameter(AnnotationMetadata annotationMetadata) {
if (annotationMetadata != null) {
return annotationMetadata.hasDeclaredAnnotation(Parameter.class);
}
return false;
}
private boolean isParametrized(ParameterElement... parameters) {
return Arrays.stream(parameters).anyMatch(p -> isAnnotatedWithParameter(p.getAnnotationMetadata()));
}
private void addConstructor(StaticBlock staticBlock) {
if (superBeanDefinition) {
classDefBuilder.addMethod(MethodDef.constructor()
.addModifiers(Modifier.PUBLIC)
.build((aThis, methodParameters)
-> aThis.superRef().invokeConstructor(
ExpressionDef.constant(beanTypeDef),
beanDefinitionTypeDef.getStaticField(staticBlock.constructorRefField)
)));
} else {
MethodDef constructor = MethodDef.constructor()
.addModifiers(Modifier.PROTECTED)
.addParameters(Class.class, AbstractInitializableBeanDefinition.MethodOrFieldReference.class)
.build((aThis, methodParameters) -> {
List<ExpressionDef> values = new ArrayList<>();
AnnotationMetadata annotationMetadata = this.annotationMetadata != null ? this.annotationMetadata : AnnotationMetadata.EMPTY_METADATA;
// 1: beanType
values.add(methodParameters.get(0));
// 2: `AbstractBeanDefinition2.MethodOrFieldReference.class` constructor
values.add(methodParameters.get(1));
// 3: annotationMetadata
if (annotationMetadata.isEmpty()) {
values.add(ExpressionDef.nullValue());
} else if (annotationMetadata instanceof AnnotationMetadataReference reference) {
values.add(AnnotationMetadataGenUtils.annotationMetadataReference(reference));
} else {
values.add(beanDefinitionTypeDef.getStaticField(staticBlock.annotationMetadataField));
}
// 4: `AbstractBeanDefinition2.MethodReference[].class` methodInjection
if (staticBlock.injectionMethodsField == null) {
values.add(ExpressionDef.nullValue());
} else {
values.add(beanDefinitionTypeDef.getStaticField(staticBlock.injectionMethodsField));
}
// 5: `AbstractBeanDefinition2.FieldReference[].class` fieldInjection
if (staticBlock.injectionFieldsField == null) {
values.add(ExpressionDef.nullValue());
} else {
values.add(beanDefinitionTypeDef.getStaticField(staticBlock.injectionFieldsField));
}
// 6: `AbstractBeanDefinition2.AnnotationReference[].class` annotationInjection
if (staticBlock.annotationInjectionsFieldType == null) {
values.add(ExpressionDef.nullValue());
} else {
values.add(beanDefinitionTypeDef.getStaticField(staticBlock.annotationInjectionsFieldType));
}
// 7: `ExecutableMethod[]` executableMethods
if (staticBlock.executableMethodsField == null) {
values.add(ExpressionDef.nullValue());
} else {
values.add(beanDefinitionTypeDef.getStaticField(staticBlock.executableMethodsField));
}
// 8: `Map<String, Argument<?>[]>` typeArgumentsMap
if (staticBlock.typeArgumentsField == null) {
values.add(ExpressionDef.nullValue());
} else {
values.add(beanDefinitionTypeDef.getStaticField(staticBlock.typeArgumentsField));
}
// 9: `PrecalculatedInfo`
values.add(beanDefinitionTypeDef.getStaticField(staticBlock.precalculatedInfoField));
if (BEAN_DEFINITION_CLASS_CONSTRUCTOR2.isPresent()) {
if (staticBlock.preStartConditionsField == null) {
// 10: Pre conditions
values.add(ClassTypeDef.of(Condition.class).array().instantiate());
// 11: Post conditions
values.add(ClassTypeDef.of(Condition.class).array().instantiate());
} else {
// 10: Pre conditions
values.add(beanDefinitionTypeDef.getStaticField(staticBlock.preStartConditionsField));
// 11: Post conditions
values.add(beanDefinitionTypeDef.getStaticField(staticBlock.postStartConditionsField));
}
// 12: Exception
values.add(beanDefinitionTypeDef.getStaticField(staticBlock.failedInitializationField));
return aThis.superRef(TYPE_ABSTRACT_BEAN_DEFINITION_AND_REFERENCE).invokeConstructor(BEAN_DEFINITION_CLASS_CONSTRUCTOR2.get(), values);
} else if (BEAN_DEFINITION_CLASS_CONSTRUCTOR1.isPresent()) {
return aThis.superRef(TYPE_ABSTRACT_BEAN_DEFINITION_AND_REFERENCE).invokeConstructor(BEAN_DEFINITION_CLASS_CONSTRUCTOR1.get(), values);
} else {
throw new IllegalStateException();
}
});
classDefBuilder.addMethod(constructor);
classDefBuilder.addMethod(MethodDef.constructor()
.addModifiers(Modifier.PUBLIC)
.build((aThis, methodParameters)
-> aThis.invokeConstructor(
constructor,
ExpressionDef.constant(beanTypeDef),
beanDefinitionTypeDef.getStaticField(FIELD_CONSTRUCTOR, ClassTypeDef.of(AbstractInitializableBeanDefinition.MethodOrFieldReference.class))
)));
}
}
private boolean isContainerType() {
return beanTypeElement.isArray() || DefaultArgument.CONTAINER_TYPES.stream().anyMatch(c -> c.equals(beanFullClassName));
}
private boolean isConfigurationProperties(AnnotationMetadata annotationMetadata) {
return isIterable(annotationMetadata) || annotationMetadata.hasStereotype(ConfigurationReader.class);
}
private boolean isIterable(AnnotationMetadata annotationMetadata) {
return annotationMetadata.hasDeclaredStereotype(EachProperty.class) || annotationMetadata.hasDeclaredStereotype(EachBean.class);
}
private ExpressionDef getNewMethodReference(TypedElement beanType,
MethodElement methodElement,
AnnotationMetadata annotationMetadata,
boolean isPostConstructMethod,
boolean isPreDestroyMethod) {
annotationMetadata = annotationMetadata.getTargetAnnotationMetadata();
if (annotationMetadata instanceof AnnotationMetadataHierarchy hierarchy) {
annotationMetadata = hierarchy.merge();
}
List<ExpressionDef> values = new ArrayList<>(
List.of(
// 1: declaringType
ExpressionDef.constant(TypeDef.erasure(beanType)),
// 2: methodName
ExpressionDef.constant(methodElement.getName()),
// 3: arguments
!methodElement.hasParameters() ? ExpressionDef.nullValue() : ArgumentExpUtils.pushBuildArgumentsForMethod(
this.annotationMetadata,
ClassElement.of(beanFullClassName),
beanDefinitionTypeDef,
Arrays.asList(methodElement.getParameters()),
loadClassValueExpressionFn
),
// 4: annotationMetadata
getAnnotationMetadataExpression(annotationMetadata)
)
);
if (isPreDestroyMethod || isPostConstructMethod) {
// 5: isPostConstructMethod
values.add(ExpressionDef.constant(isPostConstructMethod));
// 6: isPreDestroyMethod
values.add(ExpressionDef.constant(isPreDestroyMethod));
return ClassTypeDef.of(AbstractInitializableBeanDefinition.MethodReference.class)
.instantiate(
METHOD_REFERENCE_CONSTRUCTOR_POST_PRE, values
);
} else {
return ClassTypeDef.of(AbstractInitializableBeanDefinition.MethodReference.class)
.instantiate(
METHOD_REFERENCE_CONSTRUCTOR, values
);
}
}
private ExpressionDef getNewFieldReference(TypedElement declaringType, FieldElement fieldElement, AnnotationMetadata annotationMetadata) {
return ClassTypeDef.of(AbstractInitializableBeanDefinition.FieldReference.class)
.instantiate(
FIELD_REFERENCE_CONSTRUCTOR,
// 1: declaringType
ExpressionDef.constant(TypeDef.erasure(declaringType)),
// 2: argument
ArgumentExpUtils.pushCreateArgument(
this.annotationMetadata,
ClassElement.of(beanFullClassName),
beanDefinitionTypeDef,
fieldElement.getName(),
fieldElement.getGenericType(),
annotationMetadata,
fieldElement.getGenericType().getTypeArguments(),
loadClassValueExpressionFn
)
);
}
private ExpressionDef getNewAnnotationReference(TypedElement referencedType) {
return ClassTypeDef.of(AbstractInitializableBeanDefinition.AnnotationReference.class)
.instantiate(
ANNOTATION_REFERENCE_CONSTRUCTOR,
ClassTypeDef.of(Argument.class)
.invokeStatic(
ARGUMENT_OF_METHOD,
ExpressionDef.constant(TypeDef.erasure(referencedType))
)
);
}
private ExpressionDef getAnnotationMetadataExpression(AnnotationMetadata annotationMetadata) {
annotationMetadata = annotationMetadata.getTargetAnnotationMetadata();
//
// MutableAnnotationMetadata.contributeDefaults(
// this.annotationMetadata,
// annotationMetadata
// );
if (annotationMetadata == AnnotationMetadata.EMPTY_METADATA || annotationMetadata.isEmpty()) {
return ExpressionDef.nullValue();
} else if (annotationMetadata instanceof AnnotationMetadataHierarchy annotationMetadataHierarchy) {
return AnnotationMetadataGenUtils.instantiateNewMetadataHierarchy(annotationMetadataHierarchy, loadClassValueExpressionFn);
} else if (annotationMetadata instanceof MutableAnnotationMetadata mutableAnnotationMetadata) {
return AnnotationMetadataGenUtils.instantiateNewMetadata(mutableAnnotationMetadata, loadClassValueExpressionFn);
} else {
throw new IllegalStateException("Unknown annotation metadata: " + annotationMetadata.getClass().getName());
}
}
private static Method getBeanLookupMethod(String methodName, boolean requiresGenericType) {
if (requiresGenericType) {
return ReflectionUtils.getRequiredInternalMethod(
AbstractInitializableBeanDefinition.class,
methodName,
BeanResolutionContext.class,
BeanContext.class,
int.class,
Argument.class,
Qualifier.class);
} else {
return ReflectionUtils.getRequiredInternalMethod(
AbstractInitializableBeanDefinition.class,
methodName,
BeanResolutionContext.class,
BeanContext.class,
int.class,
Qualifier.class
);
}
}
private static Method getBeanLookupMethodForArgument(String methodName, boolean requiresGenericType) {
if (requiresGenericType) {
return ReflectionUtils.getRequiredInternalMethod(
AbstractInitializableBeanDefinition.class,
methodName,
BeanResolutionContext.class,
BeanContext.class,
int.class,
int.class,
Argument.class,
Qualifier.class);
}
return ReflectionUtils.getRequiredInternalMethod(
AbstractInitializableBeanDefinition.class,
methodName,
BeanResolutionContext.class,
BeanContext.class,
int.class,
int.class,
Qualifier.class);
}
@Override
public String getName() {
return beanDefinitionName;
}
@Override
public boolean isProtected() {
return false;
}
@Override
public boolean isPublic() {
return true;
}
@Override
public Object getNativeType() {
return this;
}
@Override
public Collection<Element> getInjectionPoints() {
if (fieldInjectionPoints.isEmpty() && methodInjectionPoints.isEmpty()) {
return Collections.emptyList();
} else {
Collection<Element> injectionPoints = new ArrayList<>();
for (FieldVisitData fieldInjectionPoint : fieldInjectionPoints) {
injectionPoints.add(fieldInjectionPoint.fieldElement);
}
for (MethodVisitData methodInjectionPoint : methodInjectionPoints) {
injectionPoints.add(methodInjectionPoint.methodElement);
}
return Collections.unmodifiableCollection(injectionPoints);
}
}
@Override
public boolean isAbstract() {
return this.isAbstract;
}
@Override
public <T extends Annotation> Element annotate(String annotationType, Consumer<AnnotationValueBuilder<T>> consumer) {
this.beanProducingElement.annotate(annotationType, consumer);
return this;
}
@Override
public Element removeAnnotation(String annotationType) {
this.beanProducingElement.removeAnnotation(annotationType);
return this;
}
@Override
public <T extends Annotation> Element removeAnnotationIf(Predicate<AnnotationValue<T>> predicate) {
this.beanProducingElement.removeAnnotationIf(predicate);
return this;
}
@Override
public Element removeStereotype(String annotationType) {
this.beanProducingElement.removeStereotype(annotationType);
return this;
}
@Override
public ClassElement getDeclaringClass() {
final Element beanProducingElement = this.beanProducingElement;
return getDeclaringType(beanProducingElement);
}
private ClassElement getDeclaringType(Element beanProducingElement) {
if (beanProducingElement instanceof ClassElement element) {
return element;
} else if (beanProducingElement instanceof MemberElement element) {
return element.getDeclaringType();
} else if (beanProducingElement instanceof BeanElementBuilder builder) {
return builder.getDeclaringElement();
} else {
return this.beanTypeElement;
}
}
@Override
public Element getProducingElement() {
return beanProducingElement;
}
@Override
public Set<ClassElement> getBeanTypes() {
final String[] types = this.annotationMetadata.stringValues(Bean.class, "typed");
if (ArrayUtils.isNotEmpty(types)) {
HashSet<ClassElement> classElements = new HashSet<>();
for (String type : types) {
visitorContext.getClassElement(type).ifPresent(classElements::add);
}
return Collections.unmodifiableSet(classElements);
} else {
final Optional<ClassElement> superType = beanTypeElement.getSuperType();
final Collection<ClassElement> interfaces = beanTypeElement.getInterfaces();
if (superType.isPresent() || !interfaces.isEmpty()) {
Set<ClassElement> beanTypes = new HashSet<>();
beanTypes.add(beanTypeElement);
populateBeanTypes(new HashSet<>(), beanTypes, superType.orElse(null), interfaces);
return Collections.unmodifiableSet(beanTypes);
} else {
return Collections.singleton(beanTypeElement);
}
}
}
private void populateBeanTypes(Set<String> processedTypes, Set<ClassElement> beanTypes, ClassElement superType, Collection<ClassElement> interfaces) {
for (ClassElement anInterface : interfaces) {
final String n = anInterface.getName();
if (!processedTypes.contains(n)) {
processedTypes.add(n);
beanTypes.add(anInterface);
populateBeanTypes(processedTypes, beanTypes, null, anInterface.getInterfaces());
}
}
if (superType != null) {
final String n = superType.getName();
if (!processedTypes.contains(n)) {
processedTypes.add(n);
beanTypes.add(superType);
final ClassElement next = superType.getSuperType().orElse(null);
populateBeanTypes(processedTypes, beanTypes, next, superType.getInterfaces());
}
}
}
@Override
public Optional<String> getScope() {
return annotationMetadata.getAnnotationNameByStereotype(AnnotationUtil.SCOPE);
}
@Override
public Collection<String> getQualifiers() {
return Collections.unmodifiableList(annotationMetadata.getAnnotationNamesByStereotype(AnnotationUtil.QUALIFIER));
}
@Override
public BeanElementBuilder addAssociatedBean(ClassElement type, VisitorContext visitorContext) {
if (visitorContext instanceof BeanElementVisitorContext context) {
final Element[] originatingElements = getOriginatingElements();
return context
.addAssociatedBean(originatingElements[0], type);
}
return BeanElement.super.addAssociatedBean(type, visitorContext);
}
@Override
public Element[] getOriginatingElements() {
return this.originatingElements.getOriginatingElements();
}
@Override
public void addOriginatingElement(Element element) {
originatingElements.addOriginatingElement(element);
}
/**
* Sets whether this bean is a proxied type.
*
* @param proxiedBean True if it proxied
* @param isProxyTarget True if the proxied bean is a retained target
*/
public void setProxiedBean(boolean proxiedBean, boolean isProxyTarget) {
this.proxiedBean = proxiedBean;
this.isProxyTarget = isProxyTarget;
}
@Override
public boolean isProxyTarget() {
return isProxyTarget;
}
@Override
public boolean isProxiedBean() {
return proxiedBean;
}
/**
* Finish any work writing beans.
*/
@Internal
public static void finish() {
AbstractAnnotationMetadataBuilder.clearMutated();
AbstractAnnotationMetadataBuilder.clearCaches();
EvaluatedExpressionProcessor.reset();
}
@Internal
private static final class AnnotationVisitData {
final TypedElement memberBeanType;
final String memberPropertyName;
final MethodElement memberPropertyGetter;
final String requiredValue;
final String notEqualsValue;
public AnnotationVisitData(TypedElement memberBeanType,
String memberPropertyName,
MethodElement memberPropertyGetter,
@Nullable String requiredValue,
@Nullable String notEqualsValue) {
this.memberBeanType = memberBeanType;
this.memberPropertyName = memberPropertyName;
this.memberPropertyGetter = memberPropertyGetter;
this.requiredValue = requiredValue;
this.notEqualsValue = notEqualsValue;
}
}
@Internal
private static final class FieldVisitData {
final TypedElement beanType;
final FieldElement fieldElement;
final AnnotationMetadata annotationMetadata;
final boolean requiresReflection;
FieldVisitData(
TypedElement beanType,
FieldElement fieldElement,
AnnotationMetadata annotationMetadata,
boolean requiresReflection) {
this.beanType = beanType;
this.fieldElement = fieldElement;
this.annotationMetadata = annotationMetadata;
this.requiresReflection = requiresReflection;
}
}
/**
* Data used when visiting method.
*/
@Internal
public static final class MethodVisitData {
private final TypedElement beanType;
private final boolean requiresReflection;
private final MethodElement methodElement;
private final AnnotationMetadata annotationMetadata;
private final boolean postConstruct;
private final boolean preDestroy;
/**
* Default constructor.
*
* @param beanType The declaring type
* @param methodElement The method element
* @param requiresReflection Whether reflection is required
* @param annotationMetadata The annotation metadata
*/
MethodVisitData(
TypedElement beanType,
MethodElement methodElement,
boolean requiresReflection,
AnnotationMetadata annotationMetadata) {
this.beanType = beanType;
this.requiresReflection = requiresReflection;
this.methodElement = methodElement;
this.annotationMetadata = annotationMetadata;
this.postConstruct = false;
this.preDestroy = false;
}
MethodVisitData(
TypedElement beanType,
MethodElement methodElement,
boolean requiresReflection,
AnnotationMetadata annotationMetadata,
boolean postConstruct,
boolean preDestroy) {
this.beanType = beanType;
this.requiresReflection = requiresReflection;
this.methodElement = methodElement;
this.annotationMetadata = annotationMetadata;
this.postConstruct = postConstruct;
this.preDestroy = preDestroy;
}
/**
* @return The method element
*/
public MethodElement getMethodElement() {
return methodElement;
}
/**
* @return The annotationMetadata
*/
public AnnotationMetadata getAnnotationMetadata() {
return annotationMetadata;
}
/**
* @return The declaring type object.
*/
public TypedElement getBeanType() {
return beanType;
}
/**
* @return is reflection required
*/
public boolean isRequiresReflection() {
return requiresReflection;
}
public boolean isPostConstruct() {
return postConstruct;
}
public boolean isPreDestroy() {
return preDestroy;
}
}
private static final class FactoryBuildMethodDefinition extends BuildMethodDefinition {
private final ClassElement factoryClass;
private final Element factoryElement;
private final ParameterElement[] parameters;
private FactoryBuildMethodDefinition(ClassElement factoryClass, Element factoryElement, ParameterElement[] parameters) {
this.factoryClass = factoryClass;
this.factoryElement = factoryElement;
this.parameters = parameters;
}
@Override
public ParameterElement[] getParameters() {
return parameters;
}
}
private static final class ConstructorBuildMethodDefinition extends BuildMethodDefinition {
private final MethodElement constructor;
private final boolean requiresReflection;
private ConstructorBuildMethodDefinition(MethodElement constructor, boolean requiresReflection) {
this.constructor = constructor;
this.requiresReflection = requiresReflection;
}
@Override
ParameterElement[] getParameters() {
return constructor.getParameters();
}
}
private abstract static class BuildMethodDefinition {
private BuildMethodLifecycleDefinition postConstruct;
private BuildMethodLifecycleDefinition preDestroy;
abstract ParameterElement[] getParameters();
void postConstruct(boolean intercepted) {
if (postConstruct == null) {
postConstruct = new BuildMethodLifecycleDefinition(intercepted);
}
}
void preDestroy(boolean intercepted) {
if (preDestroy == null) {
preDestroy = new BuildMethodLifecycleDefinition(intercepted);
}
}
}
private static final class BuildMethodLifecycleDefinition {
private final boolean intercepted;
private final List<InjectMethodBuildCommand> injectionPoints = new ArrayList<>();
private BuildMethodLifecycleDefinition(boolean intercepted) {
this.intercepted = intercepted;
}
}
private record SetterInjectionInjectCommand(TypedElement declaringType,
MethodElement methodElement,
AnnotationMetadata annotationMetadata,
boolean requiresReflection,
boolean isOptional) implements InjectMethodCommand {
@Override
public boolean hasInjectScope() {
return BeanDefinitionWriter.hasInjectScope(methodElement.getParameters());
}
}
private record InjectMethodInjectCommand(TypedElement declaringType,
MethodElement methodElement,
boolean requiresReflection,
VisitorContext visitorContext,
int methodIndex) implements InjectMethodCommand {
@Override
public boolean hasInjectScope() {
return BeanDefinitionWriter.hasInjectScope(methodElement.getParameters());
}
}
private record ConfigFieldBuilderInjectCommand(ClassElement type,
String field,
AnnotationMetadata annotationMetadata,
ConfigurationMetadataBuilder metadataBuilder,
boolean isInterface,
ConfigBuilderState configBuilderState,
List<ConfigBuilderPointInjectCommand> builderPoints) implements ConfigBuilderInjectCommand {
@Override
public boolean hasInjectScope() {
return false;
}
}
private record ConfigMethodBuilderInjectPointCommand(ClassElement type,
String methodName,
AnnotationMetadata annotationMetadata,
ConfigurationMetadataBuilder metadataBuilder,
boolean isInterface,
ConfigBuilderState configBuilderState,
List<ConfigBuilderPointInjectCommand> builderPoints) implements ConfigBuilderInjectCommand {
@Override
public boolean hasInjectScope() {
return false;
}
}
private record ConfigBuilderMethodDurationInjectCommand(String propertyName,
ClassElement returnType,
String methodName,
String path) implements ConfigBuilderPointInjectCommand {
}
private record ConfigBuilderMethodInjectCommand(String propertyName,
ClassElement returnType,
String methodName,
ClassElement paramType,
Map<String, ClassElement> generics,
String path) implements ConfigBuilderPointInjectCommand {
}
private interface ConfigBuilderInjectCommand extends InjectMethodCommand {
List<ConfigBuilderPointInjectCommand> builderPoints();
}
private interface ConfigBuilderPointInjectCommand {
}
private record InjectFieldInjectCommand(TypedElement declaringType,
FieldElement fieldElement,
boolean requiresReflection) implements InjectMethodCommand {
@Override
public boolean hasInjectScope() {
return BeanDefinitionWriter.hasInjectScope(fieldElement);
}
}
private record InjectFieldValueInjectCommand(TypedElement declaringType,
FieldElement fieldElement,
boolean requiresReflection,
boolean isOptional) implements InjectMethodCommand {
@Override
public boolean hasInjectScope() {
return BeanDefinitionWriter.hasInjectScope(fieldElement);
}
}
private interface InjectMethodCommand {
boolean hasInjectScope();
}
private record InjectMethodBuildCommand(TypedElement declaringType, MethodElement methodElement,
boolean requiresReflection, int methodIndex) {
}
private record InjectMethodSignature(
VariableDef.This aThis,
List<VariableDef.MethodParameter> methodParameters,
VariableDef beanResolutionContext,
VariableDef beanContext,
VariableDef instanceVar
) {
private InjectMethodSignature(VariableDef.This aThis,
List<VariableDef.MethodParameter> methodParameters,
VariableDef instanceVar) {
this(aThis, methodParameters, methodParameters.get(0), methodParameters.get(1), instanceVar);
}
}
private record StaticBlock(@NonNull
StatementDef statement,
@NonNull
FieldDef annotationMetadataField,
@NonNull
FieldDef failedInitializationField,
@NonNull
FieldDef constructorRefField,
@Nullable
FieldDef injectionMethodsField,
@Nullable
FieldDef injectionFieldsField,
@Nullable
FieldDef annotationInjectionsFieldType,
@Nullable
FieldDef typeArgumentsField,
@Nullable
FieldDef executableMethodsField,
@NonNull
FieldDef precalculatedInfoField,
@Nullable
FieldDef preStartConditionsField,
@Nullable
FieldDef postStartConditionsField) {
}
}