Как создавать пользовательские аннотации в Spring Boot

Как создавать пользовательские аннотации в Spring Boot

3 сентября 2024 г.

Подобные аннотации заполняют весь проект в Spring Boot.

Но знаете ли вы, какие проблемы решают эти аннотации?

Почему изначально были введены пользовательские аннотации?

Как создавать пользовательские аннотации?

Сегодня я расскажу:

  • Зачем создавать пользовательские аннотации?
  • Каковы основные преимущества использования этих аннотаций?
  • Как создавать пользовательские аннотации?
  • Как вызывается аннотированный метод?
  • Когда следует использовать пользовательские аннотации?
  • Когда не следует использовать пользовательские аннотации?
  • Каковы недостатки использования пользовательских аннотаций?

🎯 Зачем создавать пользовательские аннотации?

В Spring Boot аннотации — это не просто способ добавления метаданных. Они

  • Упростите сложные задачи
  • Уменьшение количества шаблонного кода
  • Улучшение читаемости кода

До того, как в Spring появились пользовательские аннотации, разработчикам приходилось управлять конфигурациями, такими как проверка электронной почты, с помощью XML-файлов конфигурации.

Конфигурация XML будет определять компоненты, валидаторы и другие необходимые компоненты для выполнения таких задач, как проверка адресов электронной почты.

Вот пример того, как можно настроить проверку электронной почты с использованием XML в приложении Spring:

Как видите, это может легко превратиться в кошмар, если есть сотни классов, многие из которых зависят друг от друга.

Это также означало, что разработчику приходилось искать этот XML-код каждый раз, когда ему нужно было добавить новую зависимость.

Основные преимущества пользовательских аннотаций

Упрощение конфигурации

Spring представил пользовательские аннотации для упрощения настройки, позволяя разработчикам использовать аннотации непосредственно в своем коде.

Это уменьшило необходимость в обширной настройке XML, сделав кодовую базу более чистой и простой в обслуживании.

Поддержка декларативного программирования

Пользовательские аннотации в Spring позволяют использовать декларативный подход. Разработчики могут использовать аннотации, такие как@Transactional, @Cacheable, или@Scheduledдекларировать желаемое поведение без прописывания базовой логики.

Это приводит к более читаемому и поддерживаемому коду.

Решение сквозных проблем

Пользовательские аннотации Spring, часто используемые с аспектно-ориентированным программированием (АОП), позволяют разработчикам централизованно обрабатывать сквозные задачи.

Например,@Transactionalаннотация управляет транзакциями между несколькими методами или классами, не распыляя логику управления транзакциями по всему коду.

Сокращение шаблонного кода

Это снижает необходимость в шаблонном коде за счет инкапсуляции общих поведений.

Например,@Autowiredаннотация упрощает внедрение зависимостей, позволяя Spring автоматически внедрять зависимости, не требуя явных методов конструктора или сеттера

Это другой разговор, стоит ли вам использовать@Autowiredили нет.

Улучшение читаемости и согласованности кода

Благодаря абстрагированию конфигурации и сквозных задач в аннотации Spring улучшает читаемость кода.

Вы и ваши коллеги-разработчики можете быстро понять назначение метода или класса, просмотрев его аннотации, а аннотации помогают обеспечить согласованность во всей кодовой базе.

Гибкость и расширяемость фреймворка

Пользовательские аннотации позволяют разработчикам создавать свои аннотации, соответствующие конкретным потребностям, тем самым стандартизированным образом расширяя функциональность фреймворка.

Такая гибкость помогла Spring оставаться актуальным и эффективным средством для различных приложений и архитектур.

🚀 Как создать пользовательскую аннотацию

Шаг 1: Определите аннотацию

  • Создайте новую аннотацию, определив интерфейс.

  • Использовать@interfaceчтобы объявить об этом.

  • Добавьте метааннотации, чтобы указать, как должна себя вести аннотация.

    JAVA
    package co.officegeek.tokenratelimiter;
    
    import java.lang.annotation.ElementType; 
    import java.lang.annotation.Retention; 
    import java.lang.annotation.RetentionPolicy; 
    import java.lang.annotation.Target;
    
    @Retention(RetentionPolicy.RUNTIME)  // Annotation available at runtime 
    @Target(ElementType.METHOD)          // Can be applied to methods 
    
    public @interface LogExecutionTime { 
    
    }
    

  • @Target: Указывает, где можно использовать аннотацию (например, методы, классы).

  • @Retention: Указывает, как долго сохраняется аннотация (например, во время выполнения, во время компиляции).

Шаг 2: Создайте аспект для обработки аннотации

Вы можете создать собственную логику для обработки аннотаций с помощью SpringBeanPostProcessor, Aspectили пользовательская логика обработки аннотаций.

package co.officegeek.tokenratelimiter;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class LogExecutionTimeAspect {

    @Around("@annotation(LogExecutionTime)")
    public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
        long start = System.currentTimeMillis();
        Object proceed = joinPoint.proceed();
        long executionTime = System.currentTimeMillis() - start;

        System.out.println(joinPoint.getSignature() + " executed in " + executionTime + "ms");
        return proceed;
    }
}

