diff --git a/inject-groovy/src/main/groovy/io/micronaut/ast/groovy/TypeElementVisitorEnd.groovy b/inject-groovy/src/main/groovy/io/micronaut/ast/groovy/TypeElementVisitorEnd.groovy index 6656e3af067..cca3f6957b6 100644 --- a/inject-groovy/src/main/groovy/io/micronaut/ast/groovy/TypeElementVisitorEnd.groovy +++ b/inject-groovy/src/main/groovy/io/micronaut/ast/groovy/TypeElementVisitorEnd.groovy @@ -105,6 +105,7 @@ class TypeElementVisitorEnd implements ASTTransformation, CompilationUnitAware { TypeElementVisitorTransform.loadedVisitors.remove() TypeElementVisitorTransform.beanDefinitionBuilders.remove() AstAnnotationUtils.invalidateCache() + AbstractAnnotationMetadataBuilder.clearMutated() } @Override diff --git a/inject-groovy/src/test/groovy/io/micronaut/inject/visitor/AnnotationMetadataSpec.groovy b/inject-groovy/src/test/groovy/io/micronaut/inject/visitor/AnnotationMetadataSpec.groovy new file mode 100644 index 00000000000..4d5932025f0 --- /dev/null +++ b/inject-groovy/src/test/groovy/io/micronaut/inject/visitor/AnnotationMetadataSpec.groovy @@ -0,0 +1,31 @@ +package io.micronaut.inject.visitor + +import io.micronaut.ast.transform.test.AbstractBeanDefinitionSpec + +class AnnotationMetadataSpec extends AbstractBeanDefinitionSpec { + + void "test mutated metadata from a visitor is available on beans"() { + when: + def definition = buildBeanDefinition('test.TestListener', ''' +package test + +import io.micronaut.context.annotation.* +import jakarta.inject.Singleton +import io.micronaut.inject.visitor.SomeAnn + +@Singleton +class TestListener { + + @SomeAnn + @Executable + void receive(String v) { + } +} + +''') + + then: + noExceptionThrown() //asserts default values are available + definition.findMethod("receive", String).get().hasAnnotation('my.custom.Annotation') + } +} diff --git a/inject-groovy/src/test/groovy/io/micronaut/inject/visitor/MutatingVisitor.groovy b/inject-groovy/src/test/groovy/io/micronaut/inject/visitor/MutatingVisitor.groovy new file mode 100644 index 00000000000..18b6c840ae3 --- /dev/null +++ b/inject-groovy/src/test/groovy/io/micronaut/inject/visitor/MutatingVisitor.groovy @@ -0,0 +1,11 @@ +package io.micronaut.inject.visitor + +import io.micronaut.inject.ast.MethodElement + +class MutatingVisitor implements TypeElementVisitor { + + void visitMethod(MethodElement element, VisitorContext context) { + element.annotate("my.custom.Annotation") + element.annotationMetadata.findAnnotation(SomeAnn).get().getRequiredValue("someValue", String) + } +} diff --git a/inject-groovy/src/test/groovy/io/micronaut/inject/visitor/SomeAnn.groovy b/inject-groovy/src/test/groovy/io/micronaut/inject/visitor/SomeAnn.groovy index f31a596aff8..9bb62d8dfdc 100644 --- a/inject-groovy/src/test/groovy/io/micronaut/inject/visitor/SomeAnn.groovy +++ b/inject-groovy/src/test/groovy/io/micronaut/inject/visitor/SomeAnn.groovy @@ -1,5 +1,5 @@ package io.micronaut.inject.visitor @interface SomeAnn { - + String someValue() default "OK" } diff --git a/inject-groovy/src/test/resources/META-INF/services/io.micronaut.inject.visitor.TypeElementVisitor b/inject-groovy/src/test/resources/META-INF/services/io.micronaut.inject.visitor.TypeElementVisitor index bd24237f9f0..b22d1586918 100644 --- a/inject-groovy/src/test/resources/META-INF/services/io.micronaut.inject.visitor.TypeElementVisitor +++ b/inject-groovy/src/test/resources/META-INF/services/io.micronaut.inject.visitor.TypeElementVisitor @@ -2,4 +2,5 @@ io.micronaut.inject.visitor.TestInjectVisitor io.micronaut.inject.visitor.AllElementsVisitor io.micronaut.inject.visitor.AllClassesVisitor io.micronaut.inject.visitor.ControllerGetVisitor -io.micronaut.inject.visitor.IntroductionVisitor \ No newline at end of file +io.micronaut.inject.visitor.IntroductionVisitor +io.micronaut.inject.visitor.MutatingVisitor diff --git a/inject-java/src/main/java/io/micronaut/annotation/processing/BeanDefinitionInjectProcessor.java b/inject-java/src/main/java/io/micronaut/annotation/processing/BeanDefinitionInjectProcessor.java index da5b77b3ac3..5fd37ec1f07 100644 --- a/inject-java/src/main/java/io/micronaut/annotation/processing/BeanDefinitionInjectProcessor.java +++ b/inject-java/src/main/java/io/micronaut/annotation/processing/BeanDefinitionInjectProcessor.java @@ -31,6 +31,7 @@ import io.micronaut.core.util.CollectionUtils; import io.micronaut.core.util.StringUtils; import io.micronaut.core.value.OptionalValues; +import io.micronaut.inject.annotation.AbstractAnnotationMetadataBuilder; import io.micronaut.inject.annotation.AnnotationMetadataHierarchy; import io.micronaut.inject.annotation.AnnotationMetadataReference; import io.micronaut.inject.ast.*; @@ -268,6 +269,7 @@ public final boolean process(Set annotations, RoundEnviro } } finally { AnnotationUtils.invalidateCache(); + AbstractAnnotationMetadataBuilder.clearMutated(); JavaAnnotationMetadataBuilder.clearCaches(); } } diff --git a/inject/src/main/java/io/micronaut/inject/annotation/AbstractAnnotationMetadataBuilder.java b/inject/src/main/java/io/micronaut/inject/annotation/AbstractAnnotationMetadataBuilder.java index 078325864a2..949f2405af3 100644 --- a/inject/src/main/java/io/micronaut/inject/annotation/AbstractAnnotationMetadataBuilder.java +++ b/inject/src/main/java/io/micronaut/inject/annotation/AbstractAnnotationMetadataBuilder.java @@ -2004,8 +2004,15 @@ public static boolean isMetadataMutated(String declaringType, Object element) { * Used to clear mutated metadata at the end of a compilation cycle. */ @Internal - public static void clearCaches() { + public static void clearMutated() { MUTATED_ANNOTATION_METADATA.clear(); + } + + /** + * Used to clear caches at the end of a compilation cycle. + */ + @Internal + public static void clearCaches() { ANNOTATION_DEFAULTS.clear(); } diff --git a/inject/src/main/java/io/micronaut/inject/annotation/DefaultAnnotationMetadata.java b/inject/src/main/java/io/micronaut/inject/annotation/DefaultAnnotationMetadata.java index e0a3804669a..46f73bd9c73 100644 --- a/inject/src/main/java/io/micronaut/inject/annotation/DefaultAnnotationMetadata.java +++ b/inject/src/main/java/io/micronaut/inject/annotation/DefaultAnnotationMetadata.java @@ -982,7 +982,7 @@ Optional getDefaultValue(@NonNull String annotation, @NonNull String memb ArgumentUtils.requireNonNull("member", member); ArgumentUtils.requireNonNull("requiredType", requiredType); - Map defaultValues = AnnotationMetadataSupport.getDefaultValues(annotation); + Map defaultValues = getDefaultValues(annotation); if (defaultValues.containsKey(member)) { final Object v = defaultValues.get(member); if (requiredType.isInstance(v)) { @@ -1270,11 +1270,11 @@ Optional> findAnnotation(@NonNull Stri if (allAnnotations != null && StringUtils.isNotEmpty(annotation)) { Map values = allAnnotations.get(annotation); if (values != null) { - return Optional.of(new AnnotationValue<>(annotation, values, AnnotationMetadataSupport.getDefaultValues(annotation))); + return Optional.of(new AnnotationValue<>(annotation, values, getDefaultValues(annotation))); } else if (allStereotypes != null) { values = allStereotypes.get(annotation); if (values != null) { - return Optional.of(new AnnotationValue<>(annotation, values, AnnotationMetadataSupport.getDefaultValues(annotation))); + return Optional.of(new AnnotationValue<>(annotation, values, getDefaultValues(annotation))); } } } @@ -1289,11 +1289,11 @@ Optional> findDeclaredAnnotation(@NonN if (declaredAnnotations != null && StringUtils.isNotEmpty(annotation)) { Map values = declaredAnnotations.get(annotation); if (values != null) { - return Optional.of(new AnnotationValue<>(annotation, values, AnnotationMetadataSupport.getDefaultValues(annotation))); + return Optional.of(new AnnotationValue<>(annotation, values, getDefaultValues(annotation))); } else if (declaredStereotypes != null) { values = declaredStereotypes.get(annotation); if (values != null) { - return Optional.of(new AnnotationValue<>(annotation, values, AnnotationMetadataSupport.getDefaultValues(annotation))); + return Optional.of(new AnnotationValue<>(annotation, values, getDefaultValues(annotation))); } } } @@ -1344,7 +1344,7 @@ Optional getDefaultValue(@NonNull String annotation, @NonNull String memb ArgumentUtils.requireNonNull("member", member); ArgumentUtils.requireNonNull("requiredType", requiredType); // Note this method should never reference the "annotationDefaultValues" field, which is used only at compile time - Map defaultValues = AnnotationMetadataSupport.getDefaultValues(annotation); + Map defaultValues = getDefaultValues(annotation); if (defaultValues.containsKey(member)) { return ConversionService.SHARED.convert(defaultValues.get(member), requiredType); } diff --git a/inject/src/main/java/io/micronaut/inject/annotation/MutableAnnotationMetadata.java b/inject/src/main/java/io/micronaut/inject/annotation/MutableAnnotationMetadata.java index c28348c4568..f46200d1fcb 100644 --- a/inject/src/main/java/io/micronaut/inject/annotation/MutableAnnotationMetadata.java +++ b/inject/src/main/java/io/micronaut/inject/annotation/MutableAnnotationMetadata.java @@ -20,6 +20,7 @@ import io.micronaut.core.annotation.NonNull; import io.micronaut.core.annotation.Nullable; import io.micronaut.core.util.CollectionUtils; +import org.jetbrains.annotations.NotNull; import java.lang.annotation.Annotation; import java.lang.annotation.RetentionPolicy; @@ -30,6 +31,7 @@ import java.util.Map; import java.util.Objects; import java.util.function.Predicate; +import java.util.stream.Collectors; /** * A mutable various of {@link DefaultAnnotationMetadata} that is used only at build time. @@ -38,6 +40,7 @@ * @since 2.4.0 */ public class MutableAnnotationMetadata extends DefaultAnnotationMetadata { + private boolean hasPropertyExpressions = false; /** @@ -82,6 +85,20 @@ public MutableAnnotationMetadata clone() { return cloned; } + @NotNull + @Override + public Map getDefaultValues(@NonNull String annotation) { + Map values = super.getDefaultValues(annotation); + if (values.isEmpty() && annotationDefaultValues != null) { + final Map compileTimeDefaults = annotationDefaultValues.get(annotation); + if (compileTimeDefaults != null && !compileTimeDefaults.isEmpty()) { + return compileTimeDefaults.entrySet().stream() + .collect(Collectors.toMap(e -> e.getKey().toString(), Map.Entry::getValue)); + } + } + return values; + } + @Override public void removeAnnotationIf(@NonNull Predicate> predicate) { super.removeAnnotationIf(predicate);