Introduce getDeclaredMethods() for MethodMetadata of all user-declared methods
Closes gh-27701
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-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.
|
||||
@@ -115,6 +115,14 @@ public interface AnnotationMetadata extends ClassMetadata, AnnotatedTypeMetadata
|
||||
*/
|
||||
Set<MethodMetadata> getAnnotatedMethods(String annotationName);
|
||||
|
||||
/**
|
||||
* Retrieve the method metadata for all user-declared methods on the
|
||||
* underlying class, preserving declaration order as far as possible.
|
||||
* @return a set of {@link MethodMetadata}
|
||||
* @since 6.0
|
||||
*/
|
||||
Set<MethodMetadata> getDeclaredMethods();
|
||||
|
||||
|
||||
/**
|
||||
* Factory method to create a new {@link AnnotationMetadata} instance
|
||||
|
||||
@@ -143,26 +143,24 @@ public class StandardAnnotationMetadata extends StandardClassMetadata implements
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("deprecation")
|
||||
public Set<MethodMetadata> getAnnotatedMethods(String annotationName) {
|
||||
Set<MethodMetadata> annotatedMethods = null;
|
||||
Set<MethodMetadata> result = new LinkedHashSet<>(4);
|
||||
if (AnnotationUtils.isCandidateClass(getIntrospectedClass(), annotationName)) {
|
||||
try {
|
||||
Method[] methods = ReflectionUtils.getDeclaredMethods(getIntrospectedClass());
|
||||
for (Method method : methods) {
|
||||
if (isAnnotatedMethod(method, annotationName)) {
|
||||
if (annotatedMethods == null) {
|
||||
annotatedMethods = new LinkedHashSet<>(4);
|
||||
}
|
||||
annotatedMethods.add(new StandardMethodMetadata(method, this.nestedAnnotationsAsMap));
|
||||
}
|
||||
ReflectionUtils.doWithLocalMethods(getIntrospectedClass(), method -> {
|
||||
if (isAnnotatedMethod(method, annotationName)) {
|
||||
result.add(new StandardMethodMetadata(method, this.nestedAnnotationsAsMap));
|
||||
}
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
throw new IllegalStateException("Failed to introspect annotated methods on " + getIntrospectedClass(), ex);
|
||||
}
|
||||
});
|
||||
}
|
||||
return annotatedMethods != null ? annotatedMethods : Collections.emptySet();
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<MethodMetadata> getDeclaredMethods() {
|
||||
Set<MethodMetadata> result = new LinkedHashSet<>(16);
|
||||
ReflectionUtils.doWithLocalMethods(getIntrospectedClass(), method ->
|
||||
result.add(new StandardMethodMetadata(method, this.nestedAnnotationsAsMap)));
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -68,10 +68,8 @@ public class StandardMethodMetadata implements MethodMetadata {
|
||||
* {@link org.springframework.core.annotation.AnnotationAttributes} for compatibility
|
||||
* with ASM-based {@link AnnotationMetadata} implementations
|
||||
* @since 3.1.1
|
||||
* @deprecated since 5.2 in favor of obtaining instances via {@link AnnotationMetadata}
|
||||
*/
|
||||
@Deprecated
|
||||
public StandardMethodMetadata(Method introspectedMethod, boolean nestedAnnotationsAsMap) {
|
||||
StandardMethodMetadata(Method introspectedMethod, boolean nestedAnnotationsAsMap) {
|
||||
Assert.notNull(introspectedMethod, "Method must not be null");
|
||||
this.introspectedMethod = introspectedMethod;
|
||||
this.nestedAnnotationsAsMap = nestedAnnotationsAsMap;
|
||||
|
||||
@@ -25,6 +25,7 @@ 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.StringUtils;
|
||||
|
||||
/**
|
||||
* {@link AnnotationMetadata} created from a
|
||||
@@ -32,6 +33,7 @@ import org.springframework.lang.Nullable;
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Sam Brannen
|
||||
* @author Juergen Hoeller
|
||||
* @since 5.2
|
||||
*/
|
||||
final class SimpleAnnotationMetadata implements AnnotationMetadata {
|
||||
@@ -48,11 +50,11 @@ final class SimpleAnnotationMetadata implements AnnotationMetadata {
|
||||
|
||||
private final boolean independentInnerClass;
|
||||
|
||||
private final String[] interfaceNames;
|
||||
private final Set<String> interfaceNames;
|
||||
|
||||
private final String[] memberClassNames;
|
||||
private final Set<String> memberClassNames;
|
||||
|
||||
private final MethodMetadata[] annotatedMethods;
|
||||
private final Set<MethodMetadata> declaredMethods;
|
||||
|
||||
private final MergedAnnotations annotations;
|
||||
|
||||
@@ -61,8 +63,8 @@ final class SimpleAnnotationMetadata implements AnnotationMetadata {
|
||||
|
||||
|
||||
SimpleAnnotationMetadata(String className, int access, @Nullable String enclosingClassName,
|
||||
@Nullable String superClassName, boolean independentInnerClass, String[] interfaceNames,
|
||||
String[] memberClassNames, MethodMetadata[] annotatedMethods, MergedAnnotations annotations) {
|
||||
@Nullable String superClassName, boolean independentInnerClass, Set<String> interfaceNames,
|
||||
Set<String> memberClassNames, Set<MethodMetadata> declaredMethods, MergedAnnotations annotations) {
|
||||
|
||||
this.className = className;
|
||||
this.access = access;
|
||||
@@ -71,7 +73,7 @@ final class SimpleAnnotationMetadata implements AnnotationMetadata {
|
||||
this.independentInnerClass = independentInnerClass;
|
||||
this.interfaceNames = interfaceNames;
|
||||
this.memberClassNames = memberClassNames;
|
||||
this.annotatedMethods = annotatedMethods;
|
||||
this.declaredMethods = declaredMethods;
|
||||
this.annotations = annotations;
|
||||
}
|
||||
|
||||
@@ -119,12 +121,17 @@ final class SimpleAnnotationMetadata implements AnnotationMetadata {
|
||||
|
||||
@Override
|
||||
public String[] getInterfaceNames() {
|
||||
return this.interfaceNames.clone();
|
||||
return StringUtils.toStringArray(this.interfaceNames);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getMemberClassNames() {
|
||||
return this.memberClassNames.clone();
|
||||
return StringUtils.toStringArray(this.memberClassNames);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MergedAnnotations getAnnotations() {
|
||||
return this.annotations;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -140,21 +147,18 @@ final class SimpleAnnotationMetadata implements AnnotationMetadata {
|
||||
|
||||
@Override
|
||||
public Set<MethodMetadata> getAnnotatedMethods(String annotationName) {
|
||||
Set<MethodMetadata> annotatedMethods = null;
|
||||
for (MethodMetadata annotatedMethod : this.annotatedMethods) {
|
||||
Set<MethodMetadata> result = new LinkedHashSet<>(4);
|
||||
for (MethodMetadata annotatedMethod : this.declaredMethods) {
|
||||
if (annotatedMethod.isAnnotated(annotationName)) {
|
||||
if (annotatedMethods == null) {
|
||||
annotatedMethods = new LinkedHashSet<>(4);
|
||||
}
|
||||
annotatedMethods.add(annotatedMethod);
|
||||
result.add(annotatedMethod);
|
||||
}
|
||||
}
|
||||
return annotatedMethods != null ? annotatedMethods : Collections.emptySet();
|
||||
return Collections.unmodifiableSet(result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MergedAnnotations getAnnotations() {
|
||||
return this.annotations;
|
||||
public Set<MethodMetadata> getDeclaredMethods() {
|
||||
return Collections.unmodifiableSet(this.declaredMethods);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -16,9 +16,7 @@
|
||||
|
||||
package org.springframework.core.type.classreading;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.asm.AnnotationVisitor;
|
||||
@@ -32,12 +30,12 @@ import org.springframework.core.type.MethodMetadata;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* ASM class visitor that creates {@link SimpleAnnotationMetadata}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Juergen Hoeller
|
||||
* @since 5.2
|
||||
*/
|
||||
final class SimpleAnnotationMetadataReadingVisitor extends ClassVisitor {
|
||||
@@ -52,18 +50,18 @@ final class SimpleAnnotationMetadataReadingVisitor extends ClassVisitor {
|
||||
@Nullable
|
||||
private String superClassName;
|
||||
|
||||
private String[] interfaceNames = new String[0];
|
||||
|
||||
@Nullable
|
||||
private String enclosingClassName;
|
||||
|
||||
private boolean independentInnerClass;
|
||||
|
||||
private final Set<String> interfaceNames = new LinkedHashSet<>(4);
|
||||
|
||||
private final Set<String> memberClassNames = new LinkedHashSet<>(4);
|
||||
|
||||
private final List<MergedAnnotation<?>> annotations = new ArrayList<>();
|
||||
private final Set<MergedAnnotation<?>> annotations = new LinkedHashSet<>(4);
|
||||
|
||||
private final List<SimpleMethodMetadata> annotatedMethods = new ArrayList<>();
|
||||
private final Set<MethodMetadata> declaredMethods = new LinkedHashSet<>(4);
|
||||
|
||||
@Nullable
|
||||
private SimpleAnnotationMetadata metadata;
|
||||
@@ -87,9 +85,8 @@ final class SimpleAnnotationMetadataReadingVisitor extends ClassVisitor {
|
||||
if (supername != null && !isInterface(access)) {
|
||||
this.superClassName = toClassName(supername);
|
||||
}
|
||||
this.interfaceNames = new String[interfaces.length];
|
||||
for (int i = 0; i < interfaces.length; i++) {
|
||||
this.interfaceNames[i] = toClassName(interfaces[i]);
|
||||
this.interfaceNames.add(toClassName(interfaces[i]));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,8 +96,7 @@ final class SimpleAnnotationMetadataReadingVisitor extends ClassVisitor {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitInnerClass(String name, @Nullable String outerName, String innerName,
|
||||
int access) {
|
||||
public void visitInnerClass(String name, @Nullable String outerName, String innerName, int access) {
|
||||
if (outerName != null) {
|
||||
String className = toClassName(name);
|
||||
String outerClassName = toClassName(outerName);
|
||||
@@ -126,24 +122,20 @@ final class SimpleAnnotationMetadataReadingVisitor extends ClassVisitor {
|
||||
public MethodVisitor visitMethod(
|
||||
int access, String name, String descriptor, 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 (isBridge(access)) {
|
||||
// Skip bridge methods and constructors - we're only interested in original user methods.
|
||||
if (isBridge(access) || name.equals("<init>")) {
|
||||
return null;
|
||||
}
|
||||
return new SimpleMethodMetadataReadingVisitor(this.classLoader, this.className,
|
||||
access, name, descriptor, this.annotatedMethods::add);
|
||||
access, name, descriptor, this.declaredMethods::add);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitEnd() {
|
||||
String[] memberClassNames = StringUtils.toStringArray(this.memberClassNames);
|
||||
MethodMetadata[] annotatedMethods = this.annotatedMethods.toArray(new MethodMetadata[0]);
|
||||
MergedAnnotations annotations = MergedAnnotations.of(this.annotations);
|
||||
this.metadata = new SimpleAnnotationMetadata(this.className, this.access,
|
||||
this.enclosingClassName, this.superClassName, this.independentInnerClass,
|
||||
this.interfaceNames, memberClassNames, annotatedMethods, annotations);
|
||||
this.interfaceNames, this.memberClassNames, this.declaredMethods, annotations);
|
||||
}
|
||||
|
||||
public SimpleAnnotationMetadata getMetadata() {
|
||||
|
||||
@@ -33,6 +33,7 @@ import org.springframework.lang.Nullable;
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Sam Brannen
|
||||
* @author Juergen Hoeller
|
||||
* @since 5.2
|
||||
*/
|
||||
final class SimpleMethodMetadataReadingVisitor extends MethodVisitor {
|
||||
@@ -78,13 +79,11 @@ final class SimpleMethodMetadataReadingVisitor extends MethodVisitor {
|
||||
|
||||
@Override
|
||||
public void visitEnd() {
|
||||
if (!this.annotations.isEmpty()) {
|
||||
String returnTypeName = Type.getReturnType(this.descriptor).getClassName();
|
||||
MergedAnnotations annotations = MergedAnnotations.of(this.annotations);
|
||||
SimpleMethodMetadata metadata = new SimpleMethodMetadata(this.methodName, this.access,
|
||||
this.declaringClassName, returnTypeName, getSource(), annotations);
|
||||
this.consumer.accept(metadata);
|
||||
}
|
||||
String returnTypeName = Type.getReturnType(this.descriptor).getClassName();
|
||||
MergedAnnotations annotations = MergedAnnotations.of(this.annotations);
|
||||
SimpleMethodMetadata metadata = new SimpleMethodMetadata(this.methodName, this.access,
|
||||
this.declaringClassName, returnTypeName, getSource(), annotations);
|
||||
this.consumer.accept(metadata);
|
||||
}
|
||||
|
||||
private Object getSource() {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-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.
|
||||
@@ -397,6 +397,7 @@ class AnnotationMetadataTests {
|
||||
}
|
||||
|
||||
private void doTestMethodAnnotationInfo(AnnotationMetadata classMetadata) {
|
||||
assertThat(classMetadata.getDeclaredMethods()).hasSize(3);
|
||||
Set<MethodMetadata> methods = classMetadata.getAnnotatedMethods(TestAutowired.class.getName());
|
||||
assertThat(methods).hasSize(1);
|
||||
for (MethodMetadata methodMetadata : methods) {
|
||||
@@ -503,6 +504,9 @@ class AnnotationMetadataTests {
|
||||
@NamedComposedAnnotation
|
||||
private static class AnnotatedComponent implements Serializable {
|
||||
|
||||
public AnnotatedComponent() {
|
||||
}
|
||||
|
||||
@TestAutowired
|
||||
public void doWork(@TestQualifier("myColor") java.awt.Color color) {
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user