Ответ
Аннотация @Retention определяет, на каком этапе жизненного цикла кода аннотация будет доступна. В Java существует три политики сохранения, задаваемые перечислением RetentionPolicy:
| Политика | Доступность | Типичное использование |
|---|---|---|
SOURCE |
Только в исходном коде. Удаляется компилятором и отсутствует в .class файле. |
@Override, @SuppressWarnings — для проверок на этапе компиляции. |
CLASS (по умолчанию) |
Сохраняется в байт-коде (.class файл), но недоступна во время выполнения (JVM). |
Может использоваться инструментами для пост-обработки байт-кода (например, инструменты анализа). |
RUNTIME |
Сохраняется в байт-коде и доступна во время выполнения через Reflection API. | Аннотации для фреймворков (@Test в JUnit, @Controller в Spring), сериализации, кастомной логики. |
Почему это важно? Выбор политики влияет на поведение программы и инструментов. Например, аннотации для Dependency Injection (как в Spring) должны быть RUNTIME, чтобы контейнер мог их прочитать и создать бины.
Пример объявления аннотации с политикой RUNTIME:
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME) // Ключевая строка
public @interface LogExecution {
String value() default "";
}
// Использование
@LogExecution("Service method")
public void processOrder() { /* ... */ } Ответ 18+ 🔞
Давай разберём эту аннотацию @Retention, а то народ часто путается, как она работает, и потом удивляется, почему его кастомная аннотация нихуя не отрабатывает в рантайме.
Вот смотри, есть три варианта, куда эта штука может засунуться и где её потом искать. Как будто ты оставляешь записку, но пишешь на ней: «Прочитать и выкинуть», «Приколоть к доске, но не заморачиваться» или «Вшить в татуировку и носить с собой всегда».
1. RetentionPolicy.SOURCE
Это как черновик, который ты кидаешь в урну сразу после написания. Аннотация живёт только в исходниках (.java файл). Компилятор её видит, делает что надо (например, ругается или не ругается, как @Override), и на этом всё — в скомпилированный байт-код она не попадает. Чисто для статических проверок, чтобы твои кривые руки не накосячили.
2. RetentionPolicy.CLASS (дефолтная, между прочим!)
Вот тут уже интереснее. Аннотация попадает в .class файл, но когда программа уже запустилась (JVM её крутит) — её там нет, как бы. Она как призрак. Зачем? Ну, для всяких посторонних инструментов, которые байт-код анализируют, но не в рантайме. Например, какие-нибудь анализаторы кода или генераторы прокси на этапе компиляции. Обычному разработчику на неё почти всегда похуй, если только ты не пишешь какой-нибудь хитрый плагин для мавен или градла.
3. RetentionPolicy.RUNTIME
А вот это, блядь, наш вариант! Аннотация лежит в .class файле и доступна во время выполнения программы через этот ваш Reflection API. Это прям must have для всех фреймворков, которые работают в рантайме: Spring (@Autowired, @Service), JUnit (@Test), Hibernate (@Entity) — все эти ребята в момент запуска лезут в классы, ищут эти аннотации и делают с ними что хотят. Без RUNTIME они просто нихуя не увидят.
Короче, запомни:
- Хочешь, чтобы аннотацию видел только компилятор —
SOURCE. - Хочешь, чтобы её видели инструменты, работающие с байт-кодом, но не JVM —
CLASS(или просто не указывай, это и так дефолт). - Хочешь, чтобы её можно было прочитать во время работы программы через рефлексию — только
RUNTIME, другого не дано.
Вот пример, как объявить аннотацию, которая не сдохнет после компиляции:
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
// Вот эта строчка — ключевая. Без неё вся магия рассосётся.
@Retention(RetentionPolicy.RUNTIME)
public @interface LogExecution {
String value() default "";
}
// А теперь используем её так, чтобы фреймворк или наш хендлер могли её найти.
@LogExecution("Service method")
public void processOrder() {
// ... какой-то код, который, возможно, будет логироваться
}
Если тут указать не RUNTIME, а, скажем, SOURCE, то в рантайме метод getAnnotation(LogExecution.class) вернёт тебе null, и ты будешь чесать репу, думая, что ты криворукий. А ты просто не ту политику указал, вот и вся ебалова история.