Расширьте функциональность класса с помощью процессора аннотаций во время компиляции

У меня есть следующий класс Spring Boot с пользовательской аннотацией Counted:

@RestController
@RequestMapping("/identity")
public class IdentityController {

    @Autowired
    private IdentityService identityService;

    @PostMapping
    @Counted(value = "post_requests_identity")
    public Integer createIdentity() {
        return identityService.createIdentity();
    }
}

Обозначение Counted определяется следующим образом:

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.SOURCE)
public @interface Counted {
    String value();
}

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

@RestController
@RequestMapping("/identity")
public class IdentityController {

    @Autowired
    private IdentityService identityService;

    @Autowired
    private PrometheusMeterRegistry registry;

    @PostConstruct
    public void init() {
        registry.counter("post_requests_identity");
    }

    @PostMapping
    public Integer createIdentity() {
        registry.counter("post_requests_identity").increment();
        return identityService.createIdentity();
    }
}

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

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

Что вам нужно, это АОП, а не обработка аннотаций

Dean Xu 24.12.2018 23:54

@DeanXu прав. в Java EE можно использовать Перехватчики. Если вы действительно настаиваете на изменении существующих источников во время компиляции, взгляните на Проект Ломбок. Они используют обработчик аннотаций в качестве точки входа и изменяют AST с помощью некоторого недокументированного API компилятора.

Turing85 25.12.2018 00:03

Я пробовал этот подход, но не нашел хорошего способа инициализировать свои счетчики с помощью Spring AOP API. В этом случае я использовал отражение, что мне не понравилось. На что мне обратить внимание в этом конкретном случае? Как бы то ни было, я не настаиваю на использовании обработки аннотаций для достижения этой функциональности.

Ben 25.12.2018 00:15
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
0
3
595
1

Ответы 1

Вы можете сделать свой собственный перехватчик или создать свой собственный PostProcessor. Однако в Spring есть хорошая встроенная функция (которая фактически используется повсюду во фреймворке) под названием События приложения. Это приятная мелочь, которая использует DI и Spring в качестве шины через небольшую симпатичную абстракцию именно для таких нужд, как ваши. (также см. статью этот блог для получения дополнительной информации).

Со стороны, принимающей ApplicationEvent, у вас может быть такая простая установка, как:

// Create an ApplicationEvent type for your "count" event like so...
public class CountEvent extends ApplicationEvent {

    private String counterName;

    ...

}

// Create a class that accepts the count events and meters them...
@Component
public class MeterEventService {

    @Autowired
    private PrometheusMeterRegistry registry;

    @EventListener
    public void createIdentity(CountEvent countEvent) {
        String countedMethod = countEvent.getCounterName();
        registry.counter(countedMethod).increment();
    }
}

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

@Component
@Aspect
public class Mail {

    // Autowire in ApplicationContext
    private ApplicationContext applicationContext;

    @After("execution (@<package to annotation>.Counted * *(..))")
    public void countMetric(JoinPoint jp){
        MethodSignature signature = (MethodSignature) jp.getSignature();
        String method = signature.getMethod().getName();
        applicationContext.publishEvent(new CountEvent(method));
    }
}

IMHO это не слишком много кода для большого удобства наличия такой функции.

Кроме того, если вы предпочитаете использовать value из @Counted вместо имени метода, вы можете аналогичным образом снять аннотацию делая это.

Я нашел решение, используя АОП и библиотеку отражений. Спасибо, что направили меня на правильный путь.

Ben 25.12.2018 17:33

Другие вопросы по теме