0
# Annotation Processing
1
2
Spring Core provides comprehensive annotation processing capabilities that enable meta-annotation support, attribute overrides, and sophisticated annotation composition. This system forms the foundation for Spring's annotation-driven programming model.
3
4
## Core Annotation Support
5
6
Spring provides several key annotations and utilities for annotation processing.
7
8
**Core Annotations**
9
```java { .api }
10
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
11
@Retention(RetentionPolicy.RUNTIME)
12
@Documented
13
public @interface Order {
14
int value() default Ordered.LOWEST_PRECEDENCE;
15
}
16
17
@Target(ElementType.METHOD)
18
@Retention(RetentionPolicy.RUNTIME)
19
@Documented
20
public @interface AliasFor {
21
String value() default "";
22
String attribute() default "";
23
Class<? extends Annotation> annotation() default Annotation.class;
24
}
25
```
26
27
**Usage Examples**
28
```java
29
// Using @Order annotation
30
@Component
31
@Order(1)
32
public class HighPriorityService {
33
// Implementation
34
}
35
36
@Component
37
@Order(Ordered.HIGHEST_PRECEDENCE)
38
public class CriticalService {
39
// Implementation
40
}
41
42
// Using @AliasFor for attribute aliasing
43
@Target(ElementType.TYPE)
44
@Retention(RetentionPolicy.RUNTIME)
45
@Component
46
public @interface Service {
47
@AliasFor(annotation = Component.class)
48
String value() default "";
49
50
@AliasFor("value")
51
String name() default "";
52
}
53
54
@Service("userService") // Equivalent to @Service(name = "userService")
55
public class UserService {
56
// Implementation
57
}
58
```
59
60
## Merged Annotations System
61
62
Spring's merged annotation system provides a powerful way to handle annotation composition, inheritance, and attribute overrides.
63
64
**MergedAnnotations Interface**
65
```java { .api }
66
public interface MergedAnnotations extends Iterable<MergedAnnotation<Annotation>> {
67
<A extends Annotation> boolean isPresent(Class<A> annotationType);
68
boolean isPresent(String annotationType);
69
70
<A extends Annotation> boolean isDirectlyPresent(Class<A> annotationType);
71
boolean isDirectlyPresent(String annotationType);
72
73
<A extends Annotation> MergedAnnotation<A> get(Class<A> annotationType);
74
<A extends Annotation> MergedAnnotation<A> get(Class<A> annotationType,
75
Predicate<? super MergedAnnotation<A>> predicate);
76
MergedAnnotation<Annotation> get(String annotationType);
77
MergedAnnotation<Annotation> get(String annotationType,
78
Predicate<? super MergedAnnotation<Annotation>> predicate);
79
80
<A extends Annotation> Stream<MergedAnnotation<A>> stream(Class<A> annotationType);
81
Stream<MergedAnnotation<Annotation>> stream(String annotationType);
82
Stream<MergedAnnotation<Annotation>> stream();
83
84
// Static factory methods
85
static MergedAnnotations from(AnnotatedElement element);
86
static MergedAnnotations from(AnnotatedElement element, SearchStrategy searchStrategy);
87
static MergedAnnotations from(Annotation... annotations);
88
static MergedAnnotations from(Object source, Annotation... annotations);
89
}
90
91
public enum SearchStrategy {
92
DIRECT,
93
INHERITED_ANNOTATIONS,
94
SUPERCLASS,
95
TYPE_HIERARCHY,
96
TYPE_HIERARCHY_AND_ENCLOSING_CLASSES
97
}
98
```
99
100
**MergedAnnotation Interface**
101
```java { .api }
102
public interface MergedAnnotation<A extends Annotation> {
103
Class<A> getType();
104
boolean isPresent();
105
boolean isDirectlyPresent();
106
boolean isMetaPresent();
107
108
int getDistance();
109
int getAggregateIndex();
110
Object getSource();
111
MergedAnnotation<?> getParent();
112
MergedAnnotation<A> getRoot();
113
114
List<Class<? extends Annotation>> getMetaTypes();
115
116
boolean hasAttribute(String attributeName);
117
boolean hasDefaultValue(String attributeName);
118
119
<T> Optional<T> getValue(String attributeName);
120
<T> Optional<T> getValue(String attributeName, Class<T> type);
121
<T> Optional<T> getDefaultValue(String attributeName);
122
<T> Optional<T> getDefaultValue(String attributeName, Class<T> type);
123
124
MergedAnnotation<A> filterAttributes(Predicate<String> predicate);
125
MergedAnnotation<A> withAttribute(String attributeName, Object value);
126
127
AnnotationAttributes asAnnotationAttributes();
128
AnnotationAttributes asAnnotationAttributes(Adapt... adaptations);
129
Map<String, Object> asMap();
130
Map<String, Object> asMap(Adapt... adaptations);
131
132
<T extends Map<String, Object>> T asMap(Function<MergedAnnotation<?>, T> factory, Adapt... adaptations);
133
134
A synthesize();
135
A synthesize(Predicate<String> condition) throws NoSuchElementException;
136
137
Optional<A> synthesize(Predicate<String> condition);
138
139
enum Adapt {
140
CLASS_TO_STRING,
141
ANNOTATION_TO_MAP
142
}
143
}
144
```
145
146
**Usage Examples**
147
```java
148
// Basic merged annotation usage
149
@Service
150
@Transactional
151
public class BusinessService {
152
public void processData() { }
153
}
154
155
Class<?> serviceClass = BusinessService.class;
156
MergedAnnotations annotations = MergedAnnotations.from(serviceClass);
157
158
// Check for annotation presence
159
boolean hasService = annotations.isPresent(Service.class);
160
boolean hasTransactional = annotations.isPresent(Transactional.class);
161
boolean hasComponent = annotations.isPresent(Component.class); // true due to @Service -> @Component
162
163
// Get merged annotation with attribute values
164
MergedAnnotation<Service> serviceAnnotation = annotations.get(Service.class);
165
String serviceName = serviceAnnotation.getValue("value", String.class).orElse("");
166
167
// Work with meta-annotations
168
MergedAnnotation<Component> componentAnnotation = annotations.get(Component.class);
169
boolean isMetaPresent = componentAnnotation.isMetaPresent(); // true
170
int distance = componentAnnotation.getDistance(); // 1 (via @Service)
171
172
// Stream all annotations
173
annotations.stream()
174
.forEach(annotation -> {
175
System.out.println("Found: " + annotation.getType().getSimpleName());
176
System.out.println("Distance: " + annotation.getDistance());
177
});
178
179
// Get annotation attributes
180
Method method = BusinessService.class.getMethod("processData");
181
MergedAnnotations methodAnnotations = MergedAnnotations.from(method);
182
MergedAnnotation<Transactional> txAnnotation = methodAnnotations.get(Transactional.class);
183
184
Map<String, Object> attributes = txAnnotation.asMap();
185
String propagation = txAnnotation.getValue("propagation", String.class).orElse("REQUIRED");
186
```
187
188
## Annotation Utilities
189
190
Spring provides extensive utilities for working with annotations in various contexts.
191
192
**AnnotationUtils Class**
193
```java { .api }
194
public abstract class AnnotationUtils {
195
// Find annotations
196
public static <A extends Annotation> A findAnnotation(Method method, Class<A> annotationType);
197
public static <A extends Annotation> A findAnnotation(Class<?> clazz, Class<A> annotationType);
198
public static <A extends Annotation> A findAnnotation(AnnotatedElement annotatedElement, Class<A> annotationType);
199
200
// Get annotation attributes
201
public static Map<String, Object> getAnnotationAttributes(Annotation annotation);
202
public static Map<String, Object> getAnnotationAttributes(Annotation annotation, boolean classValuesAsString);
203
public static Map<String, Object> getAnnotationAttributes(Annotation annotation, boolean classValuesAsString, boolean nestedAnnotationsAsMap);
204
205
// Synthesize annotations
206
public static <A extends Annotation> A synthesizeAnnotation(A annotation);
207
public static <A extends Annotation> A synthesizeAnnotation(A annotation, AnnotatedElement annotatedElement);
208
public static <A extends Annotation> A synthesizeAnnotation(Map<String, Object> attributes, Class<A> annotationType, AnnotatedElement annotatedElement);
209
210
// Utility methods
211
public static boolean isCandidateClass(Class<?> clazz, Class<? extends Annotation> annotationType);
212
public static boolean isCandidateClass(Class<?> clazz, Collection<Class<? extends Annotation>> annotationTypes);
213
public static boolean isCandidateClass(Class<?> clazz, String annotationName);
214
215
public static String getDefaultValue(Class<? extends Annotation> annotationType, String attributeName);
216
public static <A extends Annotation> A getAnnotation(Annotation annotation, Class<A> annotationType);
217
public static <A extends Annotation> A getAnnotation(AnnotatedElement annotatedElement, Class<A> annotationType);
218
219
public static void clearCache();
220
}
221
```
222
223
**AnnotatedElementUtils Class**
224
```java { .api }
225
public abstract class AnnotatedElementUtils {
226
// Has annotation checks
227
public static boolean hasAnnotation(AnnotatedElement element, Class<? extends Annotation> annotationType);
228
public static boolean hasAnnotation(AnnotatedElement element, String annotationName);
229
230
// Find merged annotations
231
public static <A extends Annotation> A findMergedAnnotation(AnnotatedElement element, Class<A> annotationType);
232
public static <A extends Annotation> A findMergedAnnotation(AnnotatedElement element, String annotationName);
233
234
public static <A extends Annotation> Set<A> findAllMergedAnnotations(AnnotatedElement element, Class<A> annotationType);
235
public static Set<Annotation> findAllMergedAnnotations(AnnotatedElement element, Set<Class<? extends Annotation>> annotationTypes);
236
237
// Get merged annotation attributes
238
public static AnnotationAttributes findMergedAnnotationAttributes(AnnotatedElement element, Class<? extends Annotation> annotationType, boolean classValuesAsString, boolean nestedAnnotationsAsMap);
239
public static AnnotationAttributes findMergedAnnotationAttributes(AnnotatedElement element, String annotationName, boolean classValuesAsString, boolean nestedAnnotationsAsMap);
240
241
public static MultiValueMap<String, Object> getAllAnnotationAttributes(AnnotatedElement element, String annotationName);
242
public static MultiValueMap<String, Object> getAllAnnotationAttributes(AnnotatedElement element, String annotationName, boolean classValuesAsString, boolean nestedAnnotationsAsMap);
243
244
// Meta-annotation support
245
public static boolean isAnnotated(AnnotatedElement element, Class<? extends Annotation> annotationType);
246
public static boolean isAnnotated(AnnotatedElement element, String annotationName);
247
248
public static AnnotationAttributes getMergedAnnotationAttributes(AnnotatedElement element, Class<? extends Annotation> annotationType);
249
public static AnnotationAttributes getMergedAnnotationAttributes(AnnotatedElement element, String annotationName);
250
}
251
```
252
253
**Usage Examples**
254
```java
255
// Define custom annotation with meta-annotation support
256
@Target(ElementType.TYPE)
257
@Retention(RetentionPolicy.RUNTIME)
258
@Service
259
@Transactional(propagation = Propagation.REQUIRED)
260
public @interface BusinessService {
261
@AliasFor(annotation = Service.class, attribute = "value")
262
String value() default "";
263
264
@AliasFor(annotation = Transactional.class, attribute = "timeout")
265
int timeout() default -1;
266
}
267
268
@BusinessService("orderService")
269
public class OrderService {
270
public void processOrder() { }
271
}
272
273
// Use AnnotationUtils
274
Class<?> orderServiceClass = OrderService.class;
275
276
// Find annotations (follows meta-annotation hierarchy)
277
Service serviceAnnotation = AnnotationUtils.findAnnotation(orderServiceClass, Service.class);
278
Transactional txAnnotation = AnnotationUtils.findAnnotation(orderServiceClass, Transactional.class);
279
280
// Get annotation attributes
281
Map<String, Object> businessServiceAttrs = AnnotationUtils.getAnnotationAttributes(
282
orderServiceClass.getAnnotation(BusinessService.class)
283
);
284
285
// Synthesize annotation with attribute overrides
286
BusinessService businessServiceAnn = orderServiceClass.getAnnotation(BusinessService.class);
287
Service synthesizedService = AnnotationUtils.synthesizeAnnotation(businessServiceAnn, orderServiceClass);
288
289
// Use AnnotatedElementUtils for more advanced processing
290
boolean hasService = AnnotatedElementUtils.hasAnnotation(orderServiceClass, Service.class); // true
291
boolean hasTransactional = AnnotatedElementUtils.hasAnnotation(orderServiceClass, Transactional.class); // true
292
293
// Find merged annotations with attribute overrides resolved
294
Service mergedService = AnnotatedElementUtils.findMergedAnnotation(orderServiceClass, Service.class);
295
String serviceName = mergedService.value(); // "orderService"
296
297
Transactional mergedTx = AnnotatedElementUtils.findMergedAnnotation(orderServiceClass, Transactional.class);
298
Propagation propagation = mergedTx.propagation(); // REQUIRED
299
int timeout = mergedTx.timeout(); // resolved from BusinessService.timeout
300
301
// Get merged annotation attributes
302
AnnotationAttributes serviceAttrs = AnnotatedElementUtils.findMergedAnnotationAttributes(
303
orderServiceClass, Service.class, false, false
304
);
305
306
AnnotationAttributes txAttrs = AnnotatedElementUtils.findMergedAnnotationAttributes(
307
orderServiceClass, Transactional.class, false, false
308
);
309
```
310
311
## Annotation Order Comparators
312
313
Spring provides ordering support that respects the `@Order` annotation and `Ordered` interface.
314
315
**AnnotationAwareOrderComparator Class**
316
```java { .api }
317
public class AnnotationAwareOrderComparator extends OrderComparator {
318
public static final AnnotationAwareOrderComparator INSTANCE = new AnnotationAwareOrderComparator();
319
320
public static void sort(List<?> list);
321
public static void sort(Object[] array);
322
323
@Override
324
protected Integer findOrder(Object obj);
325
public Integer getPriority(Object obj);
326
327
protected Integer findOrderFromAnnotation(Object obj);
328
}
329
```
330
331
**Usage Examples**
332
```java
333
// Components with different ordering
334
@Component
335
@Order(1)
336
public class FirstComponent { }
337
338
@Component
339
@Order(3)
340
public class ThirdComponent { }
341
342
@Component
343
public class UnorderedComponent implements Ordered {
344
@Override
345
public int getOrder() {
346
return 2;
347
}
348
}
349
350
// Sort components by order
351
List<Object> components = Arrays.asList(
352
new ThirdComponent(),
353
new FirstComponent(),
354
new UnorderedComponent()
355
);
356
357
AnnotationAwareOrderComparator.sort(components);
358
// Result: [FirstComponent(1), UnorderedComponent(2), ThirdComponent(3)]
359
360
// Check ordering at runtime
361
AnnotationAwareOrderComparator comparator = AnnotationAwareOrderComparator.INSTANCE;
362
Integer firstOrder = comparator.findOrder(new FirstComponent()); // 1
363
Integer thirdOrder = comparator.findOrder(new ThirdComponent()); // 3
364
```
365
366
## Annotation Attributes Support
367
368
**AnnotationAttributes Class**
369
```java { .api }
370
public class AnnotationAttributes extends LinkedHashMap<String, Object> {
371
public AnnotationAttributes();
372
public AnnotationAttributes(Class<? extends Annotation> annotationType);
373
public AnnotationAttributes(Map<String, Object> map);
374
public AnnotationAttributes(AnnotationAttributes other);
375
376
public Class<? extends Annotation> annotationType();
377
public String getDisplayName();
378
379
// Type-safe attribute access
380
public String getString(String attributeName);
381
public String[] getStringArray(String attributeName);
382
public boolean getBoolean(String attributeName);
383
public <N extends Number> N getNumber(String attributeName);
384
public <E extends Enum<?>> E getEnum(String attributeName);
385
public <T> Class<? extends T> getClass(String attributeName);
386
public Class<?>[] getClassArray(String attributeName);
387
public AnnotationAttributes getAnnotation(String attributeName);
388
public <A extends Annotation> A getAnnotation(String attributeName, Class<A> annotationType);
389
public AnnotationAttributes[] getAnnotationArray(String attributeName);
390
391
// Static factory methods
392
public static AnnotationAttributes fromMap(Map<String, Object> map);
393
}
394
```
395
396
**Usage Examples**
397
```java
398
// Create and populate annotation attributes
399
AnnotationAttributes attributes = new AnnotationAttributes(Service.class);
400
attributes.put("value", "myService");
401
attributes.put("qualifiers", new String[]{"primary", "default"});
402
403
// Type-safe access
404
String serviceName = attributes.getString("value"); // "myService"
405
String[] qualifiers = attributes.getStringArray("qualifiers"); // ["primary", "default"]
406
407
// From annotation
408
@Component("testComponent")
409
public class TestComponent { }
410
411
Annotation componentAnn = TestComponent.class.getAnnotation(Component.class);
412
Map<String, Object> attrMap = AnnotationUtils.getAnnotationAttributes(componentAnn);
413
AnnotationAttributes componentAttrs = AnnotationAttributes.fromMap(attrMap);
414
415
String componentName = componentAttrs.getString("value"); // "testComponent"
416
```
417
418
## Annotation Value Resolution
419
420
**AnnotationValueResolver**
421
```java { .api }
422
public interface AnnotationValueResolver {
423
Object resolve(Object value);
424
425
static AnnotationValueResolver none() {
426
return value -> value;
427
}
428
}
429
430
public final class AnnotationConfigUtils {
431
public static final String CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME =
432
"org.springframework.context.annotation.internalConfigurationAnnotationProcessor";
433
434
public static final String AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME =
435
"org.springframework.context.annotation.internalAutowiredAnnotationProcessor";
436
437
public static final String COMMON_ANNOTATION_PROCESSOR_BEAN_NAME =
438
"org.springframework.context.annotation.internalCommonAnnotationProcessor";
439
440
public static final String EVENT_LISTENER_PROCESSOR_BEAN_NAME =
441
"org.springframework.context.event.internalEventListenerProcessor";
442
443
public static final String EVENT_LISTENER_FACTORY_BEAN_NAME =
444
"org.springframework.context.event.internalEventListenerFactory";
445
}
446
```
447
448
## Custom Annotation Processing
449
450
**RepeatableContainers Support**
451
```java { .api }
452
public final class RepeatableContainers {
453
public static RepeatableContainers standardRepeatables();
454
public static RepeatableContainers of(Class<? extends Annotation> container, Class<? extends Annotation> repeatable);
455
public static RepeatableContainers none();
456
457
public RepeatableContainers and(Class<? extends Annotation> container, Class<? extends Annotation> repeatable);
458
459
Annotation[] findRepeatedAnnotations(Annotation annotation);
460
}
461
```
462
463
**Usage Examples**
464
```java
465
// Define repeatable annotation
466
@Target(ElementType.TYPE)
467
@Retention(RetentionPolicy.RUNTIME)
468
@Repeatable(Qualifiers.class)
469
public @interface Qualifier {
470
String value();
471
}
472
473
@Target(ElementType.TYPE)
474
@Retention(RetentionPolicy.RUNTIME)
475
public @interface Qualifiers {
476
Qualifier[] value();
477
}
478
479
// Use repeatable annotations
480
@Qualifier("primary")
481
@Qualifier("default")
482
@Component
483
public class MultiQualifiedComponent { }
484
485
// Process repeatable annotations
486
Class<?> componentClass = MultiQualifiedComponent.class;
487
MergedAnnotations annotations = MergedAnnotations.from(componentClass);
488
489
// Get all instances of repeatable annotation
490
Stream<MergedAnnotation<Qualifier>> qualifiers = annotations.stream(Qualifier.class);
491
List<String> qualifierValues = qualifiers
492
.map(q -> q.getValue("value", String.class).orElse(""))
493
.collect(Collectors.toList());
494
// Result: ["primary", "default"]
495
```
496
497
This comprehensive annotation processing system enables Spring's powerful declarative programming model, allowing developers to use annotations not just as markers but as configuration vehicles with rich attribute processing and composition capabilities.