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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with 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);
|
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
|
* Factory method to create a new {@link AnnotationMetadata} instance
|
||||||
|
|||||||
@@ -143,26 +143,24 @@ public class StandardAnnotationMetadata extends StandardClassMetadata implements
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@SuppressWarnings("deprecation")
|
|
||||||
public Set<MethodMetadata> getAnnotatedMethods(String annotationName) {
|
public Set<MethodMetadata> getAnnotatedMethods(String annotationName) {
|
||||||
Set<MethodMetadata> annotatedMethods = null;
|
Set<MethodMetadata> result = new LinkedHashSet<>(4);
|
||||||
if (AnnotationUtils.isCandidateClass(getIntrospectedClass(), annotationName)) {
|
if (AnnotationUtils.isCandidateClass(getIntrospectedClass(), annotationName)) {
|
||||||
try {
|
ReflectionUtils.doWithLocalMethods(getIntrospectedClass(), method -> {
|
||||||
Method[] methods = ReflectionUtils.getDeclaredMethods(getIntrospectedClass());
|
if (isAnnotatedMethod(method, annotationName)) {
|
||||||
for (Method method : methods) {
|
result.add(new StandardMethodMetadata(method, this.nestedAnnotationsAsMap));
|
||||||
if (isAnnotatedMethod(method, annotationName)) {
|
|
||||||
if (annotatedMethods == null) {
|
|
||||||
annotatedMethods = new LinkedHashSet<>(4);
|
|
||||||
}
|
|
||||||
annotatedMethods.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
|
* {@link org.springframework.core.annotation.AnnotationAttributes} for compatibility
|
||||||
* with ASM-based {@link AnnotationMetadata} implementations
|
* with ASM-based {@link AnnotationMetadata} implementations
|
||||||
* @since 3.1.1
|
* @since 3.1.1
|
||||||
* @deprecated since 5.2 in favor of obtaining instances via {@link AnnotationMetadata}
|
|
||||||
*/
|
*/
|
||||||
@Deprecated
|
StandardMethodMetadata(Method introspectedMethod, boolean nestedAnnotationsAsMap) {
|
||||||
public StandardMethodMetadata(Method introspectedMethod, boolean nestedAnnotationsAsMap) {
|
|
||||||
Assert.notNull(introspectedMethod, "Method must not be null");
|
Assert.notNull(introspectedMethod, "Method must not be null");
|
||||||
this.introspectedMethod = introspectedMethod;
|
this.introspectedMethod = introspectedMethod;
|
||||||
this.nestedAnnotationsAsMap = nestedAnnotationsAsMap;
|
this.nestedAnnotationsAsMap = nestedAnnotationsAsMap;
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import org.springframework.core.annotation.MergedAnnotations;
|
|||||||
import org.springframework.core.type.AnnotationMetadata;
|
import org.springframework.core.type.AnnotationMetadata;
|
||||||
import org.springframework.core.type.MethodMetadata;
|
import org.springframework.core.type.MethodMetadata;
|
||||||
import org.springframework.lang.Nullable;
|
import org.springframework.lang.Nullable;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@link AnnotationMetadata} created from a
|
* {@link AnnotationMetadata} created from a
|
||||||
@@ -32,6 +33,7 @@ import org.springframework.lang.Nullable;
|
|||||||
*
|
*
|
||||||
* @author Phillip Webb
|
* @author Phillip Webb
|
||||||
* @author Sam Brannen
|
* @author Sam Brannen
|
||||||
|
* @author Juergen Hoeller
|
||||||
* @since 5.2
|
* @since 5.2
|
||||||
*/
|
*/
|
||||||
final class SimpleAnnotationMetadata implements AnnotationMetadata {
|
final class SimpleAnnotationMetadata implements AnnotationMetadata {
|
||||||
@@ -48,11 +50,11 @@ final class SimpleAnnotationMetadata implements AnnotationMetadata {
|
|||||||
|
|
||||||
private final boolean independentInnerClass;
|
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;
|
private final MergedAnnotations annotations;
|
||||||
|
|
||||||
@@ -61,8 +63,8 @@ final class SimpleAnnotationMetadata implements AnnotationMetadata {
|
|||||||
|
|
||||||
|
|
||||||
SimpleAnnotationMetadata(String className, int access, @Nullable String enclosingClassName,
|
SimpleAnnotationMetadata(String className, int access, @Nullable String enclosingClassName,
|
||||||
@Nullable String superClassName, boolean independentInnerClass, String[] interfaceNames,
|
@Nullable String superClassName, boolean independentInnerClass, Set<String> interfaceNames,
|
||||||
String[] memberClassNames, MethodMetadata[] annotatedMethods, MergedAnnotations annotations) {
|
Set<String> memberClassNames, Set<MethodMetadata> declaredMethods, MergedAnnotations annotations) {
|
||||||
|
|
||||||
this.className = className;
|
this.className = className;
|
||||||
this.access = access;
|
this.access = access;
|
||||||
@@ -71,7 +73,7 @@ final class SimpleAnnotationMetadata implements AnnotationMetadata {
|
|||||||
this.independentInnerClass = independentInnerClass;
|
this.independentInnerClass = independentInnerClass;
|
||||||
this.interfaceNames = interfaceNames;
|
this.interfaceNames = interfaceNames;
|
||||||
this.memberClassNames = memberClassNames;
|
this.memberClassNames = memberClassNames;
|
||||||
this.annotatedMethods = annotatedMethods;
|
this.declaredMethods = declaredMethods;
|
||||||
this.annotations = annotations;
|
this.annotations = annotations;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -119,12 +121,17 @@ final class SimpleAnnotationMetadata implements AnnotationMetadata {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String[] getInterfaceNames() {
|
public String[] getInterfaceNames() {
|
||||||
return this.interfaceNames.clone();
|
return StringUtils.toStringArray(this.interfaceNames);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String[] getMemberClassNames() {
|
public String[] getMemberClassNames() {
|
||||||
return this.memberClassNames.clone();
|
return StringUtils.toStringArray(this.memberClassNames);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MergedAnnotations getAnnotations() {
|
||||||
|
return this.annotations;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -140,21 +147,18 @@ final class SimpleAnnotationMetadata implements AnnotationMetadata {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Set<MethodMetadata> getAnnotatedMethods(String annotationName) {
|
public Set<MethodMetadata> getAnnotatedMethods(String annotationName) {
|
||||||
Set<MethodMetadata> annotatedMethods = null;
|
Set<MethodMetadata> result = new LinkedHashSet<>(4);
|
||||||
for (MethodMetadata annotatedMethod : this.annotatedMethods) {
|
for (MethodMetadata annotatedMethod : this.declaredMethods) {
|
||||||
if (annotatedMethod.isAnnotated(annotationName)) {
|
if (annotatedMethod.isAnnotated(annotationName)) {
|
||||||
if (annotatedMethods == null) {
|
result.add(annotatedMethod);
|
||||||
annotatedMethods = new LinkedHashSet<>(4);
|
|
||||||
}
|
|
||||||
annotatedMethods.add(annotatedMethod);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return annotatedMethods != null ? annotatedMethods : Collections.emptySet();
|
return Collections.unmodifiableSet(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public MergedAnnotations getAnnotations() {
|
public Set<MethodMetadata> getDeclaredMethods() {
|
||||||
return this.annotations;
|
return Collections.unmodifiableSet(this.declaredMethods);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -16,9 +16,7 @@
|
|||||||
|
|
||||||
package org.springframework.core.type.classreading;
|
package org.springframework.core.type.classreading;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.LinkedHashSet;
|
import java.util.LinkedHashSet;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import org.springframework.asm.AnnotationVisitor;
|
import org.springframework.asm.AnnotationVisitor;
|
||||||
@@ -32,12 +30,12 @@ import org.springframework.core.type.MethodMetadata;
|
|||||||
import org.springframework.lang.Nullable;
|
import org.springframework.lang.Nullable;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
import org.springframework.util.ClassUtils;
|
import org.springframework.util.ClassUtils;
|
||||||
import org.springframework.util.StringUtils;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ASM class visitor that creates {@link SimpleAnnotationMetadata}.
|
* ASM class visitor that creates {@link SimpleAnnotationMetadata}.
|
||||||
*
|
*
|
||||||
* @author Phillip Webb
|
* @author Phillip Webb
|
||||||
|
* @author Juergen Hoeller
|
||||||
* @since 5.2
|
* @since 5.2
|
||||||
*/
|
*/
|
||||||
final class SimpleAnnotationMetadataReadingVisitor extends ClassVisitor {
|
final class SimpleAnnotationMetadataReadingVisitor extends ClassVisitor {
|
||||||
@@ -52,18 +50,18 @@ final class SimpleAnnotationMetadataReadingVisitor extends ClassVisitor {
|
|||||||
@Nullable
|
@Nullable
|
||||||
private String superClassName;
|
private String superClassName;
|
||||||
|
|
||||||
private String[] interfaceNames = new String[0];
|
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private String enclosingClassName;
|
private String enclosingClassName;
|
||||||
|
|
||||||
private boolean independentInnerClass;
|
private boolean independentInnerClass;
|
||||||
|
|
||||||
|
private final Set<String> interfaceNames = new LinkedHashSet<>(4);
|
||||||
|
|
||||||
private final Set<String> memberClassNames = 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
|
@Nullable
|
||||||
private SimpleAnnotationMetadata metadata;
|
private SimpleAnnotationMetadata metadata;
|
||||||
@@ -87,9 +85,8 @@ final class SimpleAnnotationMetadataReadingVisitor extends ClassVisitor {
|
|||||||
if (supername != null && !isInterface(access)) {
|
if (supername != null && !isInterface(access)) {
|
||||||
this.superClassName = toClassName(supername);
|
this.superClassName = toClassName(supername);
|
||||||
}
|
}
|
||||||
this.interfaceNames = new String[interfaces.length];
|
|
||||||
for (int i = 0; i < interfaces.length; i++) {
|
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
|
@Override
|
||||||
public void visitInnerClass(String name, @Nullable String outerName, String innerName,
|
public void visitInnerClass(String name, @Nullable String outerName, String innerName, int access) {
|
||||||
int access) {
|
|
||||||
if (outerName != null) {
|
if (outerName != null) {
|
||||||
String className = toClassName(name);
|
String className = toClassName(name);
|
||||||
String outerClassName = toClassName(outerName);
|
String outerClassName = toClassName(outerName);
|
||||||
@@ -126,24 +122,20 @@ final class SimpleAnnotationMetadataReadingVisitor extends ClassVisitor {
|
|||||||
public MethodVisitor visitMethod(
|
public MethodVisitor visitMethod(
|
||||||
int access, String name, String descriptor, String signature, String[] exceptions) {
|
int access, String name, String descriptor, String signature, String[] exceptions) {
|
||||||
|
|
||||||
// Skip bridge methods - we're only interested in original
|
// Skip bridge methods and constructors - we're only interested in original user methods.
|
||||||
// annotation-defining user methods. On JDK 8, we'd otherwise run into
|
if (isBridge(access) || name.equals("<init>")) {
|
||||||
// double detection of the same annotated method...
|
|
||||||
if (isBridge(access)) {
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return new SimpleMethodMetadataReadingVisitor(this.classLoader, this.className,
|
return new SimpleMethodMetadataReadingVisitor(this.classLoader, this.className,
|
||||||
access, name, descriptor, this.annotatedMethods::add);
|
access, name, descriptor, this.declaredMethods::add);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visitEnd() {
|
public void visitEnd() {
|
||||||
String[] memberClassNames = StringUtils.toStringArray(this.memberClassNames);
|
|
||||||
MethodMetadata[] annotatedMethods = this.annotatedMethods.toArray(new MethodMetadata[0]);
|
|
||||||
MergedAnnotations annotations = MergedAnnotations.of(this.annotations);
|
MergedAnnotations annotations = MergedAnnotations.of(this.annotations);
|
||||||
this.metadata = new SimpleAnnotationMetadata(this.className, this.access,
|
this.metadata = new SimpleAnnotationMetadata(this.className, this.access,
|
||||||
this.enclosingClassName, this.superClassName, this.independentInnerClass,
|
this.enclosingClassName, this.superClassName, this.independentInnerClass,
|
||||||
this.interfaceNames, memberClassNames, annotatedMethods, annotations);
|
this.interfaceNames, this.memberClassNames, this.declaredMethods, annotations);
|
||||||
}
|
}
|
||||||
|
|
||||||
public SimpleAnnotationMetadata getMetadata() {
|
public SimpleAnnotationMetadata getMetadata() {
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ import org.springframework.lang.Nullable;
|
|||||||
*
|
*
|
||||||
* @author Phillip Webb
|
* @author Phillip Webb
|
||||||
* @author Sam Brannen
|
* @author Sam Brannen
|
||||||
|
* @author Juergen Hoeller
|
||||||
* @since 5.2
|
* @since 5.2
|
||||||
*/
|
*/
|
||||||
final class SimpleMethodMetadataReadingVisitor extends MethodVisitor {
|
final class SimpleMethodMetadataReadingVisitor extends MethodVisitor {
|
||||||
@@ -78,13 +79,11 @@ final class SimpleMethodMetadataReadingVisitor extends MethodVisitor {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visitEnd() {
|
public void visitEnd() {
|
||||||
if (!this.annotations.isEmpty()) {
|
String returnTypeName = Type.getReturnType(this.descriptor).getClassName();
|
||||||
String returnTypeName = Type.getReturnType(this.descriptor).getClassName();
|
MergedAnnotations annotations = MergedAnnotations.of(this.annotations);
|
||||||
MergedAnnotations annotations = MergedAnnotations.of(this.annotations);
|
SimpleMethodMetadata metadata = new SimpleMethodMetadata(this.methodName, this.access,
|
||||||
SimpleMethodMetadata metadata = new SimpleMethodMetadata(this.methodName, this.access,
|
this.declaringClassName, returnTypeName, getSource(), annotations);
|
||||||
this.declaringClassName, returnTypeName, getSource(), annotations);
|
this.consumer.accept(metadata);
|
||||||
this.consumer.accept(metadata);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Object getSource() {
|
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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with 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) {
|
private void doTestMethodAnnotationInfo(AnnotationMetadata classMetadata) {
|
||||||
|
assertThat(classMetadata.getDeclaredMethods()).hasSize(3);
|
||||||
Set<MethodMetadata> methods = classMetadata.getAnnotatedMethods(TestAutowired.class.getName());
|
Set<MethodMetadata> methods = classMetadata.getAnnotatedMethods(TestAutowired.class.getName());
|
||||||
assertThat(methods).hasSize(1);
|
assertThat(methods).hasSize(1);
|
||||||
for (MethodMetadata methodMetadata : methods) {
|
for (MethodMetadata methodMetadata : methods) {
|
||||||
@@ -503,6 +504,9 @@ class AnnotationMetadataTests {
|
|||||||
@NamedComposedAnnotation
|
@NamedComposedAnnotation
|
||||||
private static class AnnotatedComponent implements Serializable {
|
private static class AnnotatedComponent implements Serializable {
|
||||||
|
|
||||||
|
public AnnotatedComponent() {
|
||||||
|
}
|
||||||
|
|
||||||
@TestAutowired
|
@TestAutowired
|
||||||
public void doWork(@TestQualifier("myColor") java.awt.Color color) {
|
public void doWork(@TestQualifier("myColor") java.awt.Color color) {
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user