Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MergedAnnotation meta-data support #22909

Merged
merged 14 commits into from
May 7, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -55,7 +55,7 @@ public class AnnotatedGenericBeanDefinition extends GenericBeanDefinition implem
*/
public AnnotatedGenericBeanDefinition(Class<?> beanClass) {
setBeanClass(beanClass);
this.metadata = new StandardAnnotationMetadata(beanClass, true);
this.metadata = AnnotationMetadata.introspect(beanClass);
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -29,7 +29,6 @@
import org.springframework.core.io.DescriptiveResource;
import org.springframework.core.io.Resource;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.StandardAnnotationMetadata;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
Expand Down Expand Up @@ -104,7 +103,7 @@ public ConfigurationClass(MetadataReader metadataReader, @Nullable Configuration
*/
public ConfigurationClass(Class<?> clazz, String beanName) {
Assert.notNull(beanName, "Bean name must not be null");
this.metadata = new StandardAnnotationMetadata(clazz, true);
this.metadata = AnnotationMetadata.introspect(clazz);
this.resource = new DescriptiveResource(clazz.getName());
this.beanName = beanName;
}
Expand All @@ -118,7 +117,7 @@ public ConfigurationClass(Class<?> clazz, String beanName) {
* @since 3.1.1
*/
public ConfigurationClass(Class<?> clazz, @Nullable ConfigurationClass importedBy) {
this.metadata = new StandardAnnotationMetadata(clazz, true);
this.metadata = AnnotationMetadata.introspect(clazz);
this.resource = new DescriptiveResource(clazz.getName());
this.importedBy.add(importedBy);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -913,7 +913,7 @@ private class SourceClass implements Ordered {
public SourceClass(Object source) {
this.source = source;
if (source instanceof Class) {
this.metadata = new StandardAnnotationMetadata((Class<?>) source, true);
this.metadata = AnnotationMetadata.introspect((Class<?>) source);
}
else {
this.metadata = ((MetadataReader) source).getAnnotationMetadata();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.annotation.Order;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.StandardAnnotationMetadata;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.lang.Nullable;
Expand Down Expand Up @@ -106,7 +105,7 @@ else if (beanDef instanceof AbstractBeanDefinition && ((AbstractBeanDefinition)
EventListenerFactory.class.isAssignableFrom(beanClass)) {
return false;
}
metadata = new StandardAnnotationMetadata(beanClass, true);
metadata = AnnotationMetadata.introspect(beanClass);
}
else {
try {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -16,9 +16,13 @@

package example.scannable_scoped;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.ScopedProxyMode;

@Retention(RetentionPolicy.RUNTIME)
public @interface MyScope {
String value() default BeanDefinition.SCOPE_SINGLETON;
ScopedProxyMode proxyMode() default ScopedProxyMode.DEFAULT;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -16,6 +16,9 @@

package org.springframework.context.annotation.configuration.spr9031;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

import org.junit.Test;

import org.springframework.beans.factory.annotation.Autowired;
Expand Down Expand Up @@ -75,5 +78,6 @@ static class LowLevelConfig {
@Autowired Spr9031Component scanned;
}

@Retention(RetentionPolicy.RUNTIME)
public @interface MarkerAnnotation {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -502,7 +502,7 @@ public static MultiValueMap<String, Object> getAllAnnotationAttributes(Annotated

Adapt[] adaptations = Adapt.values(classValuesAsString, nestedAnnotationsAsMap);
return getAnnotations(element).stream(annotationName)
.filter(MergedAnnotationPredicates.unique(AnnotatedElementUtils::parentAndType))
.filter(MergedAnnotationPredicates.unique(MergedAnnotation::getTypeHierarchy))
.map(MergedAnnotation::withNonMergedAttributes)
.collect(MergedAnnotationCollectors.toMultiValueMap(AnnotatedElementUtils::nullIfEmpty, adaptations));
}
Expand Down Expand Up @@ -775,13 +775,6 @@ private static MergedAnnotations findRepeatableAnnotations(AnnotatedElement elem
repeatableContainers, AnnotationFilter.PLAIN);
}

private static Object parentAndType(MergedAnnotation<Annotation> annotation) {
if (annotation.getParent() == null) {
return annotation.getType().getName();
}
return annotation.getParent().getType().getName() + ":" + annotation.getParent().getType().getName();
}

@Nullable
private static MultiValueMap<String, Object> nullIfEmpty(MultiValueMap<String, Object> map) {
return (map.isEmpty() ? null : map);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ final class AnnotationTypeMapping {

private final Class<? extends Annotation> annotationType;

private final List<Class<? extends Annotation>> annotationTypeHierarchy;

@Nullable
private final Annotation annotation;

Expand Down Expand Up @@ -84,6 +86,9 @@ final class AnnotationTypeMapping {
this.root = (parent != null ? parent.getRoot() : this);
this.depth = (parent == null ? 0 : parent.getDepth() + 1);
this.annotationType = annotationType;
this.annotationTypeHierarchy = merge(
parent != null ? parent.getAnnotationTypeHierarchy() : null,
annotationType);
this.annotation = annotation;
this.attributes = AttributeMethods.forAnnotationType(annotationType);
this.mirrorSets = new MirrorSets();
Expand All @@ -98,6 +103,16 @@ final class AnnotationTypeMapping {
}


private static <T> List<T> merge(@Nullable List<T> existing, T element) {
if (existing == null) {
return Collections.singletonList(element);
}
List<T> merged = new ArrayList<>(existing.size() + 1);
merged.addAll(existing);
merged.add(element);
return Collections.unmodifiableList(merged);
}

private Map<Method, List<Method>> resolveAliasedForTargets() {
Map<Method, List<Method>> aliasedBy = new HashMap<>();
for (int i = 0; i < this.attributes.size(); i++) {
Expand Down Expand Up @@ -372,6 +387,10 @@ Class<? extends Annotation> getAnnotationType() {
return this.annotationType;
}

List<Class<? extends Annotation>> getAnnotationTypeHierarchy() {
return this.annotationTypeHierarchy;
}

/**
* Get the source annotation for this mapping. This will be the
* meta-annotation, or {@code null} if this is the root mapping.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -905,7 +905,7 @@ private static Map<String, DefaultValueHolder> computeDefaultValues(
}
else {
// If we have nested annotations, we need them as nested maps
AnnotationAttributes attributes = MergedAnnotation.from(annotationType)
AnnotationAttributes attributes = MergedAnnotation.of(annotationType)
.asMap(annotation ->
new AnnotationAttributes(annotation.getType(), true), Adapt.ANNOTATION_TO_MAP);
for (Map.Entry<String, Object> element : attributes.entrySet()) {
Expand Down Expand Up @@ -1151,7 +1151,7 @@ public static Object getDefaultValue(
if (annotationType == null || !StringUtils.hasText(attributeName)) {
return null;
}
return MergedAnnotation.from(annotationType).getDefaultValue(attributeName).orElse(null);
return MergedAnnotation.of(annotationType).getDefaultValue(attributeName).orElse(null);
}

/**
Expand Down Expand Up @@ -1232,7 +1232,7 @@ public static <A extends Annotation> A synthesizeAnnotation(Map<String, Object>
Class<A> annotationType, @Nullable AnnotatedElement annotatedElement) {

try {
return MergedAnnotation.from(annotatedElement, annotationType, attributes).synthesize();
return MergedAnnotation.of(annotatedElement, annotationType, attributes).synthesize();
}
catch (NoSuchElementException | IllegalStateException ex) {
throw new IllegalArgumentException(ex);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
* @param <C> the context type
* @param <R> the result type
* @author Phillip Webb
* @since 5.2
* @see AnnotationsScanner
* @see TypeMappedAnnotations
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Proxy;
import java.util.EnumSet;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Optional;
Expand Down Expand Up @@ -72,6 +73,15 @@ public interface MergedAnnotation<A extends Annotation> {
*/
Class<A> getType();

/**
* Return a complete type hierarchy from this annotation to the
* {@link #getRoot() root}. Provides a useful way to uniquely identify a
* merged annotation instance.
* @return the type heirarchy for the annotation
* @see MergedAnnotationPredicates#unique(Function)
*/
List<Class<? extends Annotation>> getTypeHierarchy();

/**
* Determine if the annotation is present on the source. Considers
* {@linkplain #isDirectlyPresent() directly present} and
Expand Down Expand Up @@ -513,34 +523,34 @@ static <A extends Annotation> MergedAnnotation<A> from(@Nullable Object source,
}

/**
* Create a new {@link MergedAnnotation} instance from the specified
* Create a new {@link MergedAnnotation} instance of the specified
* annotation type. The resulting annotation will not have any attribute
* values but may still be used to query default values.
* @param annotationType the annotation type
* @return a {@link MergedAnnotation} instance for the annotation
*/
static <A extends Annotation> MergedAnnotation<A> from(Class<A> annotationType) {
return from(null, annotationType, null);
static <A extends Annotation> MergedAnnotation<A> of(Class<A> annotationType) {
return of(null, annotationType, null);
}

/**
* Create a new {@link MergedAnnotation} instance from the specified
* annotation type and attributes map.
* Create a new {@link MergedAnnotation} instance of the specified
* annotation type with attributes values supplied by a map.
* @param annotationType the annotation type
* @param attributes the annotation attributes or {@code null} if just default
* values should be used
* @return a {@link MergedAnnotation} instance for the annotation and attributes
* @see #from(AnnotatedElement, Class, Map)
* @see #of(AnnotatedElement, Class, Map)
*/
static <A extends Annotation> MergedAnnotation<A> from(
static <A extends Annotation> MergedAnnotation<A> of(
Class<A> annotationType, @Nullable Map<String, ?> attributes) {

return from(null, annotationType, attributes);
return of(null, annotationType, attributes);
}

/**
* Create a new {@link MergedAnnotation} instance from the specified
* annotation type and attributes map.
* Create a new {@link MergedAnnotation} instance of the specified
* annotation type with attributes values supplied by a map.
* @param source the source for the annotation. This source is used only for
* information and logging. It does not need to <em>actually</em> contain
* the specified annotations and it will not be searched.
Expand All @@ -549,10 +559,29 @@ static <A extends Annotation> MergedAnnotation<A> from(
* values should be used
* @return a {@link MergedAnnotation} instance for the annotation and attributes
*/
static <A extends Annotation> MergedAnnotation<A> from(
static <A extends Annotation> MergedAnnotation<A> of(
@Nullable AnnotatedElement source, Class<A> annotationType, @Nullable Map<String, ?> attributes) {

return TypeMappedAnnotation.from(source, annotationType, attributes);
return of(null, source, annotationType, attributes);
}

/**
* Create a new {@link MergedAnnotation} instance of the specified
* annotation type with attributes values supplied by a map.
* @param classLoader the class loader used to resolve class attributes
* @param source the source for the annotation. This source is used only for
* information and logging. It does not need to <em>actually</em> contain
* the specified annotations and it will not be searched.
* @param annotationType the annotation type
* @param attributes the annotation attributes or {@code null} if just default
* values should be used
* @return a {@link MergedAnnotation} instance for the annotation and attributes
*/
static <A extends Annotation> MergedAnnotation<A> of(
@Nullable ClassLoader classLoader, @Nullable Object source,
Class<A> annotationType, @Nullable Map<String, ?> attributes) {

return TypeMappedAnnotation.of(classLoader, source, annotationType, attributes);
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ public static <A extends Annotation> MergedAnnotationSelector<A> nearest() {
}

/**
* Select the first directly declared annotation when possible. If not direct
* Select the first directly declared annotation when possible. If no direct
* annotations are declared then the earliest annotation is selected.
* @return a selector that picks the first directly declared annotation whenever possible
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import java.lang.annotation.Inherited;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.function.Predicate;
import java.util.stream.Stream;

Expand Down Expand Up @@ -368,6 +369,26 @@ static MergedAnnotations from(@Nullable Object source, Annotation[] annotations,
return TypeMappedAnnotations.from(source, annotations, repeatableContainers, annotationFilter);
}

/**
* Create a new {@link MergedAnnotations} instance from the specified
* collection of directly present annotations. This method allows a
* {@link MergedAnnotations} instance to be created from annotations that
* are not necessarily loaded using reflection. The provided annotations
* must all be {@link MergedAnnotation#isDirectlyPresent() directly present}
* and must have a {@link MergedAnnotation#getAggregateIndex() aggregate
* index} of {@code 0}.
* <p>
* The resulting {@link MergedAnnotations} instance will contain both the
* specified annotations, and any meta-annotations that can be read using
* reflection.
* @param annotations the annotations to include
* @return a {@link MergedAnnotations} instance containing the annotations
* @see MergedAnnotation#of(ClassLoader, Object, Class, java.util.Map)
*/
static MergedAnnotations of(Collection<MergedAnnotation<?>> annotations) {
return MergedAnnotationsCollection.of(annotations);
}


/**
* Search strategies supported by
Expand Down
Loading