Skip to content

Commit cc9b39b

Browse files
committed
Extract AnnotatedElementAdapter to public type
This commit extracts AnnotatedElementAdapter from TypeDescriptor and introduces it as a public, top-level type in the org.springframework.core.annotation package, and AnnotatedElementUtils.forAnnotations() now returns an instance of AnnotatedElementAdapter instead of AnnotatedElementForAnnotations which has been removed. In addition, this commit adds missing Javadoc for AnnotatedElementAdapter and refines some of the implementation. Closes gh-34628
1 parent a376ef3 commit cc9b39b

File tree

3 files changed

+134
-114
lines changed

3 files changed

+134
-114
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
/*
2+
* Copyright 2002-2025 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.core.annotation;
18+
19+
import java.io.Serializable;
20+
import java.lang.annotation.Annotation;
21+
import java.lang.reflect.AnnotatedElement;
22+
import java.util.Arrays;
23+
24+
import org.jspecify.annotations.Nullable;
25+
26+
/**
27+
* Adapter for exposing a set of annotations as an {@link AnnotatedElement}, in
28+
* particular as input for various methods in {@link AnnotatedElementUtils}.
29+
*
30+
* @author Juergen Hoeller
31+
* @author Sam Brannen
32+
* @since 7.0
33+
* @see #from(Annotation[])
34+
* @see AnnotatedElementUtils#isAnnotated(AnnotatedElement, Class)
35+
* @see AnnotatedElementUtils#getMergedAnnotation(AnnotatedElement, Class)
36+
*/
37+
@SuppressWarnings("serial")
38+
public final class AnnotatedElementAdapter implements AnnotatedElement, Serializable {
39+
40+
private static final AnnotatedElementAdapter EMPTY = new AnnotatedElementAdapter(new Annotation[0]);
41+
42+
43+
/**
44+
* Create an {@code AnnotatedElementAdapter} from the supplied annotations.
45+
* <p>The supplied annotations will be considered to be both <em>present</em>
46+
* and <em>directly present</em> with regard to the results returned from
47+
* methods such as {@link #getAnnotation(Class)},
48+
* {@link #getDeclaredAnnotation(Class)}, etc.
49+
* <p>If the supplied annotations array is either {@code null} or empty, this
50+
* factory method will return an {@linkplain #isEmpty() empty} adapter.
51+
* @param annotations the annotations to expose via the {@link AnnotatedElement}
52+
* API
53+
* @return a new {@code AnnotatedElementAdapter}
54+
*/
55+
public static AnnotatedElementAdapter from(Annotation @Nullable [] annotations) {
56+
if (annotations == null || annotations.length == 0) {
57+
return EMPTY;
58+
}
59+
return new AnnotatedElementAdapter(annotations);
60+
}
61+
62+
63+
private final Annotation[] annotations;
64+
65+
66+
private AnnotatedElementAdapter(Annotation[] annotations) {
67+
this.annotations = annotations;
68+
}
69+
70+
71+
@Override
72+
public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) {
73+
for (Annotation annotation : this.annotations) {
74+
if (annotation.annotationType() == annotationClass) {
75+
return true;
76+
}
77+
}
78+
return false;
79+
}
80+
81+
@Override
82+
public <A extends Annotation> @Nullable A getAnnotation(Class<A> annotationClass) {
83+
for (Annotation annotation : this.annotations) {
84+
if (annotation.annotationType() == annotationClass) {
85+
return annotationClass.cast(annotation);
86+
}
87+
}
88+
return null;
89+
}
90+
91+
@Override
92+
public Annotation[] getAnnotations() {
93+
return (isEmpty() ? this.annotations : this.annotations.clone());
94+
}
95+
96+
@Override
97+
public <A extends Annotation> @Nullable A getDeclaredAnnotation(Class<A> annotationClass) {
98+
return getAnnotation(annotationClass);
99+
}
100+
101+
@Override
102+
public Annotation[] getDeclaredAnnotations() {
103+
return getAnnotations();
104+
}
105+
106+
/**
107+
* Determine if this {@code AnnotatedElementAdapter} is empty.
108+
* @return {@code true} if this adapter contains no annotations
109+
*/
110+
public boolean isEmpty() {
111+
return (this == EMPTY);
112+
}
113+
114+
@Override
115+
public boolean equals(@Nullable Object other) {
116+
return (this == other || (other instanceof AnnotatedElementAdapter that &&
117+
Arrays.equals(this.annotations, that.annotations)));
118+
}
119+
120+
@Override
121+
public int hashCode() {
122+
return Arrays.hashCode(this.annotations);
123+
}
124+
125+
@Override
126+
public String toString() {
127+
return Arrays.toString(this.annotations);
128+
}
129+
130+
}

Diff for: spring-core/src/main/java/org/springframework/core/annotation/AnnotatedElementUtils.java

