diff --git a/spring-core/src/main/java/org/springframework/core/type/AnnotationMetadata.java b/spring-core/src/main/java/org/springframework/core/type/AnnotationMetadata.java index 66592ba7ea..944e232af9 100644 --- a/spring-core/src/main/java/org/springframework/core/type/AnnotationMetadata.java +++ b/spring-core/src/main/java/org/springframework/core/type/AnnotationMetadata.java @@ -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 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 getDeclaredMethods(); + /** * Factory method to create a new {@link AnnotationMetadata} instance diff --git a/spring-core/src/main/java/org/springframework/core/type/StandardAnnotationMetadata.java b/spring-core/src/main/java/org/springframework/core/type/StandardAnnotationMetadata.java index 60f0a26ff4..bc34ed937a 100644 --- a/spring-core/src/main/java/org/springframework/core/type/StandardAnnotationMetadata.java +++ b/spring-core/src/main/java/org/springframework/core/type/StandardAnnotationMetadata.java @@ -143,26 +143,24 @@ public class StandardAnnotationMetadata extends StandardClassMetadata implements } @Override - @SuppressWarnings("deprecation") public Set getAnnotatedMethods(String annotationName) { - Set annotatedMethods = null; + Set 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 getDeclaredMethods() { + Set result = new LinkedHashSet<>(16); + ReflectionUtils.doWithLocalMethods(getIntrospectedClass(), method -> + result.add(new StandardMethodMetadata(method, this.nestedAnnotationsAsMap))); + return result; } diff --git a/spring-core/src/main/java/org/springframework/core/type/StandardMethodMetadata.java b/spring-core/src/main/java/org/springframework/core/type/StandardMethodMetadata.java index 53c69376d8..b2eb1b84fd 100644 --- a/spring-core/src/main/java/org/springframework/core/type/StandardMethodMetadata.java +++ b/spring-core/src/main/java/org/springframework/core/type/StandardMethodMetadata.java @@ -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; diff --git a/spring-core/src/main/java/org/springframework/core/type/classreading/SimpleAnnotationMetadata.java b/spring-core/src/main/java/org/springframework/core/type/classreading/SimpleAnnotationMetadata.java index 5f1ebf4bb5..21fa6fac27 100644 --- a/spring-core/src/main/java/org/springframework/core/type/classreading/SimpleAnnotationMetadata.java +++ b/spring-core/src/main/java/org/springframework/core/type/classreading/SimpleAnnotationMetadata.java @@ -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 interfaceNames; - private final String[] memberClassNames; + private final Set memberClassNames; - private final MethodMetadata[] annotatedMethods; + private final Set 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 interfaceNames, + Set memberClassNames, Set 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 getAnnotatedMethods(String annotationName) { - Set annotatedMethods = null; - for (MethodMetadata annotatedMethod : this.annotatedMethods) { + Set 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 getDeclaredMethods() { + return Collections.unmodifiableSet(this.declaredMethods); } @Override diff --git a/spring-core/src/main/java/org/springframework/core/type/classreading/SimpleAnnotationMetadataReadingVisitor.java b/spring-core/src/main/java/org/springframework/core/type/classreading/SimpleAnnotationMetadataReadingVisitor.java index 2875b1e263..b156330eb7 100644 --- a/spring-core/src/main/java/org/springframework/core/type/classreading/SimpleAnnotationMetadataReadingVisitor.java +++ b/spring-core/src/main/java/org/springframework/core/type/classreading/SimpleAnnotationMetadataReadingVisitor.java @@ -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 interfaceNames = new LinkedHashSet<>(4); + private final Set memberClassNames = new LinkedHashSet<>(4); - private final List> annotations = new ArrayList<>(); + private final Set> annotations = new LinkedHashSet<>(4); - private final List annotatedMethods = new ArrayList<>(); + private final Set 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("")) { 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() { diff --git a/spring-core/src/main/java/org/springframework/core/type/classreading/SimpleMethodMetadataReadingVisitor.java b/spring-core/src/main/java/org/springframework/core/type/classreading/SimpleMethodMetadataReadingVisitor.java index df18e489b8..895887263e 100644 --- a/spring-core/src/main/java/org/springframework/core/type/classreading/SimpleMethodMetadataReadingVisitor.java +++ b/spring-core/src/main/java/org/springframework/core/type/classreading/SimpleMethodMetadataReadingVisitor.java @@ -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() { diff --git a/spring-core/src/test/java/org/springframework/core/type/AnnotationMetadataTests.java b/spring-core/src/test/java/org/springframework/core/type/AnnotationMetadataTests.java index f1342883bc..7994a0da24 100644 --- a/spring-core/src/test/java/org/springframework/core/type/AnnotationMetadataTests.java +++ b/spring-core/src/test/java/org/springframework/core/type/AnnotationMetadataTests.java @@ -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 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) { }