Что такое Retention Policy в Java аннотациях?
Что проверяют
Понимаете ли вы, что аннотация может исчезнуть на разных этапах: после компиляции или при загрузке класса. Следом обычно спрашивают, почему ваша собственная аннотация не находится через рефлексию и какая политика действует по умолчанию.
Как отвечать
Политику задаёт мета-аннотация @Retention со значением из перечисления RetentionPolicy:
- SOURCE: аннотация живёт только в исходнике, компилятор её отбрасывает. Нужна самому компилятору и процессорам аннотаций. Примеры:
@Override,@SuppressWarnings, аннотации Lombok вроде@Getter; - CLASS: попадает в
.class-файл, но JVM не отдаёт её через рефлексию. Это значение по умолчанию, если@Retentionне указан. Такие аннотации читают инструменты, которые работают с байт-кодом: ASM, ProGuard, SpotBugs; - RUNTIME: сохраняется в байт-коде и доступна через
getAnnotation()иisAnnotationPresent(). На ней держатся Spring (@Transactional,@Autowired), JPA (@Entity), JUnit (@Test), Jackson.
Добавьте, что @Retention почти всегда идёт в паре с @Target, который ограничивает место применения: тип, метод, поле, параметр. Своей аннотации, которую будет читать фреймворк или ваш аспект, нужен RUNTIME. Если она служит только сигналом для вашего генератора кода, хватает SOURCE: процессор видит её на этапе компиляции, а в байт-код ничего лишнего не попадёт.
Хороший короткий пример: @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface Audit {}, а затем method.getAnnotation(Audit.class) внутри аспекта, который пишет журнал вызовов.
Где спотыкаются
Забывают указать @Retention у своей аннотации и потом удивляются, что getAnnotation() возвращает null. По умолчанию стоит CLASS, и до рантайма такая аннотация не доезжает.
Относят @SuppressWarnings к CLASS. На самом деле у неё SOURCE: предупреждения нужны только компилятору.
Думают, что annotation processor требует RUNTIME. Процессоры работают с исходниками во время компиляции и видят аннотации с любой политикой.