+2-37
Original file line numberDiff line numberDiff line change
@@ -100,12 +100,12 @@ public abstract class AnnotatedElementUtils {
100100

101101
/**
102102
* Build an adapted {@link AnnotatedElement} for the given annotations,
103-
* typically for use with other methods on {@link AnnotatedElementUtils}.
103+
* typically for use with other methods in {@link AnnotatedElementUtils}.
104104
* @param annotations the annotations to expose through the {@code AnnotatedElement}
105105
* @since 4.3
106106
*/
107107
public static AnnotatedElement forAnnotations(Annotation... annotations) {
108-
return new AnnotatedElementForAnnotations(annotations);
108+
return AnnotatedElementAdapter.from(annotations);
109109
}
110110

111111
/**
@@ -837,39 +837,4 @@ private static <A extends Annotation> Comparator<MergedAnnotation<A>> highAggreg
837837
return annotation.asAnnotationAttributes(Adapt.values(classValuesAsString, nestedAnnotationsAsMap));
838838
}
839839

840-
841-
/**
842-
* Adapted {@link AnnotatedElement} that holds specific annotations.
843-
*/
844-
private static class AnnotatedElementForAnnotations implements AnnotatedElement {
845-
846-
private final Annotation[] annotations;
847-
848-
AnnotatedElementForAnnotations(Annotation... annotations) {
849-
this.annotations = annotations;
850-
}
851-
852-
@Override
853-
@SuppressWarnings("unchecked")
854-
public <T extends Annotation> @Nullable T getAnnotation(Class<T> annotationClass) {
855-
for (Annotation annotation : this.annotations) {
856-
if (annotation.annotationType() == annotationClass) {
857-
return (T) annotation;
858-
}
859-
}
860-
return null;
861-
}
862-
863-
@Override
864-
public Annotation[] getAnnotations() {
865-
return this.annotations.clone();
866-
}
867-
868-
@Override
869-
public Annotation[] getDeclaredAnnotations() {
870-
return this.annotations.clone();
871-
}
872-
873-
}
874-
875840
}

Diff for: spring-core/src/main/java/org/springframework/core/convert/TypeDescriptor.java

+2-77
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818

1919
import java.io.Serializable;
2020
import java.lang.annotation.Annotation;
21-
import java.lang.reflect.AnnotatedElement;
2221
import java.lang.reflect.Field;
2322
import java.lang.reflect.Type;
2423
import java.util.Arrays;
@@ -32,13 +31,15 @@
3231

3332
import org.springframework.core.MethodParameter;
3433
import org.springframework.core.ResolvableType;
34+
import org.springframework.core.annotation.AnnotatedElementAdapter;
3535
import org.springframework.core.annotation.AnnotatedElementUtils;
3636
import org.springframework.lang.Contract;
3737
import org.springframework.util.Assert;
3838
import org.springframework.util.ClassUtils;
3939

4040
/**
4141
* Contextual descriptor about a type to convert from or to.
42+
*
4243
* <p>Capable of representing arrays and generic collection types.
4344
*
4445
* @author Keith Donald
@@ -731,82 +732,6 @@ private static String getName(Class<?> clazz) {
731732
}
732733

733734

734-
/**
735-
* Adapter class for exposing a {@code TypeDescriptor}'s annotations as an
736-
* {@link AnnotatedElement}, in particular to {@link AnnotatedElementUtils}.
737-
* @see AnnotatedElementUtils#isAnnotated(AnnotatedElement, Class)
738-
* @see AnnotatedElementUtils#getMergedAnnotation(AnnotatedElement, Class)
739-
*/
740-
private static final class AnnotatedElementAdapter implements AnnotatedElement, Serializable {
741-
742-
private static final AnnotatedElementAdapter EMPTY = new AnnotatedElementAdapter(new Annotation[0]);
743-
744-
private final Annotation[] annotations;
745-
746-
private AnnotatedElementAdapter(Annotation[] annotations) {
747-
this.annotations = annotations;
748-
}
749-
750-
private static AnnotatedElementAdapter from(Annotation @Nullable [] annotations) {
751-
if (annotations == null || annotations.length == 0) {
752-
return EMPTY;
753-
}
754-
return new AnnotatedElementAdapter(annotations);
755-
}
756-
757-
@Override
758-
public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) {
759-
for (Annotation annotation : this.annotations) {
760-
if (annotation.annotationType() == annotationClass) {
761-
return true;
762-
}
763-
}
764-
return false;
765-
}
766-
767-
@Override
768-
@SuppressWarnings("unchecked")
769-
public <T extends Annotation> @Nullable T getAnnotation(Class<T> annotationClass) {
770-
for (Annotation annotation : this.annotations) {
771-
if (annotation.annotationType() == annotationClass) {
772-
return (T) annotation;
773-
}
774-
}
775-
return null;
776-
}
777-
778-
@Override
779-
public Annotation[] getAnnotations() {
780-
return (isEmpty() ? this.annotations : this.annotations.clone());
781-
}
782-
783-
@Override
784-
public Annotation[] getDeclaredAnnotations() {
785-
return getAnnotations();
786-
}
787-
788-
public boolean isEmpty() {
789-
return (this.annotations.length == 0);
790-
}
791-
792-
@Override
793-
public boolean equals(@Nullable Object other) {
794-
return (this == other || (other instanceof AnnotatedElementAdapter that &&
795-
Arrays.equals(this.annotations, that.annotations)));
796-
}
797-
798-
@Override
799-
public int hashCode() {
800-
return Arrays.hashCode(this.annotations);
801-
}
802-
803-
@Override
804-
public String toString() {
805-
return Arrays.toString(this.annotations);
806-
}
807-
}
808-
809-
810735
private interface AnnotatedElementSupplier extends Supplier<AnnotatedElementAdapter>, Serializable {
811736
}
812737

0 commit comments

Comments
 (0)