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"); * 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

View File

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

View File

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

View File

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

View File

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

View File

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

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"); * 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) {
} }