Как устроены основные моменты при написании аннотаций в Java
Что проверяют
Понимаете ли вы, что аннотация сама ничего не делает: это метаданные, которые работают, только когда их кто-то читает. Следом обычно спрашивают, почему ваша аннотация не видна через рефлексию или почему @Transactional не сработал.
Как отвечать
Объявление идёт через @interface. Элементы выглядят как методы без параметров, типы ограничены: примитивы, String, Class, enum, другие аннотации и одномерные массивы из них. Значение по умолчанию задаётся через default, null запрещён и там, и при использовании. Элемент с именем value позволяет писать @MyAnno("x") без имени.
Дальше мета-аннотации:
@Retention:SOURCE(видит только компилятор, как@Override),CLASS(попадает в байткод, но не в рефлексию, это значение по умолчанию),RUNTIME(доступна черезgetAnnotation());@Target: куда можно повесить,ElementType.TYPE,METHOD,FIELD,PARAMETER, а такжеTYPE_USEдля аннотаций на типах вродеList<@NonNull String>;@Inherited: наследование подклассом, работает только для классов и только от суперкласса, не от интерфейса;@Repeatable: повтор на одном элементе, требует аннотацию-контейнер.
Обрабатывают аннотации двумя способами. В рантайме рефлексией, так работают Spring и Hibernate. На этапе компиляции через процессоры аннотаций (AbstractProcessor), которые генерируют новые исходники: так устроены MapStruct и Dagger. Менять уже существующие классы API процессоров не позволяет, Lombok делает это через внутренности javac.
Хорошо упомянуть составные аннотации Spring: @RestController помечен @Controller и @ResponseBody. Одна аннотация не может расширять другую, мета-аннотации обходит сам Spring.
Где спотыкаются
Забывают @Retention(RetentionPolicy.RUNTIME), и getAnnotation() возвращает null: по умолчанию аннотация в рефлексию не попадает.
Ждут, что аннотация с интерфейса или метода унаследуется реализацией. @Inherited на это не действует, Spring ищет такие аннотации своими утилитами.
Вешают @Transactional или @Cacheable на private-метод либо вызывают метод через this. Прокси Spring такой вызов не перехватывает, и аннотация молча игнорируется.