Remove own class reading infrastructure in favor of extensions to AnnotationMetadata.

We now remove fully our own class-reading visitors as AnnotationMetadata exposes getDeclaredMethods.
MethodsMetadataReaderFactory and MethodsMetadataReader remain deprecated bridging return types to retain backward compatibility.

Closes #2520
This commit is contained in:
Mark Paluch
2022-01-03 09:54:54 +01:00
committed by Jens Schauder
parent 2ee509b707
commit a56ef010da
13 changed files with 232 additions and 1297 deletions

View File

@@ -31,9 +31,9 @@ import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.core.log.LogMessage;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.MethodMetadata;
import org.springframework.data.type.MethodsMetadata;
import org.springframework.data.type.classreading.MethodsMetadataReaderFactory;
import org.springframework.core.type.classreading.SimpleMetadataReaderFactory;
import org.springframework.data.util.StreamUtils;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
@@ -133,7 +133,7 @@ class DefaultProjectionInformation implements ProjectionInformation {
private static final Log logger = LogFactory.getLog(PropertyDescriptorSource.class);
private final Class<?> type;
private final Optional<MethodsMetadata> metadata;
private final Optional<AnnotationMetadata> metadata;
/**
* Creates a new {@link PropertyDescriptorSource} for the given type.
@@ -179,15 +179,15 @@ class DefaultProjectionInformation implements ProjectionInformation {
}
/**
* Returns a {@link Stream} of {@link PropertyDescriptor} ordered following the given {@link MethodsMetadata} only
* returning methods seen by the given {@link MethodsMetadata}.
* Returns a {@link Stream} of {@link PropertyDescriptor} ordered following the given {@link AnnotationMetadata}
* only returning methods seen by the given {@link AnnotationMetadata}.
*
* @param source must not be {@literal null}.
* @param metadata must not be {@literal null}.
* @return
*/
private static Stream<PropertyDescriptor> filterAndOrder(Stream<PropertyDescriptor> source,
MethodsMetadata metadata) {
AnnotationMetadata metadata) {
var orderedMethods = getMethodOrder(metadata);
@@ -201,12 +201,12 @@ class DefaultProjectionInformation implements ProjectionInformation {
}
/**
* Returns a {@link Stream} of interfaces using the given {@link MethodsMetadata} as primary source for ordering.
* Returns a {@link Stream} of interfaces using the given {@link AnnotationMetadata} as primary source for ordering.
*
* @param metadata must not be {@literal null}.
* @return
*/
private Stream<Class<?>> fromMetadata(MethodsMetadata metadata) {
private Stream<Class<?>> fromMetadata(AnnotationMetadata metadata) {
return Arrays.stream(metadata.getInterfaceNames()).map(it -> findType(it, type.getInterfaces()));
}
@@ -220,20 +220,20 @@ class DefaultProjectionInformation implements ProjectionInformation {
}
/**
* Attempts to obtain {@link MethodsMetadata} from {@link Class}. Returns {@link Optional} containing
* {@link MethodsMetadata} if metadata was read successfully, {@link Optional#empty()} otherwise.
* Attempts to obtain {@link AnnotationMetadata} from {@link Class}. Returns {@link Optional} containing
* {@link AnnotationMetadata} if metadata was read successfully, {@link Optional#empty()} otherwise.
*
* @param type must not be {@literal null}.
* @return the optional {@link MethodsMetadata}.
* @return the optional {@link AnnotationMetadata}.
*/
private static Optional<MethodsMetadata> getMetadata(Class<?> type) {
private static Optional<AnnotationMetadata> getMetadata(Class<?> type) {
try {
var factory = new MethodsMetadataReaderFactory(type.getClassLoader());
var factory = new SimpleMetadataReaderFactory(type.getClassLoader());
var metadataReader = factory.getMetadataReader(ClassUtils.getQualifiedName(type));
return Optional.of(metadataReader.getMethodsMetadata());
return Optional.of(metadataReader.getAnnotationMetadata());
} catch (IOException e) {
@@ -259,18 +259,18 @@ class DefaultProjectionInformation implements ProjectionInformation {
}
/**
* Returns a {@link Map} containing method name to its positional index according to {@link MethodsMetadata}.
* Returns a {@link Map} containing method name to its positional index according to {@link AnnotationMetadata}.
*
* @param metadata
* @return
*/
private static Map<String, Integer> getMethodOrder(MethodsMetadata metadata) {
private static Map<String, Integer> getMethodOrder(AnnotationMetadata metadata) {
var methods = metadata.getMethods() //
var methods = metadata.getDeclaredMethods() //
.stream() //
.map(MethodMetadata::getMethodName) //
.distinct() //
.collect(Collectors.toList());
.toList();
return IntStream.range(0, methods.size()) //
.boxed() //

View File

@@ -17,8 +17,10 @@ package org.springframework.data.type;
import java.util.Set;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.ClassMetadata;
import org.springframework.core.type.MethodMetadata;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.data.type.classreading.MethodsMetadataReader;
/**
@@ -30,7 +32,10 @@ import org.springframework.data.type.classreading.MethodsMetadataReader;
* @see MethodMetadata
* @see ClassMetadata
* @see MethodsMetadataReader#getMethodsMetadata()
* @deprecated since 3.0, use {@link MetadataReader} directly to obtain {@link AnnotationMetadata#getDeclaredMethods()
* declared methods} directly.
*/
@Deprecated
public interface MethodsMetadata extends ClassMetadata {
/**

View File

@@ -1,99 +0,0 @@
/*
* Copyright 2021 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.
* 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 org.springframework.data.type.classreading;
import java.lang.reflect.Field;
import java.security.AccessControlException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.asm.AnnotationVisitor;
import org.springframework.asm.SpringAsmInfo;
import org.springframework.asm.Type;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.lang.Nullable;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;
/**
* {@link AnnotationVisitor} to recursively visit annotations.
*
* @author Chris Beams
* @author Juergen Hoeller
* @author Phillip Webb
* @author Sam Brannen
* @since 3.1.1
* @deprecated As of Spring Framework 5.2, this class and related classes in this package have been replaced by
* {@link SimpleAnnotationMetadataReadingVisitor} and related classes for internal use within the framework.
*/
@Deprecated
abstract class AbstractRecursiveAnnotationVisitor extends AnnotationVisitor {
protected final Log logger = LogFactory.getLog(getClass());
protected final AnnotationAttributes attributes;
@Nullable protected final ClassLoader classLoader;
public AbstractRecursiveAnnotationVisitor(@Nullable ClassLoader classLoader, AnnotationAttributes attributes) {
super(SpringAsmInfo.ASM_VERSION);
this.classLoader = classLoader;
this.attributes = attributes;
}
@Override
public void visit(String attributeName, Object attributeValue) {
this.attributes.put(attributeName, attributeValue);
}
@Override
public AnnotationVisitor visitAnnotation(String attributeName, String asmTypeDescriptor) {
String annotationType = Type.getType(asmTypeDescriptor).getClassName();
AnnotationAttributes nestedAttributes = new AnnotationAttributes(annotationType, this.classLoader);
this.attributes.put(attributeName, nestedAttributes);
return new RecursiveAnnotationAttributesVisitor(annotationType, nestedAttributes, this.classLoader);
}
@Override
public AnnotationVisitor visitArray(String attributeName) {
return new RecursiveAnnotationArrayVisitor(attributeName, this.attributes, this.classLoader);
}
@Override
public void visitEnum(String attributeName, String asmTypeDescriptor, String attributeValue) {
Object newValue = getEnumValue(asmTypeDescriptor, attributeValue);
visit(attributeName, newValue);
}
protected Object getEnumValue(String asmTypeDescriptor, String attributeValue) {
Object valueToUse = attributeValue;
try {
Class<?> enumType = ClassUtils.forName(Type.getType(asmTypeDescriptor).getClassName(), this.classLoader);
Field enumConstant = ReflectionUtils.findField(enumType, attributeValue);
if (enumConstant != null) {
ReflectionUtils.makeAccessible(enumConstant);
valueToUse = enumConstant.get(null);
}
} catch (ClassNotFoundException | NoClassDefFoundError ex) {
logger.debug("Failed to classload enum type while reading annotation metadata", ex);
} catch (IllegalAccessException | AccessControlException ex) {
logger.debug("Could not access enum value while reading annotation metadata", ex);
}
return valueToUse;
}
}

View File

@@ -1,119 +0,0 @@
/*
* Copyright 2021 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.
* 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 org.springframework.data.type.classreading;
import java.lang.annotation.Annotation;
import java.lang.reflect.Modifier;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.lang.Nullable;
import org.springframework.util.MultiValueMap;
import org.springframework.util.ObjectUtils;
/**
* ASM visitor which looks for annotations defined on a class or method, including meta-annotations.
* <p>
* This visitor is fully recursive, taking into account any nested annotations or nested annotation arrays.
*
* @author Juergen Hoeller
* @author Chris Beams
* @author Phillip Webb
* @author Sam Brannen
* @since 3.0
* @deprecated As of Spring Framework 5.2, this class and related classes in this package have been replaced by
* {@link SimpleAnnotationMetadataReadingVisitor} and related classes for internal use within the framework.
*/
@Deprecated
final class AnnotationAttributesReadingVisitor extends RecursiveAnnotationAttributesVisitor {
private final MultiValueMap<String, AnnotationAttributes> attributesMap;
private final Map<String, Set<String>> metaAnnotationMap;
public AnnotationAttributesReadingVisitor(String annotationType,
MultiValueMap<String, AnnotationAttributes> attributesMap, Map<String, Set<String>> metaAnnotationMap,
@Nullable ClassLoader classLoader) {
super(annotationType, new AnnotationAttributes(annotationType, classLoader), classLoader);
this.attributesMap = attributesMap;
this.metaAnnotationMap = metaAnnotationMap;
}
@Override
public void visitEnd() {
super.visitEnd();
Class<? extends Annotation> annotationClass = this.attributes.annotationType();
if (annotationClass != null) {
List<AnnotationAttributes> attributeList = this.attributesMap.get(this.annotationType);
if (attributeList == null) {
this.attributesMap.add(this.annotationType, this.attributes);
} else {
attributeList.add(0, this.attributes);
}
if (!AnnotationUtils.isInJavaLangAnnotationPackage(annotationClass.getName())) {
try {
Annotation[] metaAnnotations = annotationClass.getAnnotations();
if (!ObjectUtils.isEmpty(metaAnnotations)) {
Set<Annotation> visited = new LinkedHashSet<>();
for (Annotation metaAnnotation : metaAnnotations) {
recursivelyCollectMetaAnnotations(visited, metaAnnotation);
}
if (!visited.isEmpty()) {
Set<String> metaAnnotationTypeNames = new LinkedHashSet<>(visited.size());
for (Annotation ann : visited) {
metaAnnotationTypeNames.add(ann.annotationType().getName());
}
this.metaAnnotationMap.put(annotationClass.getName(), metaAnnotationTypeNames);
}
}
} catch (Throwable ex) {
if (logger.isDebugEnabled()) {
logger.debug("Failed to introspect meta-annotations on " + annotationClass + ": " + ex);
}
}
}
}
}
private void recursivelyCollectMetaAnnotations(Set<Annotation> visited, Annotation annotation) {
Class<? extends Annotation> annotationType = annotation.annotationType();
String annotationName = annotationType.getName();
if (!AnnotationUtils.isInJavaLangAnnotationPackage(annotationName) && visited.add(annotation)) {
try {
// Only do attribute scanning for public annotations; we'd run into
// IllegalAccessExceptions otherwise, and we don't want to mess with
// accessibility in a SecurityManager environment.
if (Modifier.isPublic(annotationType.getModifiers())) {
this.attributesMap.add(annotationName, AnnotationUtils.getAnnotationAttributes(annotation, false, true));
}
for (Annotation metaMetaAnnotation : annotationType.getAnnotations()) {
recursivelyCollectMetaAnnotations(visited, metaMetaAnnotation);
}
} catch (Throwable ex) {
if (logger.isDebugEnabled()) {
logger.debug("Failed to introspect meta-annotations on " + annotation + ": " + ex);
}
}
}
}
}

View File

@@ -1,190 +0,0 @@
/*
* Copyright 2021 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.
* 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 org.springframework.data.type.classreading;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.springframework.asm.AnnotationVisitor;
import org.springframework.asm.MethodVisitor;
import org.springframework.asm.Opcodes;
import org.springframework.asm.Type;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.annotation.MergedAnnotations;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.MethodMetadata;
import org.springframework.lang.Nullable;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
/**
* ASM class visitor which looks for the class name and implemented types as well as for the annotations defined on the
* class, exposing them through the {@link org.springframework.core.type.AnnotationMetadata} interface.
*
* @author Juergen Hoeller
* @author Mark Fisher
* @author Costin Leau
* @author Phillip Webb
* @author Sam Brannen
*/
@Deprecated
class AnnotationMetadataReadingVisitor extends ClassMetadataReadingVisitor implements AnnotationMetadata {
@Nullable protected final ClassLoader classLoader;
protected final Set<String> annotationSet = new LinkedHashSet<>(4);
protected final Map<String, Set<String>> metaAnnotationMap = new LinkedHashMap<>(4);
/**
* Declared as a {@link LinkedMultiValueMap} instead of a {@link MultiValueMap} to ensure that the hierarchical
* ordering of the entries is preserved.
*
* @see AnnotationReadingVisitorUtils#getMergedAnnotationAttributes
*/
protected final LinkedMultiValueMap<String, AnnotationAttributes> attributesMap = new LinkedMultiValueMap<>(3);
protected final Set<MethodMetadata> methodMetadataSet = new LinkedHashSet<>(4);
public AnnotationMetadataReadingVisitor(@Nullable ClassLoader classLoader) {
this.classLoader = classLoader;
}
@Override
public MergedAnnotations getAnnotations() {
throw new UnsupportedOperationException();
}
@Override
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
// Skip bridge methods - we're only interested in original annotation-defining user methods.
// On JDK 8, we'd otherwise run into double detection of the same annotated method...
if ((access & Opcodes.ACC_BRIDGE) != 0) {
return super.visitMethod(access, name, desc, signature, exceptions);
}
return new MethodMetadataReadingVisitor(name, access, getClassName(), Type.getReturnType(desc).getClassName(),
this.classLoader, this.methodMetadataSet);
}
@Override
@Nullable
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
if (!visible) {
return null;
}
String className = Type.getType(desc).getClassName();
if (AnnotationUtils.isInJavaLangAnnotationPackage(className)) {
return null;
}
this.annotationSet.add(className);
return new AnnotationAttributesReadingVisitor(className, this.attributesMap, this.metaAnnotationMap,
this.classLoader);
}
@Override
public Set<String> getAnnotationTypes() {
return this.annotationSet;
}
@Override
public Set<String> getMetaAnnotationTypes(String annotationName) {
Set<String> metaAnnotationTypes = this.metaAnnotationMap.get(annotationName);
return (metaAnnotationTypes != null ? metaAnnotationTypes : Collections.emptySet());
}
@Override
public boolean hasMetaAnnotation(String metaAnnotationType) {
if (AnnotationUtils.isInJavaLangAnnotationPackage(metaAnnotationType)) {
return false;
}
Collection<Set<String>> allMetaTypes = this.metaAnnotationMap.values();
for (Set<String> metaTypes : allMetaTypes) {
if (metaTypes.contains(metaAnnotationType)) {
return true;
}
}
return false;
}
@Override
public boolean isAnnotated(String annotationName) {
return (!AnnotationUtils.isInJavaLangAnnotationPackage(annotationName)
&& this.attributesMap.containsKey(annotationName));
}
@Override
public boolean hasAnnotation(String annotationName) {
return getAnnotationTypes().contains(annotationName);
}
@Override
@Nullable
public AnnotationAttributes getAnnotationAttributes(String annotationName, boolean classValuesAsString) {
AnnotationAttributes raw = AnnotationReadingVisitorUtils.getMergedAnnotationAttributes(this.attributesMap,
this.metaAnnotationMap, annotationName);
if (raw == null) {
return null;
}
return AnnotationReadingVisitorUtils.convertClassValues("class '" + getClassName() + "'", this.classLoader, raw,
classValuesAsString);
}
@Override
@Nullable
public MultiValueMap<String, Object> getAllAnnotationAttributes(String annotationName, boolean classValuesAsString) {
MultiValueMap<String, Object> allAttributes = new LinkedMultiValueMap<>();
List<AnnotationAttributes> attributes = this.attributesMap.get(annotationName);
if (attributes == null) {
return null;
}
String annotatedElement = "class '" + getClassName() + "'";
for (AnnotationAttributes raw : attributes) {
for (Map.Entry<String, Object> entry : AnnotationReadingVisitorUtils
.convertClassValues(annotatedElement, this.classLoader, raw, classValuesAsString).entrySet()) {
allAttributes.add(entry.getKey(), entry.getValue());
}
}
return allAttributes;
}
@Override
public boolean hasAnnotatedMethods(String annotationName) {
for (MethodMetadata methodMetadata : this.methodMetadataSet) {
if (methodMetadata.isAnnotated(annotationName)) {
return true;
}
}
return false;
}
@Override
public Set<MethodMetadata> getAnnotatedMethods(String annotationName) {
Set<MethodMetadata> annotatedMethods = new LinkedHashSet<>(4);
for (MethodMetadata methodMetadata : this.methodMetadataSet) {
if (methodMetadata.isAnnotated(annotationName)) {
annotatedMethods.add(methodMetadata);
}
}
return annotatedMethods;
}
}

View File

@@ -1,160 +0,0 @@
/*
* Copyright 2021 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.
* 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 org.springframework.data.type.classreading;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.springframework.asm.Type;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.lang.Nullable;
import org.springframework.util.ClassUtils;
import org.springframework.util.CollectionUtils;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.ObjectUtils;
/**
* Internal utility class used when reading annotations via ASM.
*
* @author Juergen Hoeller
* @author Mark Fisher
* @author Costin Leau
* @author Phillip Webb
* @author Sam Brannen
* @since 4.0
*/
@Deprecated
abstract class AnnotationReadingVisitorUtils {
public static AnnotationAttributes convertClassValues(Object annotatedElement, @Nullable ClassLoader classLoader,
AnnotationAttributes original, boolean classValuesAsString) {
AnnotationAttributes result = new AnnotationAttributes(original);
AnnotationUtils.postProcessAnnotationAttributes(annotatedElement, result, classValuesAsString);
for (Map.Entry<String, Object> entry : result.entrySet()) {
try {
Object value = entry.getValue();
if (value instanceof AnnotationAttributes) {
value = convertClassValues(annotatedElement, classLoader, (AnnotationAttributes) value, classValuesAsString);
} else if (value instanceof AnnotationAttributes[]) {
AnnotationAttributes[] values = (AnnotationAttributes[]) value;
for (int i = 0; i < values.length; i++) {
values[i] = convertClassValues(annotatedElement, classLoader, values[i], classValuesAsString);
}
value = values;
} else if (value instanceof Type) {
value = (classValuesAsString ? ((Type) value).getClassName()
: ClassUtils.forName(((Type) value).getClassName(), classLoader));
} else if (value instanceof Type[]) {
Type[] array = (Type[]) value;
Object[] convArray = (classValuesAsString ? new String[array.length] : new Class<?>[array.length]);
for (int i = 0; i < array.length; i++) {
convArray[i] = (classValuesAsString ? array[i].getClassName()
: ClassUtils.forName(array[i].getClassName(), classLoader));
}
value = convArray;
} else if (classValuesAsString) {
if (value instanceof Class) {
value = ((Class<?>) value).getName();
} else if (value instanceof Class[]) {
Class<?>[] clazzArray = (Class<?>[]) value;
String[] newValue = new String[clazzArray.length];
for (int i = 0; i < clazzArray.length; i++) {
newValue[i] = clazzArray[i].getName();
}
value = newValue;
}
}
entry.setValue(value);
} catch (Throwable ex) {
// Class not found - can't resolve class reference in annotation attribute.
result.put(entry.getKey(), ex);
}
}
return result;
}
/**
* Retrieve the merged attributes of the annotation of the given type, if any, from the supplied
* {@code attributesMap}.
* <p>
* Annotation attribute values appearing <em>lower</em> in the annotation hierarchy (i.e., closer to the declaring
* class) will override those defined <em>higher</em> in the annotation hierarchy.
*
* @param attributesMap the map of annotation attribute lists, keyed by annotation type name
* @param metaAnnotationMap the map of meta annotation relationships, keyed by annotation type name
* @param annotationName the fully qualified class name of the annotation type to look for
* @return the merged annotation attributes, or {@code null} if no matching annotation is present in the
* {@code attributesMap}
* @since 4.0.3
*/
@Nullable
public static AnnotationAttributes getMergedAnnotationAttributes(
LinkedMultiValueMap<String, AnnotationAttributes> attributesMap, Map<String, Set<String>> metaAnnotationMap,
String annotationName) {
// Get the unmerged list of attributes for the target annotation.
List<AnnotationAttributes> attributesList = attributesMap.get(annotationName);
if (CollectionUtils.isEmpty(attributesList)) {
return null;
}
// To start with, we populate the result with a copy of all attribute values
// from the target annotation. A copy is necessary so that we do not
// inadvertently mutate the state of the metadata passed to this method.
AnnotationAttributes result = new AnnotationAttributes(attributesList.get(0));
Set<String> overridableAttributeNames = new HashSet<>(result.keySet());
overridableAttributeNames.remove(AnnotationUtils.VALUE);
// Since the map is a LinkedMultiValueMap, we depend on the ordering of
// elements in the map and reverse the order of the keys in order to traverse
// "down" the annotation hierarchy.
List<String> annotationTypes = new ArrayList<>(attributesMap.keySet());
Collections.reverse(annotationTypes);
// No need to revisit the target annotation type:
annotationTypes.remove(annotationName);
for (String currentAnnotationType : annotationTypes) {
List<AnnotationAttributes> currentAttributesList = attributesMap.get(currentAnnotationType);
if (!ObjectUtils.isEmpty(currentAttributesList)) {
Set<String> metaAnns = metaAnnotationMap.get(currentAnnotationType);
if (metaAnns != null && metaAnns.contains(annotationName)) {
AnnotationAttributes currentAttributes = currentAttributesList.get(0);
for (String overridableAttributeName : overridableAttributeNames) {
Object value = currentAttributes.get(overridableAttributeName);
if (value != null) {
// Store the value, potentially overriding a value from an attribute
// of the same name found higher in the annotation hierarchy.
result.put(overridableAttributeName, value);
}
}
}
}
}
return result;
}
}

View File

@@ -1,230 +0,0 @@
/*
* Copyright 2021 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.
* 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 org.springframework.data.type.classreading;
import java.util.LinkedHashSet;
import java.util.Set;
import org.springframework.asm.AnnotationVisitor;
import org.springframework.asm.Attribute;
import org.springframework.asm.ClassVisitor;
import org.springframework.asm.FieldVisitor;
import org.springframework.asm.MethodVisitor;
import org.springframework.asm.Opcodes;
import org.springframework.asm.SpringAsmInfo;
import org.springframework.core.type.ClassMetadata;
import org.springframework.lang.Nullable;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
/**
* ASM class visitor which looks only for the class name and implemented types, exposing them through the
* {@link org.springframework.core.type.ClassMetadata} interface.
*
* @author Rod Johnson
* @author Costin Leau
* @author Mark Fisher
* @author Ramnivas Laddad
* @author Chris Beams
* @since 2.5
*/
@Deprecated
class ClassMetadataReadingVisitor extends ClassVisitor implements ClassMetadata {
private String className = "";
private boolean isInterface;
private boolean isAnnotation;
private boolean isAbstract;
private boolean isFinal;
@Nullable private String enclosingClassName;
private boolean independentInnerClass;
@Nullable private String superClassName;
private String[] interfaces = new String[0];
private Set<String> memberClassNames = new LinkedHashSet<>(4);
public ClassMetadataReadingVisitor() {
super(SpringAsmInfo.ASM_VERSION);
}
@Override
public void visit(int version, int access, String name, String signature, @Nullable String supername,
String[] interfaces) {
this.className = ClassUtils.convertResourcePathToClassName(name);
this.isInterface = ((access & Opcodes.ACC_INTERFACE) != 0);
this.isAnnotation = ((access & Opcodes.ACC_ANNOTATION) != 0);
this.isAbstract = ((access & Opcodes.ACC_ABSTRACT) != 0);
this.isFinal = ((access & Opcodes.ACC_FINAL) != 0);
if (supername != null && !this.isInterface) {
this.superClassName = ClassUtils.convertResourcePathToClassName(supername);
}
this.interfaces = new String[interfaces.length];
for (int i = 0; i < interfaces.length; i++) {
this.interfaces[i] = ClassUtils.convertResourcePathToClassName(interfaces[i]);
}
}
@Override
public void visitOuterClass(String owner, String name, String desc) {
this.enclosingClassName = ClassUtils.convertResourcePathToClassName(owner);
}
@Override
public void visitInnerClass(String name, @Nullable String outerName, String innerName, int access) {
if (outerName != null) {
String fqName = ClassUtils.convertResourcePathToClassName(name);
String fqOuterName = ClassUtils.convertResourcePathToClassName(outerName);
if (this.className.equals(fqName)) {
this.enclosingClassName = fqOuterName;
this.independentInnerClass = ((access & Opcodes.ACC_STATIC) != 0);
} else if (this.className.equals(fqOuterName)) {
this.memberClassNames.add(fqName);
}
}
}
@Override
public void visitSource(String source, String debug) {
// no-op
}
@Override
@Nullable
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
// no-op
return new EmptyAnnotationVisitor();
}
@Override
public void visitAttribute(Attribute attr) {
// no-op
}
@Override
public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
// no-op
return new EmptyFieldVisitor();
}
@Override
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
// no-op
return new EmptyMethodVisitor();
}
@Override
public void visitEnd() {
// no-op
}
@Override
public String getClassName() {
return this.className;
}
@Override
public boolean isInterface() {
return this.isInterface;
}
@Override
public boolean isAnnotation() {
return this.isAnnotation;
}
@Override
public boolean isAbstract() {
return this.isAbstract;
}
@Override
public boolean isFinal() {
return this.isFinal;
}
@Override
public boolean isIndependent() {
return (this.enclosingClassName == null || this.independentInnerClass);
}
@Override
public boolean hasEnclosingClass() {
return (this.enclosingClassName != null);
}
@Override
@Nullable
public String getEnclosingClassName() {
return this.enclosingClassName;
}
@Override
@Nullable
public String getSuperClassName() {
return this.superClassName;
}
@Override
public String[] getInterfaceNames() {
return this.interfaces;
}
@Override
public String[] getMemberClassNames() {
return StringUtils.toStringArray(this.memberClassNames);
}
private static class EmptyAnnotationVisitor extends AnnotationVisitor {
public EmptyAnnotationVisitor() {
super(SpringAsmInfo.ASM_VERSION);
}
@Override
public AnnotationVisitor visitAnnotation(String name, String desc) {
return this;
}
@Override
public AnnotationVisitor visitArray(String name) {
return this;
}
}
private static class EmptyMethodVisitor extends MethodVisitor {
public EmptyMethodVisitor() {
super(SpringAsmInfo.ASM_VERSION);
}
}
private static class EmptyFieldVisitor extends FieldVisitor {
public EmptyFieldVisitor() {
super(SpringAsmInfo.ASM_VERSION);
}
}
}

View File

@@ -1,162 +0,0 @@
/*
* Copyright 2018-2021 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.
* 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 org.springframework.data.type.classreading;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collections;
import java.util.Set;
import org.springframework.asm.ClassReader;
import org.springframework.asm.MethodVisitor;
import org.springframework.asm.Opcodes;
import org.springframework.asm.Type;
import org.springframework.core.NestedIOException;
import org.springframework.core.io.Resource;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.ClassMetadata;
import org.springframework.core.type.MethodMetadata;
import org.springframework.data.type.MethodsMetadata;
import org.springframework.data.util.StreamUtils;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/**
* {@link MethodsMetadataReader} implementation based on an ASM {@link org.springframework.asm.ClassReader}.
*
* @author Mark Paluch
* @author Oliver Gierke
* @since 2.1
*/
class DefaultMethodsMetadataReader implements MethodsMetadataReader {
private final Resource resource;
private final ClassMetadata classMetadata;
private final AnnotationMetadata annotationMetadata;
private final MethodsMetadata methodsMetadata;
DefaultMethodsMetadataReader(Resource resource, @Nullable ClassLoader classLoader) throws IOException {
var visitor = new MethodsMetadataReadingVisitor(classLoader);
createClassReader(resource).accept(visitor, ClassReader.SKIP_DEBUG);
this.resource = resource;
this.classMetadata = visitor;
this.annotationMetadata = visitor;
this.methodsMetadata = visitor;
}
private static ClassReader createClassReader(Resource resource) throws IOException {
try (InputStream is = new BufferedInputStream(resource.getInputStream())) {
return new ClassReader(is);
} catch (IllegalArgumentException ex) {
throw new NestedIOException("ASM ClassReader failed to parse class file - "
+ "probably due to a new Java class file version that isn't supported yet: " + resource, ex);
}
}
public Resource getResource() {
return this.resource;
}
public ClassMetadata getClassMetadata() {
return this.classMetadata;
}
public AnnotationMetadata getAnnotationMetadata() {
return this.annotationMetadata;
}
public MethodsMetadata getMethodsMetadata() {
return this.methodsMetadata;
}
/**
* ASM class visitor which looks for the class name and implemented types as well as for the methods defined in the
* class, exposing them through the {@link MethodsMetadata} interface.
*
* @author Mark Paluch
* @since 2.1
* @see ClassMetadata
* @see MethodMetadata
* @see MethodMetadataReadingVisitor
*/
private static class MethodsMetadataReadingVisitor extends AnnotationMetadataReadingVisitor
implements MethodsMetadata {
/**
* Construct a new {@link MethodsMetadataReadingVisitor} given {@link ClassLoader}.
*
* @param classLoader may be {@literal null}.
*/
MethodsMetadataReadingVisitor(@Nullable ClassLoader classLoader) {
super(classLoader);
}
/*
* (non-Javadoc)
* @see org.springframework.core.type.classreading.AnnotationMetadataReadingVisitor#visitMethod(int, java.lang.String, java.lang.String, java.lang.String, java.lang.String[])
*/
@Override
@SuppressWarnings("null")
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
// Skip bridge methods - we're only interested in original user methods.
// On JDK 8, we'd otherwise run into double detection of the same method...
if ((access & Opcodes.ACC_BRIDGE) != 0) {
return super.visitMethod(access, name, desc, signature, exceptions);
}
// Skip constructors
if (name.equals("<init>")) {
return super.visitMethod(access, name, desc, signature, exceptions);
}
var visitor = new MethodMetadataReadingVisitor(name, access, getClassName(),
Type.getReturnType(desc).getClassName(), this.classLoader, this.methodMetadataSet);
this.methodMetadataSet.add(visitor);
return visitor;
}
/*
* (non-Javadoc)
* @see org.springframework.data.util.MethodsMetadata#getMethods()
*/
@Override
public Set<MethodMetadata> getMethods() {
return Collections.unmodifiableSet(methodMetadataSet);
}
/*
* (non-Javadoc)
* @see org.springframework.data.util.MethodsMetadata#getMethods(String)
*/
@Override
public Set<MethodMetadata> getMethods(String name) {
Assert.hasText(name, "Method name must not be null or empty");
return methodMetadataSet.stream() //
.filter(it -> it.getMethodName().equals(name)) //
.collect(StreamUtils.toUnmodifiableSet());
}
}
}

View File

@@ -1,165 +0,0 @@
/*
* Copyright 2021 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.
* 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 org.springframework.data.type.classreading;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.springframework.asm.AnnotationVisitor;
import org.springframework.asm.MethodVisitor;
import org.springframework.asm.Opcodes;
import org.springframework.asm.SpringAsmInfo;
import org.springframework.asm.Type;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.annotation.MergedAnnotations;
import org.springframework.core.type.MethodMetadata;
import org.springframework.lang.Nullable;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
/**
* ASM method visitor which looks for the annotations defined on a method, exposing them through the
* {@link org.springframework.core.type.MethodMetadata} interface.
*
* @author Juergen Hoeller
* @author Mark Pollack
* @author Costin Leau
* @author Chris Beams
* @author Phillip Webb
* @since 3.0
*/
@Deprecated
class MethodMetadataReadingVisitor extends MethodVisitor implements MethodMetadata {
protected final String methodName;
protected final int access;
protected final String declaringClassName;
protected final String returnTypeName;
@Nullable protected final ClassLoader classLoader;
protected final Set<MethodMetadata> methodMetadataSet;
protected final Map<String, Set<String>> metaAnnotationMap = new LinkedHashMap<>(4);
protected final LinkedMultiValueMap<String, AnnotationAttributes> attributesMap = new LinkedMultiValueMap<>(3);
public MethodMetadataReadingVisitor(String methodName, int access, String declaringClassName, String returnTypeName,
@Nullable ClassLoader classLoader, Set<MethodMetadata> methodMetadataSet) {
super(SpringAsmInfo.ASM_VERSION);
this.methodName = methodName;
this.access = access;
this.declaringClassName = declaringClassName;
this.returnTypeName = returnTypeName;
this.classLoader = classLoader;
this.methodMetadataSet = methodMetadataSet;
}
@Override
public MergedAnnotations getAnnotations() {
throw new UnsupportedOperationException();
}
@Override
@Nullable
public AnnotationVisitor visitAnnotation(final String desc, boolean visible) {
if (!visible) {
return null;
}
this.methodMetadataSet.add(this);
String className = Type.getType(desc).getClassName();
return new AnnotationAttributesReadingVisitor(className, this.attributesMap, this.metaAnnotationMap,
this.classLoader);
}
@Override
public String getMethodName() {
return this.methodName;
}
@Override
public boolean isAbstract() {
return ((this.access & Opcodes.ACC_ABSTRACT) != 0);
}
@Override
public boolean isStatic() {
return ((this.access & Opcodes.ACC_STATIC) != 0);
}
@Override
public boolean isFinal() {
return ((this.access & Opcodes.ACC_FINAL) != 0);
}
@Override
public boolean isOverridable() {
return (!isStatic() && !isFinal() && ((this.access & Opcodes.ACC_PRIVATE) == 0));
}
@Override
public boolean isAnnotated(String annotationName) {
return this.attributesMap.containsKey(annotationName);
}
@Override
@Nullable
public AnnotationAttributes getAnnotationAttributes(String annotationName, boolean classValuesAsString) {
AnnotationAttributes raw = AnnotationReadingVisitorUtils.getMergedAnnotationAttributes(this.attributesMap,
this.metaAnnotationMap, annotationName);
if (raw == null) {
return null;
}
return AnnotationReadingVisitorUtils.convertClassValues("method '" + getMethodName() + "'", this.classLoader, raw,
classValuesAsString);
}
@Override
@Nullable
public MultiValueMap<String, Object> getAllAnnotationAttributes(String annotationName, boolean classValuesAsString) {
if (!this.attributesMap.containsKey(annotationName)) {
return null;
}
MultiValueMap<String, Object> allAttributes = new LinkedMultiValueMap<>();
List<AnnotationAttributes> attributesList = this.attributesMap.get(annotationName);
if (attributesList != null) {
String annotatedElement = "method '" + getMethodName() + '\'';
for (AnnotationAttributes annotationAttributes : attributesList) {
AnnotationAttributes convertedAttributes = AnnotationReadingVisitorUtils.convertClassValues(annotatedElement,
this.classLoader, annotationAttributes, classValuesAsString);
convertedAttributes.forEach(allAttributes::add);
}
}
return allAttributes;
}
@Override
public String getDeclaringClassName() {
return this.declaringClassName;
}
@Override
public String getReturnTypeName() {
return this.returnTypeName;
}
}

View File

@@ -15,6 +15,7 @@
*/
package org.springframework.data.type.classreading;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.data.type.MethodsMetadata;
@@ -24,7 +25,10 @@ import org.springframework.data.type.MethodsMetadata;
*
* @author Mark Paluch
* @since 2.1
* @deprecated since 3.0, use {@link MetadataReader} to obtain {@link AnnotationMetadata#getDeclaredMethods() declared
* methods} directly.
*/
@Deprecated
public interface MethodsMetadataReader extends MetadataReader {
/**

View File

@@ -16,9 +16,15 @@
package org.springframework.data.type.classreading;
import java.io.IOException;
import java.util.Set;
import java.util.stream.Collectors;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.ClassMetadata;
import org.springframework.core.type.MethodMetadata;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.SimpleMetadataReaderFactory;
import org.springframework.data.type.MethodsMetadata;
import org.springframework.lang.Nullable;
@@ -29,7 +35,9 @@ import org.springframework.lang.Nullable;
*
* @author Mark Paluch
* @since 2.1
* @deprecated since 3.0. Use {@link SimpleMetadataReaderFactory} directly.
*/
@Deprecated
public class MethodsMetadataReaderFactory extends SimpleMetadataReaderFactory {
/**
@@ -61,7 +69,7 @@ public class MethodsMetadataReaderFactory extends SimpleMetadataReaderFactory {
*/
@Override
public MethodsMetadataReader getMetadataReader(String className) throws IOException {
return (MethodsMetadataReader) super.getMetadataReader(className);
return new MetadataReaderWrapper(super.getMetadataReader(className));
}
/*
@@ -70,6 +78,201 @@ public class MethodsMetadataReaderFactory extends SimpleMetadataReaderFactory {
*/
@Override
public MethodsMetadataReader getMetadataReader(Resource resource) throws IOException {
return new DefaultMethodsMetadataReader(resource, getResourceLoader().getClassLoader());
return new MetadataReaderWrapper(super.getMetadataReader(resource));
}
private static class MetadataReaderWrapper implements MethodsMetadataReader {
private final MetadataReader delegate;
MetadataReaderWrapper(MetadataReader delegate) {
this.delegate = delegate;
}
/*
* (non-Javadoc)
* @see org.springframework.data.type.classreading.MethodsMetadataReader#getMethodsMetadata()
*/
@Override
public MethodsMetadata getMethodsMetadata() {
return new MethodsMetadataWrapper(getAnnotationMetadata(), getClassMetadata());
}
/*
* (non-Javadoc)
* @see org.springframework.core.type.classreading.MetadataReader#getResource()
*/
@Override
public Resource getResource() {
return delegate.getResource();
}
/*
* (non-Javadoc)
* @see org.springframework.core.type.classreading.MetadataReader#getClassMetadata()
*/
@Override
public ClassMetadata getClassMetadata() {
return delegate.getClassMetadata();
}
/*
* (non-Javadoc)
* @see org.springframework.core.type.classreading.MetadataReader#getAnnotationMetadata()
*/
@Override
public AnnotationMetadata getAnnotationMetadata() {
return delegate.getAnnotationMetadata();
}
}
private static class MethodsMetadataWrapper implements MethodsMetadata, ClassMetadata {
private final AnnotationMetadata annotationMetadata;
private final ClassMetadata classMetadata;
MethodsMetadataWrapper(AnnotationMetadata annotationMetadata, ClassMetadata classMetadata) {
this.annotationMetadata = annotationMetadata;
this.classMetadata = classMetadata;
}
/*
* (non-Javadoc)
* @see org.springframework.data.type.MethodsMetadata#getMethods()
*/
@Override
public Set<MethodMetadata> getMethods() {
return annotationMetadata.getDeclaredMethods();
}
/*
* (non-Javadoc)
* @see org.springframework.data.type.MethodsMetadata#getMethods(java.lang.String)
*/
@Override
public Set<MethodMetadata> getMethods(String name) {
return annotationMetadata.getDeclaredMethods().stream().filter(it -> it.getMethodName().equals(name))
.collect(Collectors.toSet());
}
/*
* (non-Javadoc)
* @see org.springframework.core.type.ClassMetadata#getClassName()
*/
@Override
public String getClassName() {
return classMetadata.getClassName();
}
/*
* (non-Javadoc)
* @see org.springframework.core.type.ClassMetadata#isInterface()
*/
@Override
public boolean isInterface() {
return classMetadata.isInterface();
}
/*
* (non-Javadoc)
* @see org.springframework.core.type.ClassMetadata#isAnnotation()
*/
@Override
public boolean isAnnotation() {
return classMetadata.isAnnotation();
}
/*
* (non-Javadoc)
* @see org.springframework.core.type.ClassMetadata#isAbstract()
*/
@Override
public boolean isAbstract() {
return classMetadata.isAbstract();
}
/*
* (non-Javadoc)
* @see org.springframework.core.type.ClassMetadata#isConcrete()
*/
@Override
public boolean isConcrete() {
return classMetadata.isConcrete();
}
/*
* (non-Javadoc)
* @see org.springframework.core.type.ClassMetadata#isFinal()
*/
@Override
public boolean isFinal() {
return classMetadata.isFinal();
}
/*
* (non-Javadoc)
* @see org.springframework.core.type.ClassMetadata#isIndependent()
*/
@Override
public boolean isIndependent() {
return classMetadata.isIndependent();
}
/*
* (non-Javadoc)
* @see org.springframework.core.type.ClassMetadata#hasEnclosingClass()
*/
@Override
public boolean hasEnclosingClass() {
return classMetadata.hasEnclosingClass();
}
/*
* (non-Javadoc)
* @see org.springframework.core.type.ClassMetadata#getEnclosingClassName()
*/
@Override
@Nullable
public String getEnclosingClassName() {
return classMetadata.getEnclosingClassName();
}
/*
* (non-Javadoc)
* @see org.springframework.core.type.ClassMetadata#hasSuperClass()
*/
@Override
public boolean hasSuperClass() {
return classMetadata.hasSuperClass();
}
/*
* (non-Javadoc)
* @see org.springframework.core.type.ClassMetadata#getSuperClassName()
*/
@Override
@Nullable
public String getSuperClassName() {
return classMetadata.getSuperClassName();
}
/*
* (non-Javadoc)
* @see org.springframework.core.type.ClassMetadata#getInterfaceNames()
*/
@Override
public String[] getInterfaceNames() {
return classMetadata.getInterfaceNames();
}
/*
* (non-Javadoc)
* @see org.springframework.core.type.ClassMetadata#getMemberClassNames()
*/
@Override
public String[] getMemberClassNames() {
return classMetadata.getMemberClassNames();
}
}
}

View File

@@ -1,103 +0,0 @@
/*
* Copyright 2021 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.
* 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 org.springframework.data.type.classreading;
import java.lang.annotation.Annotation;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.List;
import org.springframework.asm.AnnotationVisitor;
import org.springframework.asm.Type;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.lang.Nullable;
import org.springframework.util.ObjectUtils;
/**
* {@link AnnotationVisitor} to recursively visit annotation arrays.
*
* @author Chris Beams
* @author Juergen Hoeller
* @since 3.1.1
* @deprecated As of Spring Framework 5.2, this class and related classes in this package have been replaced by
* {@link SimpleAnnotationMetadataReadingVisitor} and related classes for internal use within the framework.
*/
@Deprecated
class RecursiveAnnotationArrayVisitor extends AbstractRecursiveAnnotationVisitor {
private final String attributeName;
private final List<AnnotationAttributes> allNestedAttributes = new ArrayList<>();
public RecursiveAnnotationArrayVisitor(String attributeName, AnnotationAttributes attributes,
@Nullable ClassLoader classLoader) {
super(classLoader, attributes);
this.attributeName = attributeName;
}
@Override
public void visit(String attributeName, Object attributeValue) {
Object newValue = attributeValue;
Object existingValue = this.attributes.get(this.attributeName);
if (existingValue != null) {
newValue = ObjectUtils.addObjectToArray((Object[]) existingValue, newValue);
} else {
Class<?> arrayClass = newValue.getClass();
if (Enum.class.isAssignableFrom(arrayClass)) {
while (arrayClass.getSuperclass() != null && !arrayClass.isEnum()) {
arrayClass = arrayClass.getSuperclass();
}
}
Object[] newArray = (Object[]) Array.newInstance(arrayClass, 1);
newArray[0] = newValue;
newValue = newArray;
}
this.attributes.put(this.attributeName, newValue);
}
@Override
public AnnotationVisitor visitAnnotation(String attributeName, String asmTypeDescriptor) {
String annotationType = Type.getType(asmTypeDescriptor).getClassName();
AnnotationAttributes nestedAttributes = new AnnotationAttributes(annotationType, this.classLoader);
this.allNestedAttributes.add(nestedAttributes);
return new RecursiveAnnotationAttributesVisitor(annotationType, nestedAttributes, this.classLoader);
}
@Override
public void visitEnd() {
if (!this.allNestedAttributes.isEmpty()) {
this.attributes.put(this.attributeName, this.allNestedAttributes.toArray(new AnnotationAttributes[0]));
} else if (!this.attributes.containsKey(this.attributeName)) {
Class<? extends Annotation> annotationType = this.attributes.annotationType();
if (annotationType != null) {
try {
Class<?> attributeType = annotationType.getMethod(this.attributeName).getReturnType();
if (attributeType.isArray()) {
Class<?> elementType = attributeType.getComponentType();
if (elementType.isAnnotation()) {
elementType = AnnotationAttributes.class;
}
this.attributes.put(this.attributeName, Array.newInstance(elementType, 0));
}
} catch (NoSuchMethodException ex) {
// Corresponding attribute method not found: cannot expose empty array.
}
}
}
}
}

View File

@@ -1,49 +0,0 @@
/*
* Copyright 2021 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.
* 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 org.springframework.data.type.classreading;
import org.springframework.asm.AnnotationVisitor;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.lang.Nullable;
/**
* {@link AnnotationVisitor} to recursively visit annotation attributes.
*
* @author Chris Beams
* @author Juergen Hoeller
* @since 3.1.1
* @deprecated As of Spring Framework 5.2, this class and related classes in this package have been replaced by
* {@link SimpleAnnotationMetadataReadingVisitor} and related classes for internal use within the framework.
*/
@Deprecated
class RecursiveAnnotationAttributesVisitor extends AbstractRecursiveAnnotationVisitor {
protected final String annotationType;
public RecursiveAnnotationAttributesVisitor(String annotationType, AnnotationAttributes attributes,
@Nullable ClassLoader classLoader) {
super(classLoader, attributes);
this.annotationType = annotationType;
}
@Override
public void visitEnd() {
AnnotationUtils.registerDefaultValues(this.attributes);
}
}