AbstractBeanDefinitionBuilder.java
/*
* Copyright 2017-2021 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.context.annotation.Bean;
import io.micronaut.context.annotation.Executable;
import io.micronaut.context.annotation.Property;
import io.micronaut.context.annotation.Value;
import io.micronaut.core.annotation.AnnotationClassValue;
import io.micronaut.core.annotation.AnnotationMetadata;
import io.micronaut.core.annotation.AnnotationUtil;
import io.micronaut.core.annotation.AnnotationValue;
import io.micronaut.core.annotation.AnnotationValueBuilder;
import io.micronaut.core.annotation.Internal;
import io.micronaut.core.annotation.NonNull;
import io.micronaut.core.annotation.Nullable;
import io.micronaut.core.util.ArrayUtils;
import io.micronaut.core.util.CollectionUtils;
import io.micronaut.inject.InjectionPoint;
import io.micronaut.inject.annotation.AnnotationMetadataHierarchy;
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.annotation.ElementAnnotationMetadataFactory;
import io.micronaut.inject.ast.ElementModifier;
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.TypedElement;
import io.micronaut.inject.ast.beans.BeanConstructorElement;
import io.micronaut.inject.ast.beans.BeanElementBuilder;
import io.micronaut.inject.ast.beans.BeanFieldElement;
import io.micronaut.inject.ast.beans.BeanMethodElement;
import io.micronaut.inject.ast.beans.BeanParameterElement;
import io.micronaut.inject.configuration.ConfigurationMetadataBuilder;
import io.micronaut.inject.visitor.VisitorContext;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Predicate;
import static io.micronaut.inject.ast.beans.BeanParameterElement.ZERO_BEAN_PARAMETER_ELEMENTS;
/**
* Abstract implementation of the {@link BeanElementBuilder} interface that should be implemented by downstream language specific implementations.
*
* @author graemerocher
* @since 3.0.0
*/
@Internal
public abstract class AbstractBeanDefinitionBuilder implements BeanElementBuilder {
private static final Map<String, AtomicInteger> BEAN_COUNTER = new HashMap<>(15);
private static final Predicate<Set<ElementModifier>> PUBLIC_FILTER = (
elementModifiers -> elementModifiers.contains(ElementModifier.PUBLIC));
private static final Predicate<Set<ElementModifier>> NON_PUBLIC_FILTER = (
elementModifiers -> !elementModifiers.contains(ElementModifier.PUBLIC));
private static final Comparator<MemberElement> SORTER = (o1, o2) -> {
final ClassElement d1 = o1.getDeclaringType();
final ClassElement d2 = o2.getDeclaringType();
final String o1Type = d1.getName();
final String o2Type = d2.getName();
if (o1Type.equals(o2Type)) {
return 0;
} else {
if (d1.isAssignable(d2)) {
return 1;
} else {
return -1;
}
}
};
protected final ConfigurationMetadataBuilder metadataBuilder;
protected final VisitorContext visitorContext;
protected final ElementAnnotationMetadataFactory elementAnnotationMetadataFactory;
private final Element originatingElement;
private final ClassElement originatingType;
private final ClassElement beanType;
private final int identifier;
private final MutableAnnotationMetadata annotationMetadata;
private final List<BeanMethodElement> executableMethods = new ArrayList<>(5);
private final List<BeanMethodElement> interceptedMethods = new ArrayList<>(5);
private final List<AbstractBeanDefinitionBuilder> childBeans = new ArrayList<>(5);
private final List<BeanMethodElement> injectedMethods = new ArrayList<>(5);
private final List<BeanMethodElement> preDestroyMethods = new ArrayList<>(5);
private final List<BeanMethodElement> postConstructMethods = new ArrayList<>(5);
private final List<BeanFieldElement> injectedFields = new ArrayList<>(5);
private BeanConstructorElement constructorElement;
private Map<String, Map<String, ClassElement>> typeArguments;
private ClassElement[] exposedTypes;
private boolean intercepted;
/**
* Default constructor.
*
* @param originatingElement The originating element
* @param beanType The bean type
* @param metadataBuilder the metadata builder
* @param visitorContext the visitor context
* @param elementAnnotationMetadataFactory The element annotation metadata factory
*/
protected AbstractBeanDefinitionBuilder(
Element originatingElement,
ClassElement beanType,
ConfigurationMetadataBuilder metadataBuilder,
VisitorContext visitorContext,
ElementAnnotationMetadataFactory elementAnnotationMetadataFactory) {
this.originatingElement = originatingElement;
this.elementAnnotationMetadataFactory = elementAnnotationMetadataFactory;
if (originatingElement instanceof MethodElement element) {
this.originatingType = element.getDeclaringType();
} else if (originatingElement instanceof ClassElement element) {
this.originatingType = element;
} else {
throw new IllegalArgumentException("Invalid originating element: " + originatingElement);
}
this.beanType = beanType;
this.metadataBuilder = metadataBuilder;
this.visitorContext = visitorContext;
this.identifier = BEAN_COUNTER.computeIfAbsent(beanType.getName(), (s) -> new AtomicInteger(0))
.getAndIncrement();
this.annotationMetadata = MutableAnnotationMetadata.of(beanType.getAnnotationMetadata());
this.annotationMetadata.addDeclaredAnnotation(Bean.class.getName(), Collections.emptyMap());
this.constructorElement = initConstructor(beanType);
}
@Override
public BeanElementBuilder intercept(AnnotationValue<?>... annotationValue) {
for (AnnotationValue<?> value : annotationValue) {
annotate(value);
}
this.intercepted = true;
return this;
}
@Internal
public static void writeBeanDefinitionBuilders(ClassWriterOutputVisitor classWriterOutputVisitor,
List<AbstractBeanDefinitionBuilder> beanDefinitionBuilders)
throws IOException {
for (AbstractBeanDefinitionBuilder beanDefinitionBuilder : beanDefinitionBuilders) {
writeBeanDefinition(classWriterOutputVisitor, beanDefinitionBuilder);
final List<AbstractBeanDefinitionBuilder> childBeans = beanDefinitionBuilder.getChildBeans();
for (AbstractBeanDefinitionBuilder childBean : childBeans) {
writeBeanDefinition(classWriterOutputVisitor, childBean);
}
}
}
private static void writeBeanDefinition(ClassWriterOutputVisitor classWriterOutputVisitor, AbstractBeanDefinitionBuilder beanDefinitionBuilder)
throws IOException {
final ClassOutputWriter beanDefinitionWriter = beanDefinitionBuilder.build();
if (beanDefinitionWriter != null) {
beanDefinitionWriter.accept(classWriterOutputVisitor);
}
}
private InternalBeanConstructorElement initConstructor(ClassElement beanType) {
return beanType.getPrimaryConstructor().map(m -> new InternalBeanConstructorElement(
m,
!m.isPublic(),
initBeanParameters(m.getParameters())
)).orElse(null);
}
/**
* Is the bean to be built intercepted?
*
* @return True if it is
*/
protected boolean isIntercepted() {
return this.intercepted || !this.interceptedMethods.isEmpty();
}
@Override
public BeanElementBuilder inject() {
processInjectedMethods();
processInjectedFields();
return this;
}
/**
* Any child bean definitions.
*
* @return The child beans
*/
public List<AbstractBeanDefinitionBuilder> getChildBeans() {
return childBeans;
}
private void processInjectedFields() {
final ElementQuery<FieldElement> baseQuery = ElementQuery.ALL_FIELDS
.onlyInstance()
.onlyInjected();
Set<FieldElement> accessibleFields = new HashSet<>();
this.beanType.getEnclosedElements(baseQuery.modifiers(PUBLIC_FILTER))
.forEach(fieldElement -> {
accessibleFields.add(fieldElement);
new InternalBeanElementField(fieldElement, false).inject();
});
this.beanType.getEnclosedElements(baseQuery.modifiers(NON_PUBLIC_FILTER))
.forEach(fieldElement -> {
if (!accessibleFields.contains(fieldElement)) {
new InternalBeanElementField(fieldElement, true).inject();
}
});
}
private void processInjectedMethods() {
final ElementQuery<MethodElement> baseQuery = ElementQuery.ALL_METHODS
.onlyInstance()
.onlyConcrete()
.onlyInjected();
Set<MethodElement> accessibleMethods = new HashSet<>();
this.beanType.getEnclosedElements(baseQuery.modifiers(PUBLIC_FILTER))
.forEach(methodElement -> {
accessibleMethods.add(methodElement);
handleMethod(methodElement, false);
});
this.beanType.getEnclosedElements(baseQuery.modifiers(NON_PUBLIC_FILTER))
.forEach(methodElement -> {
if (!accessibleMethods.contains(methodElement)) {
handleMethod(methodElement, true);
}
});
}
private void handleMethod(MethodElement methodElement, boolean requiresReflection) {
boolean lifecycleMethod = false;
if (methodElement.getAnnotationMetadata().hasDeclaredAnnotation(AnnotationUtil.PRE_DESTROY)) {
new InternalBeanElementMethod(methodElement, requiresReflection)
.preDestroy();
lifecycleMethod = true;
}
if (methodElement.getAnnotationMetadata().hasDeclaredAnnotation(AnnotationUtil.POST_CONSTRUCT)) {
new InternalBeanElementMethod(methodElement, requiresReflection)
.postConstruct();
lifecycleMethod = true;
}
if (!lifecycleMethod) {
new InternalBeanElementMethod(methodElement, requiresReflection)
.inject();
}
}
@NonNull
@Override
public Element getOriginatingElement() {
return originatingElement;
}
@NonNull
@Override
public ClassElement getBeanType() {
return beanType;
}
/**
* Initialize the bean parameters.
*
* @param constructorParameters The parameters to use.
* @return The initialized parameters
*/
protected final BeanParameterElement[] initBeanParameters(@NonNull ParameterElement[] constructorParameters) {
if (ArrayUtils.isNotEmpty(constructorParameters)) {
return Arrays.stream(constructorParameters)
.map(InternalBeanParameter::new)
.toArray(BeanParameterElement[]::new);
} else {
return ZERO_BEAN_PARAMETER_ELEMENTS;
}
}
@NonNull
@Override
public AnnotationMetadata getAnnotationMetadata() {
return this.annotationMetadata;
}
@NonNull
@Override
public BeanElementBuilder createWith(@NonNull MethodElement element) {
// TODO: handle factories, static methods etc.
//noinspection ConstantConditions
if (element != null) {
constructorElement = new InternalBeanConstructorElement(
element,
!element.isPublic(),
initBeanParameters(element.getParameters())
);
}
return this;
}
@NonNull
@Override
public BeanElementBuilder typed(ClassElement... types) {
if (ArrayUtils.isNotEmpty(types)) {
this.exposedTypes = types;
}
return this;
}
@NonNull
@Override
public BeanElementBuilder typeArguments(@NonNull ClassElement... types) {
final Map<String, ClassElement> typeArguments = this.beanType.getTypeArguments();
Map<String, ClassElement> resolvedTypes = resolveTypeArguments(typeArguments, types);
if (resolvedTypes != null) {
if (this.typeArguments == null) {
this.typeArguments = new LinkedHashMap<>();
}
this.typeArguments.put(beanType.getName(), typeArguments);
}
return this;
}
@NonNull
@Override
public BeanElementBuilder typeArgumentsForType(ClassElement type, @NonNull ClassElement... types) {
if (type != null) {
final Map<String, ClassElement> typeArguments = type.getTypeArguments();
Map<String, ClassElement> resolvedTypes = resolveTypeArguments(typeArguments, types);
if (resolvedTypes != null) {
if (this.typeArguments == null) {
this.typeArguments = new LinkedHashMap<>();
}
this.typeArguments.put(type.getName(), resolvedTypes);
}
}
return this;
}
@Nullable
private Map<String, ClassElement> resolveTypeArguments(Map<String, ClassElement> typeArguments, ClassElement... types) {
Map<String, ClassElement> resolvedTypes = null;
if (typeArguments.size() == types.length) {
resolvedTypes = CollectionUtils.newLinkedHashMap(typeArguments.size());
final Iterator<String> i = typeArguments.keySet().iterator();
for (ClassElement type : types) {
final String variable = i.next();
resolvedTypes.put(variable, type);
}
}
return resolvedTypes;
}
@Override
public BeanElementBuilder withConstructor(Consumer<BeanConstructorElement> constructorElement) {
if (constructorElement != null && this.constructorElement != null) {
constructorElement.accept(this.constructorElement);
}
return this;
}
@NonNull
@Override
public BeanElementBuilder withMethods(
@NonNull ElementQuery<MethodElement> methods,
@NonNull Consumer<BeanMethodElement> beanMethods) {
//noinspection ConstantConditions
if (methods != null && beanMethods != null) {
final ElementQuery<MethodElement> baseQuery = methods.onlyInstance();
this.beanType.getEnclosedElements(baseQuery.modifiers(m -> m.contains(ElementModifier.PUBLIC)))
.forEach(methodElement ->
beanMethods.accept(new InternalBeanElementMethod(methodElement, false))
);
this.beanType.getEnclosedElements(baseQuery.modifiers(m -> !m.contains(ElementModifier.PUBLIC)))
.forEach(methodElement ->
beanMethods.accept(new InternalBeanElementMethod(methodElement, true))
);
}
return this;
}
@NonNull
@Override
public BeanElementBuilder withFields(@NonNull ElementQuery<FieldElement> fields, @NonNull Consumer<BeanFieldElement> beanFields) {
//noinspection ConstantConditions
if (fields != null && beanFields != null) {
this.beanType.getEnclosedElements(fields.onlyInstance().onlyAccessible(originatingType))
.forEach((fieldElement) ->
beanFields.accept(new InternalBeanElementField(fieldElement, false))
);
}
return this;
}
@NonNull
@Override
public BeanElementBuilder withParameters(Consumer<BeanParameterElement[]> parameters) {
if (parameters != null && this.constructorElement != null) {
parameters.accept(getParameters());
}
return this;
}
/**
* @return The bean creation parameters.
*/
@NonNull
protected BeanParameterElement[] getParameters() {
return constructorElement.getParameters();
}
@NonNull
@Override
public String getName() {
return beanType.getName();
}
@Override
public boolean isProtected() {
return beanType.isProtected();
}
@Override
public boolean isPublic() {
return beanType.isPublic();
}
@NonNull
@Override
public Object getNativeType() {
return beanType;
}
@NonNull
@Override
public <T extends Annotation> BeanElementBuilder annotate(@NonNull String annotationType, @NonNull Consumer<AnnotationValueBuilder<T>> consumer) {
annotate(this.annotationMetadata, annotationType, consumer);
return this;
}
@Override
public <T extends Annotation> Element annotate(AnnotationValue<T> annotationValue) {
annotate(this.annotationMetadata, annotationValue);
return this;
}
@Override
public BeanElementBuilder removeAnnotation(@NonNull String annotationType) {
removeAnnotation(this.annotationMetadata, annotationType);
return this;
}
@Override
public <T extends Annotation> BeanElementBuilder removeAnnotationIf(@NonNull Predicate<AnnotationValue<T>> predicate) {
removeAnnotationIf(this.annotationMetadata, predicate);
return this;
}
@Override
public BeanElementBuilder removeStereotype(@NonNull String annotationType) {
removeStereotype(this.annotationMetadata, annotationType);
return this;
}
private BeanElementBuilder addChildBean(@NonNull MethodElement producerMethod, Consumer<BeanElementBuilder> childBeanBuilder) {
final AbstractBeanDefinitionBuilder childBuilder = createChildBean(producerMethod);
this.childBeans.add(childBuilder);
if (childBeanBuilder != null) {
childBeanBuilder.accept(childBuilder);
}
return this;
}
private BeanElementBuilder addChildBean(@NonNull FieldElement producerMethod, Consumer<BeanElementBuilder> childBeanBuilder) {
final AbstractBeanDefinitionBuilder childBuilder = createChildBean(producerMethod);
this.childBeans.add(childBuilder);
if (childBeanBuilder != null) {
childBeanBuilder.accept(childBuilder);
}
return this;
}
@Override
public <E extends MemberElement> BeanElementBuilder produceBeans(ElementQuery<E> methodsOrFields,
Consumer<BeanElementBuilder> childBeanBuilder) {
methodsOrFields = methodsOrFields
.onlyConcrete()
.modifiers(modifiers -> modifiers.contains(ElementModifier.PUBLIC));
final List<E> enclosedElements = this.beanType.getEnclosedElements(methodsOrFields);
for (E enclosedElement : enclosedElements) {
if (enclosedElement instanceof FieldElement fe) {
final ClassElement type = fe.getGenericField().getType();
if (type.isPublic() && !type.isPrimitive()) {
addChildBean(fe, childBeanBuilder);
}
}
if (enclosedElement instanceof MethodElement me && !(enclosedElement instanceof ConstructorElement)) {
final ClassElement type = me.getGenericReturnType().getType();
if (type.isPublic() && !type.isPrimitive()) {
addChildBean(me, childBeanBuilder);
}
}
}
return this;
}
/**
* Creates a child bean for the given producer field.
*
* @param producerField The producer field
* @return The child bean builder
*/
protected abstract @NonNull AbstractBeanDefinitionBuilder createChildBean(FieldElement producerField);
/**
* Visit the intercepted methods of this type.
*
* @param consumer A consumer to handle the method
*/
protected void visitInterceptedMethods(BiConsumer<TypedElement, MethodElement> consumer) {
if (consumer != null) {
ClassElement beanClass = getBeanType();
if (CollectionUtils.isNotEmpty(interceptedMethods)) {
for (BeanMethodElement interceptedMethod : interceptedMethods) {
handleMethod(beanClass, interceptedMethod, consumer);
}
}
if (this.intercepted) {
beanClass.getEnclosedElements(
ElementQuery.ALL_METHODS
.onlyInstance()
.modifiers(mods -> !mods.contains(ElementModifier.FINAL) && mods.contains(ElementModifier.PUBLIC))
).forEach(method -> {
InternalBeanElementMethod ibem = new InternalBeanElementMethod(
method,
true
);
if (!interceptedMethods.contains(ibem)) {
handleMethod(beanClass, ibem, consumer);
}
});
}
}
}
@SuppressWarnings({"rawtypes", "unchecked"})
private void handleMethod(ClassElement beanClass, MethodElement method, BiConsumer<TypedElement, MethodElement> consumer) {
consumer.accept(
beanClass,
method.withAnnotationMetadata(new AnnotationMetadataHierarchy(getAnnotationMetadata(), method.getAnnotationMetadata()))
);
}
/**
* Creates a child bean for the given producer method.
*
* @param producerMethod The producer method
* @return The child bean builder
*/
protected abstract @NonNull AbstractBeanDefinitionBuilder createChildBean(MethodElement producerMethod);
/**
* Build the bean definition writer.
*
* @return The writer, possibly null if it wasn't possible to build it
*/
@SuppressWarnings({"ConstantConditions", "java:S2583"})
@Nullable
public BeanClassWriter build() {
BeanClassWriter beanWriter = buildBeanClassWriter();
if (beanWriter == null) {
return null;
} else {
BeanDefinitionVisitor parentVisitor = beanWriter.getBeanDefinitionVisitor();
AnnotationMetadata thisAnnotationMetadata = getAnnotationMetadata();
if (isIntercepted() && parentVisitor instanceof BeanDefinitionWriter beanDefinitionWriter) {
return new BeanClassWriter() {
@Override
public BeanDefinitionVisitor getBeanDefinitionVisitor() {
return parentVisitor;
}
@Override
public void accept(ClassWriterOutputVisitor classWriterOutputVisitor) throws IOException {
BeanDefinitionVisitor aopProxyWriter = AbstractBeanDefinitionBuilder.this.createAopWriter(beanDefinitionWriter, thisAnnotationMetadata);
if (configureBeanVisitor(aopProxyWriter)) {
return;
}
configureInjectionPoints(aopProxyWriter);
visitInterceptedMethods(
createAroundMethodVisitor(aopProxyWriter)
);
finalizeAndWriteBean(classWriterOutputVisitor, aopProxyWriter);
beanWriter.accept(classWriterOutputVisitor);
}
};
} else {
return beanWriter;
}
}
}
/**
* Creates the around method visitor.
*
* @param aopProxyWriter The AOP writer
* @return The visitor
*/
@NonNull
protected abstract BiConsumer<TypedElement, MethodElement> createAroundMethodVisitor(BeanDefinitionVisitor aopProxyWriter);
/**
* Creates the AOP writer.
*
* @param beanDefinitionWriter The bean definition writer
* @param annotationMetadata The annotation metadata
* @return The AOP writer
*/
@NonNull
protected abstract BeanDefinitionVisitor createAopWriter(BeanDefinitionWriter beanDefinitionWriter, AnnotationMetadata annotationMetadata);
@NonNull
private BeanClassWriter buildBeanClassWriter() {
final BeanDefinitionVisitor beanDefinitionWriter = createBeanDefinitionWriter();
return new BeanClassWriter() {
@Override
public BeanDefinitionVisitor getBeanDefinitionVisitor() {
return beanDefinitionWriter;
}
@Override
public void accept(ClassWriterOutputVisitor classWriterOutputVisitor) throws IOException {
if (configureBeanVisitor(beanDefinitionWriter)) {
return;
}
configureInjectionPoints(beanDefinitionWriter);
for (BeanMethodElement postConstructMethod : postConstructMethods) {
if (postConstructMethod.getDeclaringType().equals(beanType)) {
beanDefinitionWriter.visitPostConstructMethod(
beanType,
postConstructMethod,
postConstructMethod.isReflectionRequired(),
visitorContext
);
}
}
for (BeanMethodElement preDestroyMethod : preDestroyMethods) {
if (preDestroyMethod.getDeclaringType().equals(beanType)) {
beanDefinitionWriter.visitPreDestroyMethod(
beanType,
preDestroyMethod,
preDestroyMethod.isReflectionRequired(),
visitorContext
);
}
}
finalizeAndWriteBean(classWriterOutputVisitor, beanDefinitionWriter);
}
};
}
private void configureInjectionPoints(BeanDefinitionVisitor beanDefinitionWriter) {
Map<ClassElement, List<MemberElement>> sortedInjections = new LinkedHashMap<>();
List<MemberElement> allInjected = new ArrayList<>();
allInjected.addAll(injectedFields);
allInjected.addAll(injectedMethods);
allInjected.sort(SORTER);
for (MemberElement memberElement : allInjected) {
final List<MemberElement> list = sortedInjections
.computeIfAbsent(memberElement.getDeclaringType(),
classElement -> new ArrayList<>()
);
list.add(memberElement);
}
for (List<MemberElement> members : sortedInjections.values()) {
members.sort((o1, o2) -> {
if (o1 instanceof FieldElement && o2 instanceof MethodElement) {
return 1;
} else if (o1 instanceof MethodElement && o1 instanceof FieldElement) {
return -1;
}
return 0;
});
}
for (List<MemberElement> list : sortedInjections.values()) {
for (MemberElement memberElement : list) {
if (memberElement instanceof FieldElement) {
InternalBeanElementField ibf = (InternalBeanElementField) memberElement;
ibf.<InternalBeanElementField>with(element ->
visitField(beanDefinitionWriter, element, element)
);
} else {
InternalBeanElementMethod ibm = (InternalBeanElementMethod) memberElement;
ibm.<InternalBeanElementMethod>with(element ->
beanDefinitionWriter.visitMethodInjectionPoint(
ibm.getDeclaringType(),
ibm,
ibm.isReflectionRequired(),
visitorContext
)
);
}
}
}
for (BeanMethodElement executableMethod : executableMethods) {
beanDefinitionWriter.visitExecutableMethod(
beanType,
executableMethod,
visitorContext
);
if (executableMethod.getAnnotationMetadata().isTrue(Executable.class, "processOnStartup")) {
beanDefinitionWriter.setRequiresMethodProcessing(true);
}
}
}
/**
* Finish the given bean and write it to the output.
*
* @param classWriterOutputVisitor The output
* @param beanDefinitionWriter The writer
* @throws IOException If an error occurred
*/
protected void finalizeAndWriteBean(ClassWriterOutputVisitor classWriterOutputVisitor, BeanDefinitionVisitor beanDefinitionWriter) throws IOException {
beanDefinitionWriter.visitBeanDefinitionEnd();
beanDefinitionWriter.accept(classWriterOutputVisitor);
}
/**
* Configure the bean visitor for this builder.
*
* @param beanDefinitionWriter The bean visitor
* @return True if an error occurred
*/
protected boolean configureBeanVisitor(BeanDefinitionVisitor beanDefinitionWriter) {
if (exposedTypes != null) {
final AnnotationClassValue<?>[] annotationClassValues =
Arrays.stream(exposedTypes).map(ce -> new AnnotationClassValue<>(ce.getName())).toArray(AnnotationClassValue[]::new);
annotate(Bean.class, builder -> builder.member("typed", annotationClassValues));
}
if (typeArguments != null) {
beanDefinitionWriter.visitTypeArguments(AbstractBeanDefinitionBuilder.this.typeArguments);
}
Element producingElement = getProducingElement();
if (producingElement instanceof ClassElement) {
if (constructorElement == null) {
constructorElement = initConstructor(beanType);
}
if (constructorElement == null) {
visitorContext.fail("Cannot create associated bean with no accessible primary constructor. Consider supply the constructor with createWith(..)", originatingElement);
return true;
} else {
beanDefinitionWriter.visitBeanDefinitionConstructor(
constructorElement,
!constructorElement.isPublic(),
visitorContext
);
}
}
return false;
}
/**
* @return Creates the bean definition writer.
*/
protected BeanDefinitionVisitor createBeanDefinitionWriter() {
return new BeanDefinitionWriter(
this,
OriginatingElements.of(originatingElement),
visitorContext,
identifier
);
}
private void visitField(BeanDefinitionVisitor beanDefinitionWriter,
BeanFieldElement injectedField,
InternalBeanElementField ibf) {
if (injectedField.hasAnnotation(Value.class) || injectedField.hasAnnotation(Property.class)) {
beanDefinitionWriter.visitFieldValue(
injectedField.getDeclaringType(),
injectedField,
ibf.isReflectionRequired(),
ibf.isDeclaredNullable() || !InjectionPoint.isInjectionRequired(injectedField)
);
} else {
beanDefinitionWriter.visitFieldInjectionPoint(
injectedField.getDeclaringType(),
ibf,
ibf.isReflectionRequired(),
visitorContext
);
}
}
/**
* Add an annotation to the given metadata.
*
* @param annotationMetadata The annotation metadata
* @param annotationType the annotation type
* @param consumer The builder
* @param <T> The annotation generic type
*/
protected abstract <T extends Annotation> void annotate(AnnotationMetadata annotationMetadata, String annotationType, Consumer<AnnotationValueBuilder<T>> consumer);
/**
* Add an annotation to the given metadata.
*
* @param annotationMetadata The annotation metadata
* @param annotationValue The value
* @param <T> The annotation generic type
* @since 3.3.0
*/
protected abstract <T extends Annotation> void annotate(@NonNull AnnotationMetadata annotationMetadata, @NonNull AnnotationValue<T> annotationValue);
/**
* Remove a stereotype from the given metadata.
*
* @param annotationMetadata The metadata
* @param annotationType The stereotype
*/
protected abstract void removeStereotype(AnnotationMetadata annotationMetadata, String annotationType);
/**
* Remove an annotation if it matches the given condition.
*
* @param annotationMetadata The metadata
* @param predicate The predicate
* @param <T> The annotation type
*/
protected abstract <T extends Annotation> void removeAnnotationIf(AnnotationMetadata annotationMetadata, Predicate<AnnotationValue<T>> predicate);
/**
* Remove an annotation for the given name.
*
* @param annotationMetadata The metadata
* @param annotationType The type
*/
protected abstract void removeAnnotation(AnnotationMetadata annotationMetadata, String annotationType);
/**
* Super class for all bean elements.
*
* @param <E> The element type
*/
private abstract class InternalBeanElement<E extends Element> implements Element {
protected AnnotationMetadata currentMetadata;
private final E element;
private final MutableAnnotationMetadata elementMetadata;
private InternalBeanElement(E element, MutableAnnotationMetadata elementMetadata) {
this.element = element;
this.elementMetadata = elementMetadata;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
InternalBeanElement<?> that = (InternalBeanElement<?>) o;
return element.equals(that.element);
}
@Override
public int hashCode() {
return Objects.hash(element);
}
@NonNull
@Override
public AnnotationMetadata getAnnotationMetadata() {
if (currentMetadata != null) {
return currentMetadata;
}
return elementMetadata;
}
@NonNull
@Override
public String getName() {
return element.getName();
}
@Override
public boolean isProtected() {
return element.isProtected();
}
@Override
public boolean isPublic() {
return element.isPublic();
}
@NonNull
@Override
public Object getNativeType() {
return element.getNativeType();
}
@NonNull
@Override
public <T extends Annotation> Element annotate(@NonNull String annotationType, @NonNull Consumer<AnnotationValueBuilder<T>> consumer) {
AbstractBeanDefinitionBuilder.this.annotate(elementMetadata, annotationType, consumer);
return this;
}
@Override
public <T extends Annotation> Element annotate(AnnotationValue<T> annotationValue) {
AbstractBeanDefinitionBuilder.this.annotate(elementMetadata, annotationValue);
return this;
}
@Override
public Element removeAnnotation(@NonNull String annotationType) {
AbstractBeanDefinitionBuilder.this.removeAnnotation(elementMetadata, annotationType);
return this;
}
@Override
public <T extends Annotation> Element removeAnnotationIf(@NonNull Predicate<AnnotationValue<T>> predicate) {
AbstractBeanDefinitionBuilder.this.removeAnnotationIf(elementMetadata, predicate);
return this;
}
@Override
public Element removeStereotype(@NonNull String annotationType) {
AbstractBeanDefinitionBuilder.this.removeStereotype(elementMetadata, annotationType);
return this;
}
public <T extends InternalBeanElement<E>> void with(Consumer<T> consumer) {
try {
this.currentMetadata = elementMetadata.isEmpty() ? EMPTY_METADATA : elementMetadata;
//noinspection unchecked
consumer.accept((T) this);
} finally {
currentMetadata = null;
}
}
}
/**
* Models a {@link BeanMethodElement}.
*/
private final class InternalBeanElementMethod extends InternalBeanElement<MethodElement> implements BeanMethodElement {
private final MethodElement methodElement;
private final boolean requiresReflection;
private BeanParameterElement[] beanParameters;
private InternalBeanElementMethod(MethodElement methodElement, boolean requiresReflection) {
this(methodElement, requiresReflection, initBeanParameters(methodElement.getParameters()));
}
private InternalBeanElementMethod(MethodElement methodElement,
boolean requiresReflection,
BeanParameterElement[] beanParameters) {
super(methodElement, MutableAnnotationMetadata.of(methodElement.getAnnotationMetadata().getDeclaredMetadata()));
this.methodElement = methodElement;
this.requiresReflection = requiresReflection;
this.beanParameters = beanParameters;
}
@Override
public boolean isReflectionRequired() {
return requiresReflection;
}
@Override
public boolean isReflectionRequired(ClassElement callingType) {
return requiresReflection;
}
@Override
public boolean isPackagePrivate() {
return methodElement.isPackagePrivate();
}
@Override
public boolean isAbstract() {
return methodElement.isAbstract();
}
@Override
public boolean isStatic() {
return methodElement.isStatic();
}
@Override
public boolean isPrivate() {
return methodElement.isPrivate();
}
@Override
public boolean isFinal() {
return methodElement.isFinal();
}
@Override
public boolean isSuspend() {
return methodElement.isSuspend();
}
@Override
public boolean isDefault() {
return methodElement.isDefault();
}
@Override
public boolean isProtected() {
return methodElement.isProtected();
}
@Override
public boolean isPublic() {
return methodElement.isPublic();
}
@NonNull
@Override
public BeanMethodElement executable() {
if (!AbstractBeanDefinitionBuilder.this.executableMethods.contains(this)) {
AbstractBeanDefinitionBuilder.this.executableMethods.add(this);
}
return BeanMethodElement.super.executable();
}
@Override
public BeanMethodElement intercept(AnnotationValue<?>... annotationValue) {
if (!AbstractBeanDefinitionBuilder.this.interceptedMethods.contains(this)) {
AbstractBeanDefinitionBuilder.this.interceptedMethods.add(this);
}
return BeanMethodElement.super.intercept(annotationValue);
}
@Override
public BeanMethodElement executable(boolean processOnStartup) {
if (!AbstractBeanDefinitionBuilder.this.executableMethods.contains(this)) {
AbstractBeanDefinitionBuilder.this.executableMethods.add(this);
}
return BeanMethodElement.super.executable(processOnStartup);
}
@NonNull
@Override
public BeanMethodElement inject() {
if (!AbstractBeanDefinitionBuilder.this.injectedMethods.contains(this)) {
AbstractBeanDefinitionBuilder.this.injectedMethods.add(this);
}
return BeanMethodElement.super.inject();
}
@NonNull
@Override
public BeanMethodElement preDestroy() {
if (!AbstractBeanDefinitionBuilder.this.preDestroyMethods.contains(this)) {
AbstractBeanDefinitionBuilder.this.preDestroyMethods.add(this);
}
return BeanMethodElement.super.preDestroy();
}
@NonNull
@Override
public BeanMethodElement postConstruct() {
if (!AbstractBeanDefinitionBuilder.this.postConstructMethods.contains(this)) {
AbstractBeanDefinitionBuilder.this.postConstructMethods.add(this);
}
return BeanMethodElement.super.postConstruct();
}
@NonNull
@Override
public BeanParameterElement[] getParameters() {
return this.beanParameters;
}
@NonNull
@Override
public ClassElement getReturnType() {
return methodElement.getReturnType();
}
@NonNull
@Override
public ClassElement getGenericReturnType() {
return methodElement.getGenericReturnType();
}
@NonNull
@Override
public MethodElement withParameters(@NonNull ParameterElement... newParameters) {
this.beanParameters = initBeanParameters(newParameters);
return this;
}
@Override
public MethodElement withAnnotationMetadata(AnnotationMetadata annotationMetadata) {
this.currentMetadata = annotationMetadata;
return this;
}
@Override
public ClassElement getDeclaringType() {
return methodElement.getDeclaringType();
}
@Override
public ClassElement getOwningType() {
return AbstractBeanDefinitionBuilder.this.beanType;
}
}
/**
* Models a {@link io.micronaut.inject.ast.beans.BeanConstructorElement}.
*/
private final class InternalBeanConstructorElement extends InternalBeanElement<MethodElement> implements
BeanConstructorElement {
private final MethodElement methodElement;
private final boolean requiresReflection;
private BeanParameterElement[] beanParameters;
private InternalBeanConstructorElement(MethodElement methodElement,
boolean requiresReflection,
BeanParameterElement[] beanParameters) {
super(methodElement, MutableAnnotationMetadata.of(methodElement.getAnnotationMetadata()));
this.methodElement = methodElement;
this.requiresReflection = requiresReflection;
this.beanParameters = beanParameters;
}
public boolean isRequiresReflection() {
return requiresReflection;
}
@Override
public boolean isPackagePrivate() {
return methodElement.isPackagePrivate();
}
@Override
public boolean isAbstract() {
return methodElement.isAbstract();
}
@Override
public boolean isStatic() {
return methodElement.isStatic();
}
@Override
public boolean isPrivate() {
return methodElement.isPrivate();
}
@Override
public boolean isFinal() {
return methodElement.isFinal();
}
@Override
public boolean isSuspend() {
return methodElement.isSuspend();
}
@Override
public boolean isDefault() {
return methodElement.isDefault();
}
@Override
public boolean isProtected() {
return methodElement.isProtected();
}
@Override
public boolean isPublic() {
return methodElement.isPublic();
}
@NonNull
@Override
public BeanParameterElement[] getParameters() {
return this.beanParameters;
}
@NonNull
@Override
public ClassElement getReturnType() {
return methodElement.getReturnType();
}
@NonNull
@Override
public ClassElement getGenericReturnType() {
return methodElement.getGenericReturnType();
}
@NonNull
@Override
public MethodElement withParameters(@NonNull ParameterElement... newParameters) {
this.beanParameters = initBeanParameters(newParameters);
return this;
}
@Override
public ClassElement getDeclaringType() {
return methodElement.getDeclaringType();
}
@Override
public ClassElement getOwningType() {
return AbstractBeanDefinitionBuilder.this.beanType;
}
}
/**
* Models a {@link BeanFieldElement}.
*/
private final class InternalBeanElementField extends InternalBeanElement<FieldElement> implements BeanFieldElement {
private final FieldElement fieldElement;
private final boolean requiresReflection;
private ClassElement genericType;
private InternalBeanElementField(FieldElement element, boolean requiresReflection) {
super(element, MutableAnnotationMetadata.of(element.getAnnotationMetadata()));
this.fieldElement = element;
this.requiresReflection = requiresReflection;
}
public boolean isRequiresReflection() {
return requiresReflection;
}
@Override
public BeanFieldElement inject() {
if (!AbstractBeanDefinitionBuilder.this.injectedFields.contains(this)) {
AbstractBeanDefinitionBuilder.this.injectedFields.add(this);
}
return BeanFieldElement.super.inject();
}
@Override
public BeanFieldElement injectValue(String expression) {
if (!AbstractBeanDefinitionBuilder.this.injectedFields.contains(this)) {
AbstractBeanDefinitionBuilder.this.injectedFields.add(this);
}
return BeanFieldElement.super.injectValue(expression);
}
@Override
public ClassElement getDeclaringType() {
return fieldElement.getDeclaringType();
}
@Override
public ClassElement getOwningType() {
return AbstractBeanDefinitionBuilder.this.beanType;
}
@NonNull
@Override
public ClassElement getType() {
return fieldElement.getType();
}
@Override
public ClassElement getGenericField() {
if (genericType != null) {
return genericType;
} else {
return fieldElement.getGenericField();
}
}
@NonNull
@Override
public BeanFieldElement typeArguments(@NonNull ClassElement... types) {
final ClassElement genericType = fieldElement.getGenericField();
final Map<String, ClassElement> typeArguments = genericType.getTypeArguments();
final Map<String, ClassElement> resolved = resolveTypeArguments(typeArguments, types);
if (resolved != null) {
this.genericType = genericType.withTypeArguments(resolved).withAnnotationMetadata(getAnnotationMetadata());
}
return this;
}
}
/**
* Models a {@link BeanParameterElement}.
*/
private final class InternalBeanParameter extends InternalBeanElement<ParameterElement> implements BeanParameterElement {
private final ParameterElement parameterElement;
private ClassElement genericType;
private InternalBeanParameter(ParameterElement element) {
super(element, MutableAnnotationMetadata.of(element.getAnnotationMetadata()));
parameterElement = element;
}
@NonNull
@Override
public ClassElement getGenericType() {
if (genericType != null) {
return genericType;
} else {
return parameterElement.getGenericType();
}
}
@NonNull
@Override
public ClassElement getType() {
return parameterElement.getType();
}
@SuppressWarnings("rawtypes")
@NonNull
@Override
public BeanParameterElement typeArguments(@NonNull ClassElement... types) {
final ClassElement genericType = parameterElement.getGenericType();
final Map<String, ClassElement> typeArguments = genericType.getTypeArguments();
final Map<String, ClassElement> resolved = resolveTypeArguments(typeArguments, types);
if (resolved != null) {
this.genericType = genericType.withTypeArguments(resolved).withAnnotationMetadata(getAnnotationMetadata());
}
return this;
}
}
}