0
# Annotations and Qualifiers
1
2
Micronaut's annotation system provides comprehensive support for dependency injection, bean scoping, conditional loading, and configuration binding. It combines Jakarta Inject standard annotations with Micronaut-specific enhancements for advanced features.
3
4
## Jakarta Inject Annotations
5
6
### @Inject
7
8
Marks constructors, methods, and fields for dependency injection.
9
10
```java { .api }
11
@Target({METHOD, CONSTRUCTOR, FIELD})
12
@Retention(RUNTIME)
13
public @interface Inject {
14
}
15
```
16
17
**Usage Examples:**
18
19
```java
20
import jakarta.inject.Inject;
21
import jakarta.inject.Singleton;
22
23
@Singleton
24
public class UserService {
25
26
// Constructor injection (preferred)
27
private final UserRepository repository;
28
private final EmailService emailService;
29
30
@Inject
31
public UserService(UserRepository repository, EmailService emailService) {
32
this.repository = repository;
33
this.emailService = emailService;
34
}
35
36
// Field injection
37
@Inject
38
private ValidationService validationService;
39
40
// Method injection
41
@Inject
42
public void setAuditService(AuditService auditService) {
43
this.auditService = auditService;
44
}
45
}
46
```
47
48
### @Singleton
49
50
Marks a class as a singleton bean - only one instance will be created.
51
52
```java { .api }
53
@Target({TYPE, METHOD})
54
@Retention(RUNTIME)
55
@Scope
56
public @interface Singleton {
57
}
58
```
59
60
**Usage Examples:**
61
62
```java
63
import jakarta.inject.Singleton;
64
65
@Singleton
66
public class ConfigurationService {
67
public String getConfigValue(String key) {
68
// Implementation
69
return "value";
70
}
71
}
72
73
// Singleton factory method
74
@Singleton
75
public class ServiceFactory {
76
77
@Singleton
78
public DatabaseService createDatabaseService() {
79
return new DatabaseService("jdbc:postgresql://localhost/db");
80
}
81
}
82
```
83
84
### @Named
85
86
Provides a name qualifier for bean selection when multiple beans of the same type exist.
87
88
```java { .api }
89
@Target({TYPE, METHOD, FIELD, PARAMETER})
90
@Retention(RUNTIME)
91
@Qualifier
92
public @interface Named {
93
String value() default "";
94
}
95
```
96
97
**Usage Examples:**
98
99
```java
100
import jakarta.inject.Named;
101
import jakarta.inject.Singleton;
102
import jakarta.inject.Inject;
103
104
// Named bean definitions
105
@Singleton
106
@Named("mysql")
107
public class MySQLDatabaseService implements DatabaseService {
108
// MySQL implementation
109
}
110
111
@Singleton
112
@Named("postgresql")
113
public class PostgreSQLDatabaseService implements DatabaseService {
114
// PostgreSQL implementation
115
}
116
117
// Named injection
118
@Singleton
119
public class DataProcessor {
120
121
@Inject
122
@Named("mysql")
123
private DatabaseService primaryDb;
124
125
@Inject
126
@Named("postgresql")
127
private DatabaseService backupDb;
128
}
129
```
130
131
## Micronaut Bean Annotations
132
133
### @Bean
134
135
Marks a method or class as a bean definition, typically used in factory classes.
136
137
```java { .api }
138
@Target({METHOD, TYPE, ANNOTATION_TYPE})
139
@Retention(RUNTIME)
140
public @interface Bean {
141
boolean typed() default true;
142
boolean preDestroy() default true;
143
}
144
```
145
146
**Usage Examples:**
147
148
```java
149
import io.micronaut.context.annotation.Bean;
150
import io.micronaut.context.annotation.Factory;
151
import jakarta.inject.Singleton;
152
153
@Factory
154
public class ServiceFactory {
155
156
@Bean
157
@Singleton
158
public HttpClient createHttpClient() {
159
return HttpClient.newBuilder()
160
.connectTimeout(Duration.ofSeconds(10))
161
.build();
162
}
163
164
@Bean
165
public ObjectMapper createObjectMapper() {
166
ObjectMapper mapper = new ObjectMapper();
167
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
168
return mapper;
169
}
170
}
171
```
172
173
### @Factory
174
175
Marks a class as a factory for creating beans, similar to @Configuration in Spring.
176
177
```java { .api }
178
@Target({TYPE})
179
@Retention(RUNTIME)
180
public @interface Factory {
181
}
182
```
183
184
**Usage Examples:**
185
186
```java
187
import io.micronaut.context.annotation.Factory;
188
import io.micronaut.context.annotation.Bean;
189
import jakarta.inject.Singleton;
190
191
@Factory
192
public class DatabaseFactory {
193
194
@Bean
195
@Singleton
196
public DataSource createDataSource() {
197
HikariConfig config = new HikariConfig();
198
config.setJdbcUrl("jdbc:postgresql://localhost/mydb");
199
config.setUsername("user");
200
config.setPassword("password");
201
return new HikariDataSource(config);
202
}
203
204
@Bean
205
@Singleton
206
public JdbcTemplate createJdbcTemplate(DataSource dataSource) {
207
return new JdbcTemplate(dataSource);
208
}
209
}
210
```
211
212
### @Primary
213
214
Marks a bean as primary when multiple beans of the same type exist, making it the default choice.
215
216
```java { .api }
217
@Target({TYPE, METHOD})
218
@Retention(RUNTIME)
219
public @interface Primary {
220
}
221
```
222
223
**Usage Examples:**
224
225
```java
226
import io.micronaut.context.annotation.Primary;
227
import jakarta.inject.Singleton;
228
229
// Multiple implementations
230
@Singleton
231
public class EmailService implements NotificationService {
232
// Email implementation
233
}
234
235
@Singleton
236
@Primary // This will be injected by default
237
public class SmsService implements NotificationService {
238
// SMS implementation
239
}
240
241
@Singleton
242
public class NotificationController {
243
244
@Inject
245
private NotificationService service; // SmsService will be injected due to @Primary
246
}
247
```
248
249
### @Prototype
250
251
Marks a bean as prototype scoped - a new instance is created for each injection.
252
253
```java { .api }
254
@Target({TYPE, METHOD})
255
@Retention(RUNTIME)
256
@Scope
257
public @interface Prototype {
258
}
259
```
260
261
**Usage Examples:**
262
263
```java
264
import io.micronaut.context.annotation.Prototype;
265
266
@Prototype
267
public class RequestHandler {
268
private final String requestId;
269
270
public RequestHandler() {
271
this.requestId = UUID.randomUUID().toString();
272
}
273
274
public void processRequest() {
275
System.out.println("Processing request: " + requestId);
276
}
277
}
278
279
@Singleton
280
public class RequestProcessor {
281
282
@Inject
283
private RequestHandler handler; // New instance each time
284
}
285
```
286
287
## Configuration Annotations
288
289
### @Value
290
291
Injects configuration values using property placeholders.
292
293
```java { .api }
294
@Target({FIELD, PARAMETER, METHOD})
295
@Retention(RUNTIME)
296
public @interface Value {
297
String value();
298
}
299
```
300
301
**Usage Examples:**
302
303
```java
304
import io.micronaut.context.annotation.Value;
305
import jakarta.inject.Singleton;
306
307
@Singleton
308
public class ApiService {
309
310
@Value("${api.base-url}")
311
private String baseUrl;
312
313
@Value("${api.timeout:30}") // Default value of 30
314
private int timeoutSeconds;
315
316
@Value("${api.key}")
317
private String apiKey;
318
319
// Constructor injection with @Value
320
public ApiService(@Value("${api.version:v1}") String apiVersion) {
321
this.apiVersion = apiVersion;
322
}
323
}
324
```
325
326
### @Property
327
328
Injects specific property values with optional defaults.
329
330
```java { .api }
331
@Target({FIELD, PARAMETER, METHOD})
332
@Retention(RUNTIME)
333
public @interface Property {
334
String name();
335
String defaultValue() default "";
336
}
337
```
338
339
**Usage Examples:**
340
341
```java
342
import io.micronaut.context.annotation.Property;
343
import jakarta.inject.Singleton;
344
345
@Singleton
346
public class DatabaseService {
347
348
private final String url;
349
private final String username;
350
private final int maxConnections;
351
352
public DatabaseService(
353
@Property(name = "database.url") String url,
354
@Property(name = "database.username") String username,
355
@Property(name = "database.max-connections", defaultValue = "10") int maxConnections) {
356
357
this.url = url;
358
this.username = username;
359
this.maxConnections = maxConnections;
360
}
361
}
362
```
363
364
### @ConfigurationProperties
365
366
Maps configuration properties to a bean, enabling type-safe configuration.
367
368
```java { .api }
369
@Target({TYPE})
370
@Retention(RUNTIME)
371
public @interface ConfigurationProperties {
372
String value() default "";
373
boolean cliPrefix() default false;
374
}
375
```
376
377
**Usage Examples:**
378
379
```java
380
import io.micronaut.context.annotation.ConfigurationProperties;
381
import jakarta.validation.constraints.NotBlank;
382
import jakarta.validation.constraints.Min;
383
384
@ConfigurationProperties("redis")
385
public class RedisConfiguration {
386
387
@NotBlank
388
private String host = "localhost";
389
390
@Min(1)
391
private int port = 6379;
392
393
private String password;
394
private int database = 0;
395
private int timeout = 2000;
396
397
// Getters and setters
398
public String getHost() { return host; }
399
public void setHost(String host) { this.host = host; }
400
401
public int getPort() { return port; }
402
public void setPort(int port) { this.port = port; }
403
404
public String getPassword() { return password; }
405
public void setPassword(String password) { this.password = password; }
406
407
public int getDatabase() { return database; }
408
public void setDatabase(int database) { this.database = database; }
409
410
public int getTimeout() { return timeout; }
411
public void setTimeout(int timeout) { this.timeout = timeout; }
412
}
413
414
// Usage
415
@Singleton
416
public class RedisService {
417
418
private final RedisConfiguration config;
419
420
@Inject
421
public RedisService(RedisConfiguration config) {
422
this.config = config;
423
}
424
425
public void connect() {
426
System.out.println("Connecting to Redis at " + config.getHost() + ":" + config.getPort());
427
}
428
}
429
```
430
431
## Conditional Annotations
432
433
### @Requires
434
435
Conditionally loads beans based on various criteria.
436
437
```java { .api }
438
@Target({PACKAGE, TYPE, METHOD})
439
@Retention(RUNTIME)
440
@Repeatable(Requirements.class)
441
public @interface Requires {
442
String property() default "";
443
String[] value() default {};
444
String defaultValue() default "";
445
Class<?>[] beans() default {};
446
Class<?>[] missingBeans() default {};
447
String[] env() default {};
448
String[] notEnv() default {};
449
Class<? extends Condition>[] condition() default {};
450
String os() default "";
451
String[] notOs() default {};
452
String sdk() default "";
453
String[] classes() default {};
454
String[] missingClasses() default {};
455
String[] resources() default {};
456
String[] missingProperties() default {};
457
}
458
```
459
460
**Usage Examples:**
461
462
```java
463
import io.micronaut.context.annotation.Requires;
464
import jakarta.inject.Singleton;
465
466
// Conditional on property value
467
@Singleton
468
@Requires(property = "cache.enabled", value = "true")
469
public class CacheService {
470
// Only created if cache.enabled=true
471
}
472
473
// Conditional on environment
474
@Singleton
475
@Requires(env = "prod")
476
public class ProductionDatabaseService implements DatabaseService {
477
// Only loaded in production environment
478
}
479
480
// Conditional on missing bean
481
@Singleton
482
@Requires(missingBeans = DatabaseService.class)
483
public class DefaultDatabaseService implements DatabaseService {
484
// Only created if no other DatabaseService exists
485
}
486
487
// Conditional on class presence
488
@Singleton
489
@Requires(classes = "org.springframework.data.redis.core.RedisTemplate")
490
public class RedisIntegrationService {
491
// Only created if Redis classes are on classpath
492
}
493
494
// Multiple conditions
495
@Singleton
496
@Requires(property = "features.analytics", value = "true")
497
@Requires(env = {"prod", "staging"})
498
@Requires(beans = DatabaseService.class)
499
public class AnalyticsService {
500
// Created only if all conditions are met
501
}
502
```
503
504
### @Requirements
505
506
Groups multiple @Requires conditions.
507
508
```java { .api }
509
@Target({PACKAGE, TYPE, METHOD})
510
@Retention(RUNTIME)
511
public @interface Requirements {
512
Requires[] value();
513
}
514
```
515
516
**Usage Examples:**
517
518
```java
519
import io.micronaut.context.annotation.Requirements;
520
import io.micronaut.context.annotation.Requires;
521
522
@Singleton
523
@Requirements({
524
@Requires(property = "email.enabled", value = "true"),
525
@Requires(classes = "javax.mail.internet.MimeMessage"),
526
@Requires(beans = EmailConfiguration.class)
527
})
528
public class EmailService {
529
// Created only if all requirements are satisfied
530
}
531
```
532
533
## Qualifier Annotations
534
535
### Custom Qualifiers
536
537
```java
538
import io.micronaut.context.annotation.Qualifier;
539
import java.lang.annotation.Retention;
540
import java.lang.annotation.RetentionPolicy;
541
542
@Qualifier
543
@Retention(RetentionPolicy.RUNTIME)
544
public @interface Database {
545
DatabaseType value();
546
}
547
548
public enum DatabaseType {
549
MYSQL, POSTGRESQL, H2
550
}
551
552
// Usage
553
@Singleton
554
@Database(DatabaseType.MYSQL)
555
public class MySQLService implements DatabaseService {
556
// MySQL implementation
557
}
558
559
@Singleton
560
@Database(DatabaseType.POSTGRESQL)
561
public class PostgreSQLService implements DatabaseService {
562
// PostgreSQL implementation
563
}
564
565
@Singleton
566
public class DataService {
567
568
@Inject
569
@Database(DatabaseType.MYSQL)
570
private DatabaseService mysqlService;
571
572
@Inject
573
@Database(DatabaseType.POSTGRESQL)
574
private DatabaseService postgresService;
575
}
576
```
577
578
## Lifecycle Annotations
579
580
### @PostConstruct
581
582
Marks methods to be called after bean construction and dependency injection.
583
584
```java { .api }
585
@Target(METHOD)
586
@Retention(RUNTIME)
587
public @interface PostConstruct {
588
}
589
```
590
591
### @PreDestroy
592
593
Marks methods to be called before bean destruction.
594
595
```java { .api }
596
@Target(METHOD)
597
@Retention(RUNTIME)
598
public @interface PreDestroy {
599
}
600
```
601
602
**Usage Examples:**
603
604
```java
605
import jakarta.annotation.PostConstruct;
606
import jakarta.annotation.PreDestroy;
607
import jakarta.inject.Singleton;
608
609
@Singleton
610
public class DatabaseConnectionManager {
611
612
private Connection connection;
613
614
@PostConstruct
615
public void initialize() {
616
System.out.println("Initializing database connection...");
617
this.connection = DriverManager.getConnection("jdbc:h2:mem:test");
618
}
619
620
@PreDestroy
621
public void cleanup() {
622
System.out.println("Closing database connection...");
623
try {
624
if (connection != null && !connection.isClosed()) {
625
connection.close();
626
}
627
} catch (SQLException e) {
628
System.err.println("Error closing connection: " + e.getMessage());
629
}
630
}
631
632
public void executeQuery(String sql) {
633
// Use connection
634
}
635
}
636
```
637
638
## Scope Annotations
639
640
### Custom Scope
641
642
```java
643
import io.micronaut.context.scope.CustomScope;
644
import java.lang.annotation.Retention;
645
import java.lang.annotation.RetentionPolicy;
646
647
@CustomScope
648
@Retention(RetentionPolicy.RUNTIME)
649
public @interface RequestScope {
650
}
651
652
// Implementation of the custom scope
653
@Singleton
654
public class RequestScopeImpl implements CustomScope<RequestScope> {
655
656
private final Map<String, Object> requestBeans = new ConcurrentHashMap<>();
657
658
@Override
659
public Class<RequestScope> annotationType() {
660
return RequestScope.class;
661
}
662
663
@Override
664
public <T> T get(BeanCreationContext<T> creationContext) {
665
String key = creationContext.id().toString();
666
return (T) requestBeans.computeIfAbsent(key, k ->
667
creationContext.create()
668
);
669
}
670
671
public void clearScope() {
672
requestBeans.clear();
673
}
674
}
675
676
// Usage
677
@RequestScope
678
public class RequestDataHolder {
679
private Map<String, Object> data = new HashMap<>();
680
681
public void setData(String key, Object value) {
682
data.put(key, value);
683
}
684
685
public Object getData(String key) {
686
return data.get(key);
687
}
688
}
689
```
690
691
## Qualifier Utility Classes
692
693
### Qualifiers
694
695
Utility class for creating qualifiers programmatically.
696
697
```java { .api }
698
public final class Qualifiers {
699
public static <T> Qualifier<T> byName(String name);
700
public static <T> Qualifier<T> byType(Class<?>... types);
701
public static <T> Qualifier<T> byAnnotation(Annotation annotation);
702
public static <T> Qualifier<T> byTypeArguments(Class<?>... types);
703
public static <T> Qualifier<T> byTypeArgumentsClosest(Class<?>... types);
704
}
705
```
706
707
**Usage Examples:**
708
709
```java
710
import io.micronaut.context.ApplicationContext;
711
import io.micronaut.context.Qualifier;
712
import io.micronaut.inject.qualifiers.Qualifiers;
713
714
public class QualifierExample {
715
public static void main(String[] args) {
716
try (ApplicationContext context = ApplicationContext.run()) {
717
// Get bean by name qualifier
718
Qualifier<DatabaseService> nameQualifier = Qualifiers.byName("mysql");
719
DatabaseService mysqlService = context.getBean(DatabaseService.class, nameQualifier);
720
721
// Get bean by annotation qualifier
722
Database databaseAnnotation = () -> DatabaseType.POSTGRESQL;
723
Qualifier<DatabaseService> annotationQualifier = Qualifiers.byAnnotation(databaseAnnotation);
724
DatabaseService postgresService = context.getBean(DatabaseService.class, annotationQualifier);
725
}
726
}
727
}
728
```
729
730
## Advanced Configuration Annotations
731
732
### @EachBean
733
734
Creates a bean for each bean of the specified type that exists in the context.
735
736
```java { .api }
737
@Target({TYPE})
738
@Retention(RUNTIME)
739
@Documented
740
public @interface EachBean {
741
/**
742
* The bean type to iterate over
743
*/
744
Class<?> value();
745
}
746
```
747
748
**Usage Examples:**
749
750
```java
751
import io.micronaut.context.annotation.EachBean;
752
753
@EachBean(DataSource.class)
754
@Singleton
755
public class DatabaseService {
756
757
private final DataSource dataSource;
758
759
public DatabaseService(DataSource dataSource) {
760
this.dataSource = dataSource;
761
}
762
763
public Connection getConnection() {
764
return dataSource.getConnection();
765
}
766
}
767
768
// If there are 3 DataSource beans, 3 DatabaseService beans will be created
769
```
770
771
### @EachProperty
772
773
Creates a bean for each property prefix that exists in configuration.
774
775
```java { .api }
776
@Target({TYPE})
777
@Retention(RUNTIME)
778
@Documented
779
public @interface EachProperty {
780
/**
781
* The property prefix to iterate over
782
*/
783
String value();
784
785
/**
786
* Primary bean marker
787
*/
788
boolean primary() default false;
789
}
790
```
791
792
**Usage Examples:**
793
794
```java
795
import io.micronaut.context.annotation.EachProperty;
796
import io.micronaut.context.annotation.ConfigurationProperties;
797
798
@EachProperty("datasources")
799
@ConfigurationProperties("datasources")
800
public class DataSourceConfig {
801
private String url;
802
private String username;
803
private String password;
804
805
// getters and setters
806
}
807
808
// Configuration:
809
// datasources.primary.url=jdbc:h2:mem:primary
810
// datasources.secondary.url=jdbc:h2:mem:secondary
811
// Creates DataSourceConfig beans for "primary" and "secondary"
812
```
813
814
### @ConfigurationBuilder
815
816
Enables configuration of complex objects through builder pattern integration.
817
818
```java { .api }
819
@Target({FIELD, METHOD, PARAMETER})
820
@Retention(RUNTIME)
821
@Documented
822
public @interface ConfigurationBuilder {
823
/**
824
* Property prefix for configuration values
825
*/
826
String value() default "";
827
828
/**
829
* Configuration prefix for nested configuration
830
*/
831
String configurationPrefix() default "";
832
833
/**
834
* Method prefixes to include
835
*/
836
String[] includes() default {};
837
838
/**
839
* Method prefixes to exclude
840
*/
841
String[] excludes() default {};
842
843
/**
844
* Whether to allow zero args methods
845
*/
846
boolean allowZeroArgs() default false;
847
}
848
```
849
850
**Usage Examples:**
851
852
```java
853
import io.micronaut.context.annotation.ConfigurationBuilder;
854
import io.micronaut.context.annotation.ConfigurationProperties;
855
856
@ConfigurationProperties("redis")
857
public class RedisConfig {
858
859
@ConfigurationBuilder(configurationPrefix = "jedis")
860
private final JedisPoolConfig jedisConfig = new JedisPoolConfig();
861
862
@ConfigurationBuilder(configurationPrefix = "lettuce",
863
includes = {"timeout", "database"})
864
private final LettuceConnectionFactory.Builder lettuceBuilder =
865
LettuceConnectionFactory.builder();
866
867
public JedisPoolConfig getJedisConfig() {
868
return jedisConfig;
869
}
870
871
public LettuceConnectionFactory.Builder getLettuceBuilder() {
872
return lettuceBuilder;
873
}
874
}
875
876
// Configuration:
877
// redis.jedis.max-total=20
878
// redis.jedis.max-idle=10
879
// redis.lettuce.timeout=5000
880
// redis.lettuce.database=0
881
```
882
883
### @Executable
884
885
Marks methods as executable for reflection-free method invocation.
886
887
```java { .api }
888
@Target({METHOD})
889
@Retention(RUNTIME)
890
@Documented
891
public @interface Executable {
892
/**
893
* Whether the method can be invoked when reflection is not available
894
*/
895
boolean processOnStartup() default false;
896
}
897
```
898
899
**Usage Examples:**
900
901
```java
902
import io.micronaut.context.annotation.Executable;
903
904
@Singleton
905
public class BusinessService {
906
907
@Executable
908
public String processData(String input) {
909
return "Processed: " + input;
910
}
911
912
@Executable(processOnStartup = true)
913
public void initialize() {
914
System.out.println("Service initialized");
915
}
916
917
// Non-executable method - won't be optimized
918
private void internalMethod() {
919
// Internal logic
920
}
921
}
922
```
923
924
### @Mapper
925
926
Enables automatic bean mapping functionality.
927
928
```java { .api }
929
@Target({TYPE})
930
@Retention(RUNTIME)
931
@Documented
932
public @interface Mapper {
933
/**
934
* The mapping strategy
935
*/
936
Strategy strategy() default Strategy.DEFAULT;
937
938
/**
939
* Configuration for the mapper
940
*/
941
String config() default "";
942
943
enum Strategy {
944
DEFAULT, CONSTRUCTOR, SETTER, FIELD
945
}
946
}
947
```
948
949
**Usage Examples:**
950
951
```java
952
import io.micronaut.context.annotation.Mapper;
953
954
@Mapper
955
public interface UserMapper {
956
957
UserDto toDto(User user);
958
User fromDto(UserDto dto);
959
960
@Mapping(target = "fullName", source = "firstName,lastName")
961
UserSummary toSummary(User user);
962
}
963
964
// Micronaut generates implementation at compile time
965
```
966
967
## Conditional and Validation Annotations
968
969
### @Any
970
971
Qualifier that matches any bean of the specified type.
972
973
```java { .api }
974
@Target({FIELD, PARAMETER, METHOD, TYPE})
975
@Retention(RUNTIME)
976
@Qualifier
977
public @interface Any {
978
}
979
```
980
981
### @Secondary
982
983
Marks a bean as secondary - used when @Primary is not available.
984
985
```java { .api }
986
@Target({TYPE, METHOD})
987
@Retention(RUNTIME)
988
public @interface Secondary {
989
}
990
```
991
992
**Usage Examples:**
993
994
```java
995
import io.micronaut.context.annotation.Secondary;
996
997
@Singleton
998
@Primary
999
public class PrimaryEmailService implements EmailService {
1000
// Primary implementation
1001
}
1002
1003
@Singleton
1004
@Secondary
1005
public class BackupEmailService implements EmailService {
1006
// Secondary implementation - used if primary fails
1007
}
1008
```
1009
1010
## Types
1011
1012
### Annotation Metadata Types
1013
1014
```java { .api }
1015
public interface AnnotationMetadata {
1016
<T extends Annotation> Optional<T> getAnnotation(Class<T> annotationClass);
1017
boolean hasAnnotation(Class<? extends Annotation> annotation);
1018
boolean hasStereotype(Class<? extends Annotation> stereotype);
1019
Set<String> getAnnotationNames();
1020
Set<String> getDeclaredAnnotationNames();
1021
OptionalValues<Object> getValues(String annotation);
1022
<T> Optional<T> getValue(String annotation, Class<T> requiredType);
1023
String[] stringValues(Class<? extends Annotation> annotation);
1024
String[] stringValues(String annotation, String member);
1025
}
1026
}
1027
```