Шаг 3: Примените аннотацию

Примените пользовательскую аннотацию к методам, полям или классам, как определено.

package co.officegeek.tokenratelimiter;

import org.springframework.stereotype.Service;

@Service
public class TestService {

    @LogExecutionTime
    public void serve() throws InterruptedException {
        // Simulate some work
        Thread.sleep(2000);
    }
}

Как это работает:

  • The@LogExecutionTimeаннотация не приводит к непосредственному вызову какого-либо метода.
  • Фреймворк Spring AOP определяет, что метод имеет@LogExecutionTimeаннотация с использованием отражения.
  • Аспект LogExecutionTimeAspect настроен на применение рекомендаций при вызове метода с аннотацией @LogExecutionTime.
  • ThelogExecutionTimeметод в аспекте выполняется до и после аннотированного метода (serve), регистрируя время выполнения.


Как вызывается аннотированный метод?

Когда вы применяете пользовательскую аннотацию к методу, классу или полю, сама аннотация не вызывает напрямую вызов какого-либо метода. Вместо этого логика, связанная с аннотацией, обычно реализуется с использованием рефлексии или аспектно-ориентированного программирования (AOP) в таких фреймворках, как Spring.

Ниже приведено описание того, как компилятор и среда выполнения узнают, какой метод вызывать при применении аннотации:

1. Обработка во время компиляции (процессоры аннотаций)

Некоторые аннотации обрабатываются во время компиляции процессорами аннотаций.javax.annotation.processingПакет позволяет разработчикам создавать собственные процессоры аннотаций, которые генерируют код, проверяют аннотации или даже изменяют абстрактное синтаксическое дерево (AST) компилируемого кода.

Процессор аннотаций считывает аннотации во время компиляции и выполняет код на основе этих аннотаций.

Это может включать в себя создание новых классов или методов, которые код будет использовать позже.

The@OverrideАннотация — это аннотация времени компиляции, которая не вызывает метод, а вместо этого сообщает компилятору о необходимости проверить, действительно ли метод переопределяет метод суперкласса.

Как это работает:

  • Пользовательский процессор аннотаций определяется путем расширения AbstractProcessor и переопределения метода process.
  • Компилятор вызовет процессор, когда встретит вашу аннотацию, что позволит вам сгенерировать код или выполнить другие задачи.

2. Обработка во время выполнения (рефлексия)

Пользовательские аннотации могут обрабатываться во время выполнения с помощью отражения. Система выполнения (например, фреймворк вроде Spring) использует отражение для обнаружения наличия аннотаций в методах, классах или полях, а затем применяет соответствующее поведение.

Пользовательская аннотация, например@LogExecutionTimeне вызывает напрямую какой-либо вызов метода.

Вместо этого аспект или какой-либо другой рефлексивный механизм проверяет наличие аннотации во время выполнения, а затем оборачивает вызов метода дополнительной логикой.

Как это работает:

  • Во время выполнения вы используете API рефлексии Java, чтобы проверить, имеет ли метод или класс определенную аннотацию, используя такие методы, какisAnnotationPresent.
  • После обнаружения вы можете вызывать методы или выполнять логику, связанную с этой аннотацией. Например, если метод имеет аннотацию @LogExecutionTime, вы можете измерить время до и после вызова метода.

3. Аспектно-ориентированное программирование (АОП)

В таких фреймворках, как Spring, AOP обычно используется для обработки пользовательских аннотаций. AOP позволяет вам определять «аспекты», которые могут перехватывать вызовы методов и выполнять дополнительную обработку до или после выполнения метода. Когда фреймворк AOP (например, Spring AOP) обнаруживает аннотацию, он запускает выполнение метода advice, связанного с аспектом.

Этот метод-совет содержит логику, которую выполняет фреймворк АОП при вызове аннотированного метода.

А@Transactionalаннотация в Spring сама по себе не выполняет никакой логики. Вместо этого инфраструктура АОП фреймворка Spring перехватывает вызовы методов, аннотированных @Transactional, и оборачивает их логикой управления транзакциями.

Как это работает:

  • Вы определяете класс аспекта с методами рекомендаций, которые связаны с конкретными точками среза (точками соединения, к которым вы хотите применить рекомендацию).
  • Аспект использует аннотации, такие как @Around или @Before, чтобы указать, когда следует выполнить совет.
  • Фреймворк АОП гарантирует, что при вызове метода с пользовательской аннотацией соответствующая рекомендация выполняется автоматически.

Случаи, когда пользовательские аннотации являются хорошим подходом

Межсекторальные проблемы

Пользовательские аннотации идеально подходят для обработки сквозных проблем, таких как ведение журнала, безопасность, управление транзакциями и кэширование. Это проблемы, которые затрагивают несколько частей приложения, но не связаны с основной бизнес-логикой.

The@LogExecutionTimeПриведенная выше аннотация является хорошим примером, поскольку ее можно использовать во всех методах, и она не содержит никакой бизнес-логики.

Декларативное программирование

