Introduce getDeclaredMethods() for MethodMetadata of all user-declared methods

Closes gh-27701
This commit is contained in:
Juergen Hoeller
2021-12-15 13:32:42 +01:00
parent efaccd6356
commit 50c7c84886
7 changed files with 67 additions and 64 deletions

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -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

View File

@@ -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() {

View File

@@ -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() {

View File

@@ -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) {
}