Spring uses annotations as declarative metadata for component discovery, configuration, dependency injection, transactions, web request mapping, and other framework services. Java reflection exposes the raw annotations; Spring adds a consistent model for composed annotations, aliases, search strategies, and metadata reading without loading classes.
Java Annotation Semantics #
An annotation declaration is itself an interface whose implicit superinterface is
java.lang.annotation.Annotation:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Audited {
}Runtime reflection can inspect @Audited when it is directly present on a class.
Meta-annotations such as @Retention, @Target, and @Documented describe an
annotation declaration but do not cause arbitrary transitive lookup.
SOURCE, CLASS, and RUNTIME retention determine whether metadata remains in
source only, in the class file, or available through reflection. @Target
restricts declaration or type-use locations. A repeatable annotation names a
container through @Repeatable; reflection and Spring can expose either the
logical repeated annotations or their physical container.
@Inherited is a narrow exception. Selected Class lookup methods can find an
inherited annotation on a superclass when the annotation declaration carries
@Inherited. The rule does not apply through interfaces, does not cover methods or
fields, and is distinct from Spring’s composed-annotation model.
Composed Annotations #
Spring interprets annotations placed on other annotation declarations. For example,
@Service is meta-annotated with @Component:
@Component
public @interface Service {
@AliasFor(annotation = Component.class)
String value() default "";
}A class annotated with @Service is consequently a component candidate even though
plain reflection would not report @Component as directly present. Applications can
use the same mechanism to create domain-specific annotations:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Service
@Transactional(readOnly = true)
public @interface ReadOnlyService {
}The result is a programming model rather than a Java language rule. Spring code must perform the traversal, cycle detection, filtering, and attribute synthesis.
Merged Annotations #
MergedAnnotations is Spring’s central query model for directly present, inherited,
and meta-present annotations. Its SearchStrategy controls which type relationships
are traversed:
DIRECTexamines only annotations directly declared on the element.INHERITED_ANNOTATIONSfollows Java’s@Inheritedrules.SUPERCLASSsearches the superclass hierarchy.TYPE_HIERARCHYsearches superclasses and implemented interfaces.
Use the narrowest strategy that matches the framework feature. A broad hierarchy search can produce metadata that Java reflection alone would not expose.
MergedAnnotation<T> represents one discovered annotation together with its
distance, aggregate position, source, and synthesized attributes. Synthesis resolves
Spring alias relationships declared with @AliasFor. It can therefore present a
coherent attribute view across a composed annotation and its meta-annotations.
Alias declarations must remain consistent. Within one annotation, aliased attributes need compatible return types and defaults. Cross-annotation aliases must identify an attribute reachable in the meta-annotation hierarchy.
Metadata Abstractions #
Spring separates general annotated-element metadata from class- and method-specific metadata:
AnnotatedTypeMetadataexposes annotation queries common to types and methods.AnnotationMetadataadds class information and access to annotated methods.MethodMetadataadds declaring-class, return-type, and method properties.
Reflection-backed implementations inspect loaded classes. Class-reading implementations use bytecode metadata, allowing infrastructure such as component scanning and configuration parsing to inspect a class without initializing it. Avoiding class loading prevents static initialization side effects and permits metadata discovery before every optional dependency is resolvable.
For a loaded class, use the supported introspection entry point:
AnnotationMetadata metadata =
AnnotationMetadata.introspect(ExampleConfiguration.class);
boolean configuration =
metadata.hasAnnotation(Configuration.class.getName());Framework internals can instead obtain a MetadataReader from a
MetadataReaderFactory to read a .class resource.
Configuration-Class Processing #
ConfigurationClassParser consumes class metadata while processing
@Configuration, component scans, imports, property sources, conditions, and
@Bean methods. @Import can contribute configuration classes, selectors, or
registrars. @Conditional implementations can veto configuration during the
applicable parsing or bean-registration phase.
The broad flow is:
- Discover candidate bean definitions.
- Read class metadata.
- Determine whether each candidate is a configuration class.
- Evaluate conditions and traverse imports and component scans.
- Discover bean methods and register the resulting bean definitions.
Annotation parsing describes definitions; later bean-factory phases instantiate and initialize beans. Keeping those phases distinct explains why finding an annotation does not immediately execute the annotated behavior.
Choosing an API #
Use Java reflection when direct Java annotation semantics are sufficient. Use
MergedAnnotations when implementing infrastructure that must honor Spring’s
composed annotations or aliases. Use AnnotationMetadata or MetadataReader when
class-level and method-level metadata must be inspected during container startup,
especially when loading the class would be premature.
Application code should normally consume the higher-level Spring facility—such as component scanning, transaction management, or MVC mapping—rather than reproduce its annotation search rules.
Comments
Read or comment on GitHub (sign-in required to comment)