Если вы хотите указать, что должно произойти, а не как это должно произойти, пользовательские аннотации предоставляют понятный и выразительный способ сделать это.

Аннотации вроде@Cacheableили@Retryпозволяют разработчикам декларативно включать кэширование или логику повтора, не написав код реализации вручную.

Интеграция фреймворка или библиотеки

Пользовательские аннотации могут упростить интеграцию фреймворков или библиотек, скрывая сложность за простой в использовании аннотацией.

Аннотации вроде@Autowiredв Spring помогают внедрять зависимости без необходимости вручную создавать их экземпляры.

Инкапсуляция сложной логики

Когда сложную логику необходимо инкапсулировать для повторного использования, пользовательские аннотации могут предоставить понятный API для применения этой логики.

Аннотация вроде@RateLimitможно инкапсулировать логику, чтобы ограничить количество вызовов метода, не загромождая тело метода этой логикой.

Случаи, когда не следует использовать пользовательские аннотации

Простая или одноразовая логика

Если логика проста или должна быть применена только в одном месте, создание пользовательской аннотации является излишним и может неоправданно усложнить код.

Логика, требующая динамического поведения

Аннотации статически определяются во время компиляции, что делает их непригодными для сценариев, где поведение необходимо динамически определять во время выполнения.

Если поведение метода должно меняться в зависимости от ввода данных пользователем или внешней конфигурации, обработка этого с помощью пользовательских аннотаций может привести к сложным решениям.

Бизнес-логика

Основная бизнес-логика не должна абстрагироваться в пользовательских аннотациях, так как это может сделать логику менее прозрачной и затруднить ее поддержку.

Использование аннотации для инкапсуляции бизнес-процесса, например@ProcessOrderмогут скрывать важные бизнес-правила, что усложняет понимание и поддержку кода.

Сложные взаимодействия между аннотациями

Если поведение зависит от сложных взаимодействий между несколькими аннотациями, это может привести к неожиданным результатам и затруднить понимание и отладку кода.

Объединение нескольких пользовательских аннотаций, которые влияют на один и тот же метод (например,@Retry, @Cacheable, @LogExecutionTime) может привести к непредсказуемому поведению и им трудно управлять

Код, критически важный для производительности

Пользовательские аннотации часто полагаются на механизмы отражения или прокси, которые могут привести к снижению производительности. Их не следует использовать в критических для производительности разделах кода.

Использование пользовательской аннотации для добавления журналирования к методу, который вызывается миллионы раз в тесном цикле, может значительно снизить производительность.

💡 Резюме - Когда использовать пользовательские аннотации

Пользовательские аннотации идеально подходят для обработки сквозных проблем, таких как ведение журнала, безопасность и управление транзакциями. Они также отлично подходят для сценариев, где вам нужно применить одно и то же поведение в нескольких частях вашего приложения.

Однако для простой, одноразовой логики или там, где требуется детальный контроль и гибкость, пользовательские аннотации могут оказаться не лучшим подходом.

Прежде чем принять решение о внедрении этих решений, подумайте о возможных компромиссах.

🌟 Заключительные мысли

Пользовательские аннотации — мощный инструмент в вашем арсенале Spring Boot, но, как и любой инструмент, их следует использовать разумно. Они предлагают чистый, многократно используемый способ обработки повторяющихся задач и обеспечения согласованности в вашей кодовой базе.

Но помните о возможных недостатках, особенно касающихся сложности и производительности.


📣📣 Объявление

Я запускаю 10-дневный групповой курс для разработчиков программного обеспечения и начинающих архитекторов микросервисов по проектированию и внедрению сервисов с ограничением скорости с использованием Spring Boot и Bucket4j.

Вы узнаете:

✅ Как спроектировать и создать готовый к использованию микросервис

✅ Глубокие знания алгоритмов ограничения скорости и их реализации

✅ Лучшие практики разработки, тестирования и контейнеризации Spring Boot

Но это также о

✅ разбиение проекта на конкретные задачи

✅ Быть ответственным перед собой

✅ Правильное проектирование и реализация проекта

Он ориентирован на разработчиков программного обеспечения, желающих спроектировать и разработать микросервис, что является актуальным вариантом использования для большинства компаний.

Это ОСОБЕННО для тех, кто находится на ранней стадии своей карьеры разработчика программного обеспечения и у кого может не быть «опыта работы над проектами», но есть масса страсти и амбиций.

Если вы думаете, что это вам поможет или вам просто интересно узнать больше:

Зарегистрируйте свою заинтересованность, и я сообщу вам подробности семинара.


Вы разработчик, которому нужна обратная связь по написанному вами коду?

Или вы хотите, чтобы кто-то проверил ваш код, чтобы убедиться, что вы делаете все правильно?

Я помогаю людям проводить бесплатные сеансы обзора кода, чтобы они могли получить раннюю обратную связь и писать более качественный код.

Напишите мне в личкуТвиттер (Х)или наLinkedInи я помогу вам с вашим кодом.


Оригинал
PREVIOUS ARTICLE
NEXT ARTICLE