Class MergedAnnotationResolver
AliasFor.
AnnotationUtils.findAnnotation(java.lang.Class<?>, java.lang.Class<T>) only walks one
meta-annotation level and never inspects AliasFor at all, which is why
@UltiToolsModule.scanBasePackages works today (its call site hand-reads the
meta-annotation) while every other @AliasFor attribute on that same annotation is
declared but has no reader. Per-site special-casing was rejected (D-01) because it does not
remove the defect class, it only moves it to the next attribute nobody wired up yet -- this
class is the single general replacement.
Deliberately cacheless. Phase 1 (D-35/D-38) forbids a static Class-keyed cache
anywhere in this codebase: it would pin every module's Class objects for the lifetime
of the JVM and defeat the plugin class loader's release on module unload (see
AnnotationLookupCache's class javadoc for the full
reasoning). The walk this class performs runs once per class at scan/load time, not on a
per-invocation hot path, so a v1 without a cache is a deliberate choice, not an oversight. Any
future cache added here must be instance-scoped and owned by the container/scanner instance
that uses it -- never a static field on this class.
This type is @ApiStatus.Internal: module authors need
@AliasFor to work, not to call this resolver directly (D-04).
- Since:
- 6.3.0
-
Nested Class Summary
Nested ClassesModifier and TypeClassDescriptionprivate static final classOne attribute's declared@AliasFortarget, for reciprocity checking.private static final classOne candidate value for a target attribute, plus where it came from (for conflict messages). -
Field Summary
Fields -
Constructor Summary
Constructors -
Method Summary
Modifier and TypeMethodDescriptionprivate static voidaddCandidateIfNonDefault(Map<String, List<MergedAnnotationResolver.Candidate>> out, String attributeName, Method sourceMethod, Annotation sourceInstance, Class<? extends Annotation> sourceType) private static <A extends Annotation>
voidcollectOverrideCandidates(Annotation dInstance, Class<A> target, Map<String, List<MergedAnnotationResolver.Candidate>> out) Collect every attribute-value overridedInstancecontributes towardstarget's merged view: for each ofdInstance's own attribute methods carrying@AliasFor, if the alias points attarget(explicitly, or via theAnnotation.classdefault meaning "within this same annotation") and the method's value ondInstancediffers from that method's own declared default, it is a candidate override for the aliased attribute name ontarget.private static <A extends Annotation>
AcreateProxy(A original, Class<A> annotationType, Map<String, Object> overrides) static <A extends Annotation>
AFind an annotation on a class, or on any of its (meta-)annotations, or on its superclass hierarchy -- generalizingAnnotationUtils.findAnnotationfrom one meta-annotation level to the whole annotation tree, and merging in any@AliasForoverride found along the way.(package private) static MethodfindAttributeMethod(Class<? extends Annotation> type, String name) private static <A extends Annotation>
AfindOnClassHierarchy(Class<?> clazz, Class<A> annotationType, Set<Class<? extends Annotation>> visited) private static ObjectinvokeAttribute(Method method, Annotation instance) private static booleanisJavaLangAnnotation(Class<? extends Annotation> type) private static booleanisMetaPresent(Class<? extends Annotation> declaring, Class<? extends Annotation> target) Whethertargetis present, directly or transitively, ondeclaring's own meta-annotation graph -- Spring's "meta-present" requirement for an explicit@AliasFor(annotation = ...).private static booleanisMetaPresent(Class<? extends Annotation> current, Class<? extends Annotation> target, Set<Class<? extends Annotation>> visited) static booleanisPresent(Class<?> clazz, Class<? extends Annotation> annotationType) WhetherannotationTypeis present anywhere onclazz's annotation tree or superclass hierarchy.private static <A extends Annotation>
Amerge(Annotation found, Class<A> annotationType, List<Annotation> ancestors) Compute the merged attribute view offound(the located instance ofannotationType), applying any@AliasForoverride declared by an annotation on the path from the scanned class down tofound-- includingfounditself, for a within-annotation mutual alias.private static StringresolveAttributeName(AliasFor aliasFor, Method declaringMethod) private static <A extends Annotation>
Asearch(Annotation current, Class<A> annotationType, Set<Class<? extends Annotation>> visited, List<Annotation> ancestors) Depth-first walk of one annotation's own meta-annotation tree.static voidvalidateAliases(Class<? extends Annotation> annotationType) Validate that everyAliasFor-annotated attribute declared onannotationTypesatisfies Spring's documented Implementation Requirements for an explicit meta-annotation alias, throwing aContainerExceptionnaming the annotation and the offending attribute when it does not (D-02).
-
Field Details
-
JAVA_LANG_ANNOTATION_PACKAGE
- See Also:
-
-
Constructor Details
-
MergedAnnotationResolver
private MergedAnnotationResolver()
-
-
Method Details
-
find
Find an annotation on a class, or on any of its (meta-)annotations, or on its superclass hierarchy -- generalizingAnnotationUtils.findAnnotationfrom one meta-annotation level to the whole annotation tree, and merging in any@AliasForoverride found along the way.- Type Parameters:
A- the annotation type- Parameters:
clazz- the class to searchannotationType- the annotation type to find- Returns:
- the merged annotation if found, or
null
-
isPresent
WhetherannotationTypeis present anywhere onclazz's annotation tree or superclass hierarchy.- Parameters:
clazz- the class to searchannotationType- the annotation type to look for- Returns:
trueif present
-
validateAliases
Validate that everyAliasFor-annotated attribute declared onannotationTypesatisfies Spring's documented Implementation Requirements for an explicit meta-annotation alias, throwing aContainerExceptionnaming the annotation and the offending attribute when it does not (D-02).The structural checks land in this class's Task 2 commit; this stub exists so every caller of
find(java.lang.Class<?>, java.lang.Class<A>)is already wired against the real method signature before the checks themselves are filled in.- Parameters:
annotationType- the annotation type to validate
-
isMetaPresent
private static boolean isMetaPresent(Class<? extends Annotation> declaring, Class<? extends Annotation> target) Whethertargetis present, directly or transitively, ondeclaring's own meta-annotation graph -- Spring's "meta-present" requirement for an explicit@AliasFor(annotation = ...). Cycle-guarded the same waysearch(java.lang.annotation.Annotation, java.lang.Class<A>, java.util.Set<java.lang.Class<? extends java.lang.annotation.Annotation>>, java.util.List<java.lang.annotation.Annotation>)is. -
isMetaPresent
private static boolean isMetaPresent(Class<? extends Annotation> current, Class<? extends Annotation> target, Set<Class<? extends Annotation>> visited) -
findOnClassHierarchy
private static <A extends Annotation> A findOnClassHierarchy(Class<?> clazz, Class<A> annotationType, Set<Class<? extends Annotation>> visited) -
search
private static <A extends Annotation> A search(Annotation current, Class<A> annotationType, Set<Class<? extends Annotation>> visited, List<Annotation> ancestors) Depth-first walk of one annotation's own meta-annotation tree.visitedis shared across the wholefind(java.lang.Class<?>, java.lang.Class<A>)call (not reset per branch) so a cyclic meta-annotation graph terminates instead of overflowing the stack (T-03-03) -- once an annotation type has been visited anywhere in this call, it is never walked into again. -
merge
private static <A extends Annotation> A merge(Annotation found, Class<A> annotationType, List<Annotation> ancestors) Compute the merged attribute view offound(the located instance ofannotationType), applying any@AliasForoverride declared by an annotation on the path from the scanned class down tofound-- includingfounditself, for a within-annotation mutual alias. -
collectOverrideCandidates
private static <A extends Annotation> void collectOverrideCandidates(Annotation dInstance, Class<A> target, Map<String, List<MergedAnnotationResolver.Candidate>> out) Collect every attribute-value overridedInstancecontributes towardstarget's merged view: for each ofdInstance's own attribute methods carrying@AliasFor, if the alias points attarget(explicitly, or via theAnnotation.classdefault meaning "within this same annotation") and the method's value ondInstancediffers from that method's own declared default, it is a candidate override for the aliased attribute name ontarget.When
dInstanceIS an instance oftargetitself, any attribute that participates in a within-annotation mutual alias also contributes its OWN declared value as a second candidate for its own attribute name -- otherwise a conflicting pair (foo=xaliased tobar,bar=yaliased tofoo) would only ever compare "the other side's value" against itself, and the conflict betweenxandywould never surface as more than one candidate for the same attribute. -
addCandidateIfNonDefault
private static void addCandidateIfNonDefault(Map<String, List<MergedAnnotationResolver.Candidate>> out, String attributeName, Method sourceMethod, Annotation sourceInstance, Class<? extends Annotation> sourceType) -
resolveAttributeName
-
findAttributeMethod
-
invokeAttribute
-
isJavaLangAnnotation
-
createProxy
private static <A extends Annotation> A createProxy(A original, Class<A> annotationType, Map<String, Object> overrides)
-