Skip to content

Commit 50c7c84

Browse files
committed
Introduce getDeclaredMethods() for MethodMetadata of all user-declared methods
Closes gh-27701
1 parent efaccd6 commit 50c7c84

File tree

7 files changed

+67
-64
lines changed

7 files changed

+67
-64
lines changed

spring-core/src/main/java/org/springframework/core/type/AnnotationMetadata.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2019 the original author or authors.
2+
* Copyright 2002-2021 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -115,6 +115,14 @@ default boolean hasAnnotatedMethods(String annotationName) {
115115
*/
116116
Set<MethodMetadata> getAnnotatedMethods(String annotationName);
117117

118+
/**
119+
* Retrieve the method metadata for all user-declared methods on the
120+
* underlying class, preserving declaration order as far as possible.
121+
* @return a set of {@link MethodMetadata}
122+
* @since 6.0
123+
*/
124+
Set<MethodMetadata> getDeclaredMethods();
125+
118126

119127
/**
120128
* Factory method to create a new {@link AnnotationMetadata} instance

spring-core/src/main/java/org/springframework/core/type/StandardAnnotationMetadata.java

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -143,26 +143,24 @@ public boolean hasAnnotatedMethods(String annotationName) {
143143
}
144144

145145
@Override
146-
@SuppressWarnings("deprecation")
147146
public Set<MethodMetadata> getAnnotatedMethods(String annotationName) {
148-
Set<MethodMetadata> annotatedMethods = null;
147+
Set<MethodMetadata> result = new LinkedHashSet<>(4);
149148
if (AnnotationUtils.isCandidateClass(getIntrospectedClass(), annotationName)) {
150-
try {
151-
Method[] methods = ReflectionUtils.getDeclaredMethods(getIntrospectedClass());
152-
for (Method method : methods) {
153-
if (isAnnotatedMethod(method, annotationName)) {
154-
if (annotatedMethods == null) {
155-
annotatedMethods = new LinkedHashSet<>(4);
156-
}
157-
annotatedMethods.add(new StandardMethodMetadata(method, this.nestedAnnotationsAsMap));
158-
}
149+
ReflectionUtils.doWithLocalMethods(getIntrospectedClass(), method -> {
150+
if (isAnnotatedMethod(method, annotationName)) {
151+
result.add(new StandardMethodMetadata(method, this.nestedAnnotationsAsMap));
159152
}
160-
}
161-
catch (Throwable ex) {
162-
throw new IllegalStateException("Failed to introspect annotated methods on " + getIntrospectedClass(), ex);
163-
}
153+
});
164154
}
165-
return annotatedMethods != null ? annotatedMethods : Collections.emptySet();
155+
return result;
156+
}
157+
158+
@Override
159+
public Set<MethodMetadata> getDeclaredMethods() {
160+
Set<MethodMetadata> result = new LinkedHashSet<>(16);
161+
ReflectionUtils.doWithLocalMethods(getIntrospectedClass(), method ->
162+
result.add(new StandardMethodMetadata(method, this.nestedAnnotationsAsMap)));
163+
return result;
166164
}
167165

168166

spring-core/src/main/java/org/springframework/core/type/StandardMethodMetadata.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,10 +68,8 @@ public StandardMethodMetadata(Method introspectedMethod) {
6868
* {@link org.springframework.core.annotation.AnnotationAttributes} for compatibility
6969
* with ASM-based {@link AnnotationMetadata} implementations
7070
* @since 3.1.1
71-
* @deprecated since 5.2 in favor of obtaining instances via {@link AnnotationMetadata}
7271
*/
73-
@Deprecated
74-
public StandardMethodMetadata(Method introspectedMethod, boolean nestedAnnotationsAsMap) {
72+
StandardMethodMetadata(Method introspectedMethod, boolean nestedAnnotationsAsMap) {
7573
Assert.notNull(introspectedMethod, "Method must not be null");
7674
this.introspectedMethod = introspectedMethod;
7775
this.nestedAnnotationsAsMap = nestedAnnotationsAsMap;

spring-core/src/main/java/org/springframework/core/type/classreading/SimpleAnnotationMetadata.java

Lines changed: 21 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,15 @@
2525
import org.springframework.core.type.AnnotationMetadata;
2626
import org.springframework.core.type.MethodMetadata;
2727
import org.springframework.lang.Nullable;
28+
import org.springframework.util.StringUtils;
2829

2930
/**
3031
* {@link AnnotationMetadata} created from a
3132
* {@link SimpleAnnotationMetadataReadingVisitor}.
3233
*
3334
* @author Phillip Webb
3435
* @author Sam Brannen
36+
* @author Juergen Hoeller
3537
* @since 5.2
3638
*/
3739
final class SimpleAnnotationMetadata implements AnnotationMetadata {
@@ -48,11 +50,11 @@ final class SimpleAnnotationMetadata implements AnnotationMetadata {
4850

4951
private final boolean independentInnerClass;
5052

51-
private final String[] interfaceNames;
53+
private final Set<String> interfaceNames;
5254

53-
private final String[] memberClassNames;
55+
private final Set<String> memberClassNames;
5456

55-
private final MethodMetadata[] annotatedMethods;
57+
private final Set<MethodMetadata> declaredMethods;
5658

5759
private final MergedAnnotations annotations;
5860

@@ -61,8 +63,8 @@ final class SimpleAnnotationMetadata implements AnnotationMetadata {
6163

6264

6365
SimpleAnnotationMetadata(String className, int access, @Nullable String enclosingClassName,
64-
@Nullable String superClassName, boolean independentInnerClass, String[] interfaceNames,
65-
String[] memberClassNames, MethodMetadata[] annotatedMethods, MergedAnnotations annotations) {
66+
@Nullable String superClassName, boolean independentInnerClass, Set<String> interfaceNames,
67+
Set<String> memberClassNames, Set<MethodMetadata> declaredMethods, MergedAnnotations annotations) {
6668

6769
this.className = className;
6870
this.access = access;
@@ -71,7 +73,7 @@ final class SimpleAnnotationMetadata implements AnnotationMetadata {
7173
this.independentInnerClass = independentInnerClass;
7274
this.interfaceNames = interfaceNames;
7375
this.memberClassNames = memberClassNames;
74-
this.annotatedMethods = annotatedMethods;
76+
this.declaredMethods = declaredMethods;
7577
this.annotations = annotations;
7678
}
7779

@@ -119,12 +121,17 @@ public String getSuperClassName() {
119121

120122
@Override
121123
public String[] getInterfaceNames() {
122-
return this.interfaceNames.clone();
124+
return StringUtils.toStringArray(this.interfaceNames);
123125
}
124126

125127
@Override
126128
public String[] getMemberClassNames() {
127-
return this.memberClassNames.clone();
129+
return StringUtils.toStringArray(this.memberClassNames);
130+
}
131+
132+
@Override
133+
public MergedAnnotations getAnnotations() {
134+
return this.annotations;
128135
}
129136

130137
@Override
@@ -140,21 +147,18 @@ public Set<String> getAnnotationTypes() {
140147

141148
@Override
142149
public Set<MethodMetadata> getAnnotatedMethods(String annotationName) {
143-
Set<MethodMetadata> annotatedMethods = null;
144-
for (MethodMetadata annotatedMethod : this.annotatedMethods) {
150+
Set<MethodMetadata> result = new LinkedHashSet<>(4);
151+
for (MethodMetadata annotatedMethod : this.declaredMethods) {
145152
if (annotatedMethod.isAnnotated(annotationName)) {
146-
if (annotatedMethods == null) {
147-
annotatedMethods = new LinkedHashSet<>(4);
148-
}
149-
annotatedMethods.add(annotatedMethod);
153+
result.add(annotatedMethod);
150154
}
151155
}
152-
return annotatedMethods != null ? annotatedMethods : Collections.emptySet();
156+
return Collections.unmodifiableSet(result);
153157
}
154158

155159
@Override
156-
public MergedAnnotations getAnnotations() {
157-
return this.annotations;
160+
public Set<MethodMetadata> getDeclaredMethods() {
161+
return Collections.unmodifiableSet(this.declaredMethods);
158162
}
159163

160164
@Override

spring-core/src/main/java/org/springframework/core/type/classreading/SimpleAnnotationMetadataReadingVisitor.java

Lines changed: 11 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,7 @@
1616

1717
package org.springframework.core.type.classreading;
1818

19-
import java.util.ArrayList;
2019
import java.util.LinkedHashSet;
21-
import java.util.List;
2220
import java.util.Set;
2321

2422
import org.springframework.asm.AnnotationVisitor;
@@ -32,12 +30,12 @@
3230
import org.springframework.lang.Nullable;
3331
import org.springframework.util.Assert;
3432
import org.springframework.util.ClassUtils;
35-
import org.springframework.util.StringUtils;
3633

3734
/**
3835
* ASM class visitor that creates {@link SimpleAnnotationMetadata}.
3936
*
4037
* @author Phillip Webb
38+
* @author Juergen Hoeller
4139
* @since 5.2
4240
*/
4341
final class SimpleAnnotationMetadataReadingVisitor extends ClassVisitor {
@@ -52,18 +50,18 @@ final class SimpleAnnotationMetadataReadingVisitor extends ClassVisitor {
5250
@Nullable
5351
private String superClassName;
5452

55-
private String[] interfaceNames = new String[0];
56-
5753
@Nullable
5854
private String enclosingClassName;
5955

6056
private boolean independentInnerClass;
6157

58+
private final Set<String> interfaceNames = new LinkedHashSet<>(4);
59+
6260
private final Set<String> memberClassNames = new LinkedHashSet<>(4);
6361

64-
private final List<MergedAnnotation<?>> annotations = new ArrayList<>();
62+
private final Set<MergedAnnotation<?>> annotations = new LinkedHashSet<>(4);
6563

66-
private final List<SimpleMethodMetadata> annotatedMethods = new ArrayList<>();
64+
private final Set<MethodMetadata> declaredMethods = new LinkedHashSet<>(4);
6765

6866
@Nullable
6967
private SimpleAnnotationMetadata metadata;
@@ -87,9 +85,8 @@ public void visit(int version, int access, String name, String signature,
8785
if (supername != null && !isInterface(access)) {
8886
this.superClassName = toClassName(supername);
8987
}
90-
this.interfaceNames = new String[interfaces.length];
9188
for (int i = 0; i < interfaces.length; i++) {
92-
this.interfaceNames[i] = toClassName(interfaces[i]);
89+
this.interfaceNames.add(toClassName(interfaces[i]));
9390
}
9491
}
9592

@@ -99,8 +96,7 @@ public void visitOuterClass(String owner, String name, String desc) {
9996
}
10097

10198
@Override
102-
public void visitInnerClass(String name, @Nullable String outerName, String innerName,
103-
int access) {
99+
public void visitInnerClass(String name, @Nullable String outerName, String innerName, int access) {
104100
if (outerName != null) {
105101
String className = toClassName(name);
106102
String outerClassName = toClassName(outerName);
@@ -126,24 +122,20 @@ public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) {
126122
public MethodVisitor visitMethod(
127123
int access, String name, String descriptor, String signature, String[] exceptions) {
128124

129-
// Skip bridge methods - we're only interested in original
130-
// annotation-defining user methods. On JDK 8, we'd otherwise run into
131-
// double detection of the same annotated method...
132-
if (isBridge(access)) {
125+
// Skip bridge methods and constructors - we're only interested in original user methods.
126+
if (isBridge(access) || name.equals("<init>")) {
133127
return null;
134128
}
135129
return new SimpleMethodMetadataReadingVisitor(this.classLoader, this.className,
136-
access, name, descriptor, this.annotatedMethods::add);
130+
access, name, descriptor, this.declaredMethods::add);
137131
}
138132

139133
@Override
140134
public void visitEnd() {
141-
String[] memberClassNames = StringUtils.toStringArray(this.memberClassNames);
142-
MethodMetadata[] annotatedMethods = this.annotatedMethods.toArray(new MethodMetadata[0]);
143135
MergedAnnotations annotations = MergedAnnotations.of(this.annotations);
144136
this.metadata = new SimpleAnnotationMetadata(this.className, this.access,
145137
this.enclosingClassName, this.superClassName, this.independentInnerClass,
146-
this.interfaceNames, memberClassNames, annotatedMethods, annotations);
138+
this.interfaceNames, this.memberClassNames, this.declaredMethods, annotations);
147139
}
148140

149141
public SimpleAnnotationMetadata getMetadata() {

spring-core/src/main/java/org/springframework/core/type/classreading/SimpleMethodMetadataReadingVisitor.java

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
*
3434
* @author Phillip Webb
3535
* @author Sam Brannen
36+
* @author Juergen Hoeller
3637
* @since 5.2
3738
*/
3839
final class SimpleMethodMetadataReadingVisitor extends MethodVisitor {
@@ -78,13 +79,11 @@ public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) {
7879

7980
@Override
8081
public void visitEnd() {
81-
if (!this.annotations.isEmpty()) {
82-
String returnTypeName = Type.getReturnType(this.descriptor).getClassName();
83-
MergedAnnotations annotations = MergedAnnotations.of(this.annotations);
84-
SimpleMethodMetadata metadata = new SimpleMethodMetadata(this.methodName, this.access,
85-
this.declaringClassName, returnTypeName, getSource(), annotations);
86-
this.consumer.accept(metadata);
87-
}
82+
String returnTypeName = Type.getReturnType(this.descriptor).getClassName();
83+
MergedAnnotations annotations = MergedAnnotations.of(this.annotations);
84+
SimpleMethodMetadata metadata = new SimpleMethodMetadata(this.methodName, this.access,
85+
this.declaringClassName, returnTypeName, getSource(), annotations);
86+
this.consumer.accept(metadata);
8887
}
8988

9089
private Object getSource() {

spring-core/src/test/java/org/springframework/core/type/AnnotationMetadataTests.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2019 the original author or authors.
2+
* Copyright 2002-2021 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -397,6 +397,7 @@ private void doTestAnnotationInfo(AnnotationMetadata metadata) {
397397
}
398398

399399
private void doTestMethodAnnotationInfo(AnnotationMetadata classMetadata) {
400+
assertThat(classMetadata.getDeclaredMethods()).hasSize(3);
400401
Set<MethodMetadata> methods = classMetadata.getAnnotatedMethods(TestAutowired.class.getName());
401402
assertThat(methods).hasSize(1);
402403
for (MethodMetadata methodMetadata : methods) {
@@ -503,6 +504,9 @@ public enum SubclassEnum {
503504
@NamedComposedAnnotation
504505
private static class AnnotatedComponent implements Serializable {
505506

507+
public AnnotatedComponent() {
508+
}
509+
506510
@TestAutowired
507511
public void doWork(@TestQualifier("myColor") java.awt.Color color) {
508512
}

0 commit comments

Comments
 (0)