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
This commit is contained in:
parent
a376ef36e4
commit
cc9b39b53f
|
@ -0,0 +1,130 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2025 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.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.core.annotation;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.lang.annotation.Annotation;
|
||||||
|
import java.lang.reflect.AnnotatedElement;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adapter for exposing a set of annotations as an {@link AnnotatedElement}, in
|
||||||
|
* particular as input for various methods in {@link AnnotatedElementUtils}.
|
||||||
|
*
|
||||||
|
* @author Juergen Hoeller
|
||||||
|
* @author Sam Brannen
|
||||||
|
* @since 7.0
|
||||||
|
* @see #from(Annotation[])
|
||||||
|
* @see AnnotatedElementUtils#isAnnotated(AnnotatedElement, Class)
|
||||||
|
* @see AnnotatedElementUtils#getMergedAnnotation(AnnotatedElement, Class)
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("serial")
|
||||||
|
public final class AnnotatedElementAdapter implements AnnotatedElement, Serializable {
|
||||||
|
|
||||||
|
private static final AnnotatedElementAdapter EMPTY = new AnnotatedElementAdapter(new Annotation[0]);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an {@code AnnotatedElementAdapter} from the supplied annotations.
|
||||||
|
* <p>The supplied annotations will be considered to be both <em>present</em>
|
||||||
|
* and <em>directly present</em> with regard to the results returned from
|
||||||
|
* methods such as {@link #getAnnotation(Class)},
|
||||||
|
* {@link #getDeclaredAnnotation(Class)}, etc.
|
||||||
|
* <p>If the supplied annotations array is either {@code null} or empty, this
|
||||||
|
* factory method will return an {@linkplain #isEmpty() empty} adapter.
|
||||||
|
* @param annotations the annotations to expose via the {@link AnnotatedElement}
|
||||||
|
* API
|
||||||
|
* @return a new {@code AnnotatedElementAdapter}
|
||||||
|
*/
|
||||||
|
public static AnnotatedElementAdapter from(Annotation @Nullable [] annotations) {
|
||||||
|
if (annotations == null || annotations.length == 0) {
|
||||||
|
return EMPTY;
|
||||||
|
}
|
||||||
|
return new AnnotatedElementAdapter(annotations);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private final Annotation[] annotations;
|
||||||
|
|
||||||
|
|
||||||
|
private AnnotatedElementAdapter(Annotation[] annotations) {
|
||||||
|
this.annotations = annotations;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) {
|
||||||
|
for (Annotation annotation : this.annotations) {
|
||||||
|
if (annotation.annotationType() == annotationClass) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <A extends Annotation> @Nullable A getAnnotation(Class<A> annotationClass) {
|
||||||
|
for (Annotation annotation : this.annotations) {
|
||||||
|
if (annotation.annotationType() == annotationClass) {
|
||||||
|
return annotationClass.cast(annotation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Annotation[] getAnnotations() {
|
||||||
|
return (isEmpty() ? this.annotations : this.annotations.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <A extends Annotation> @Nullable A getDeclaredAnnotation(Class<A> annotationClass) {
|
||||||
|
return getAnnotation(annotationClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Annotation[] getDeclaredAnnotations() {
|
||||||
|
return getAnnotations();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if this {@code AnnotatedElementAdapter} is empty.
|
||||||
|
* @return {@code true} if this adapter contains no annotations
|
||||||
|
*/
|
||||||
|
public boolean isEmpty() {
|
||||||
|
return (this == EMPTY);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(@Nullable Object other) {
|
||||||
|
return (this == other || (other instanceof AnnotatedElementAdapter that &&
|
||||||
|
Arrays.equals(this.annotations, that.annotations)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Arrays.hashCode(this.annotations);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return Arrays.toString(this.annotations);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -100,12 +100,12 @@ public abstract class AnnotatedElementUtils {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Build an adapted {@link AnnotatedElement} for the given annotations,
|
* Build an adapted {@link AnnotatedElement} for the given annotations,
|
||||||
* typically for use with other methods on {@link AnnotatedElementUtils}.
|
* typically for use with other methods in {@link AnnotatedElementUtils}.
|
||||||
* @param annotations the annotations to expose through the {@code AnnotatedElement}
|
* @param annotations the annotations to expose through the {@code AnnotatedElement}
|
||||||
* @since 4.3
|
* @since 4.3
|
||||||
*/
|
*/
|
||||||
public static AnnotatedElement forAnnotations(Annotation... annotations) {
|
public static AnnotatedElement forAnnotations(Annotation... annotations) {
|
||||||
return new AnnotatedElementForAnnotations(annotations);
|
return AnnotatedElementAdapter.from(annotations);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -837,39 +837,4 @@ public abstract class AnnotatedElementUtils {
|
||||||
return annotation.asAnnotationAttributes(Adapt.values(classValuesAsString, nestedAnnotationsAsMap));
|
return annotation.asAnnotationAttributes(Adapt.values(classValuesAsString, nestedAnnotationsAsMap));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adapted {@link AnnotatedElement} that holds specific annotations.
|
|
||||||
*/
|
|
||||||
private static class AnnotatedElementForAnnotations implements AnnotatedElement {
|
|
||||||
|
|
||||||
private final Annotation[] annotations;
|
|
||||||
|
|
||||||
AnnotatedElementForAnnotations(Annotation... annotations) {
|
|
||||||
this.annotations = annotations;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public <T extends Annotation> @Nullable T getAnnotation(Class<T> annotationClass) {
|
|
||||||
for (Annotation annotation : this.annotations) {
|
|
||||||
if (annotation.annotationType() == annotationClass) {
|
|
||||||
return (T) annotation;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Annotation[] getAnnotations() {
|
|
||||||
return this.annotations.clone();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Annotation[] getDeclaredAnnotations() {
|
|
||||||
return this.annotations.clone();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,6 @@ package org.springframework.core.convert;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.lang.annotation.Annotation;
|
import java.lang.annotation.Annotation;
|
||||||
import java.lang.reflect.AnnotatedElement;
|
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.Type;
|
import java.lang.reflect.Type;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
@ -32,6 +31,7 @@ import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
import org.springframework.core.MethodParameter;
|
import org.springframework.core.MethodParameter;
|
||||||
import org.springframework.core.ResolvableType;
|
import org.springframework.core.ResolvableType;
|
||||||
|
import org.springframework.core.annotation.AnnotatedElementAdapter;
|
||||||
import org.springframework.core.annotation.AnnotatedElementUtils;
|
import org.springframework.core.annotation.AnnotatedElementUtils;
|
||||||
import org.springframework.lang.Contract;
|
import org.springframework.lang.Contract;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
|
@ -39,6 +39,7 @@ import org.springframework.util.ClassUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Contextual descriptor about a type to convert from or to.
|
* Contextual descriptor about a type to convert from or to.
|
||||||
|
*
|
||||||
* <p>Capable of representing arrays and generic collection types.
|
* <p>Capable of representing arrays and generic collection types.
|
||||||
*
|
*
|
||||||
* @author Keith Donald
|
* @author Keith Donald
|
||||||
|
@ -731,82 +732,6 @@ public class TypeDescriptor implements Serializable {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adapter class for exposing a {@code TypeDescriptor}'s annotations as an
|
|
||||||
* {@link AnnotatedElement}, in particular to {@link AnnotatedElementUtils}.
|
|
||||||
* @see AnnotatedElementUtils#isAnnotated(AnnotatedElement, Class)
|
|
||||||
* @see AnnotatedElementUtils#getMergedAnnotation(AnnotatedElement, Class)
|
|
||||||
*/
|
|
||||||
private static final class AnnotatedElementAdapter implements AnnotatedElement, Serializable {
|
|
||||||
|
|
||||||
private static final AnnotatedElementAdapter EMPTY = new AnnotatedElementAdapter(new Annotation[0]);
|
|
||||||
|
|
||||||
private final Annotation[] annotations;
|
|
||||||
|
|
||||||
private AnnotatedElementAdapter(Annotation[] annotations) {
|
|
||||||
this.annotations = annotations;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static AnnotatedElementAdapter from(Annotation @Nullable [] annotations) {
|
|
||||||
if (annotations == null || annotations.length == 0) {
|
|
||||||
return EMPTY;
|
|
||||||
}
|
|
||||||
return new AnnotatedElementAdapter(annotations);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) {
|
|
||||||
for (Annotation annotation : this.annotations) {
|
|
||||||
if (annotation.annotationType() == annotationClass) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public <T extends Annotation> @Nullable T getAnnotation(Class<T> annotationClass) {
|
|
||||||
for (Annotation annotation : this.annotations) {
|
|
||||||
if (annotation.annotationType() == annotationClass) {
|
|
||||||
return (T) annotation;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Annotation[] getAnnotations() {
|
|
||||||
return (isEmpty() ? this.annotations : this.annotations.clone());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Annotation[] getDeclaredAnnotations() {
|
|
||||||
return getAnnotations();
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isEmpty() {
|
|
||||||
return (this.annotations.length == 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(@Nullable Object other) {
|
|
||||||
return (this == other || (other instanceof AnnotatedElementAdapter that &&
|
|
||||||
Arrays.equals(this.annotations, that.annotations)));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
return Arrays.hashCode(this.annotations);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return Arrays.toString(this.annotations);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private interface AnnotatedElementSupplier extends Supplier<AnnotatedElementAdapter>, Serializable {
|
private interface AnnotatedElementSupplier extends Supplier<AnnotatedElementAdapter>, Serializable {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue