Код-ревью Middle AOP и прокси обновлено

Код-ревью: импорт товаров

Вопрос

Найдите проблемы, связанные с прокси и аннотациями.

@Service
public class ImportService {
    private final ProductRepository repo;
    private final PriceClient priceClient;

    public ImportService(ProductRepository repo, PriceClient priceClient) {
        this.repo = repo;
        this.priceClient = priceClient;
    }

    public void importAll(List<ProductDto> items) {
        for (ProductDto dto : items) {
            importOne(dto);
        }
        notifyAdmins(items.size());
    }

    @Transactional
    public void importOne(ProductDto dto) {
        Product p = repo.save(new Product(dto.sku(), dto.name()));
        p.setPrice(currentPrice(dto.sku()));
    }

    @Cacheable("prices")
    public BigDecimal currentPrice(String sku) {
        return priceClient.fetch(sku);
    }

    @Async
    private void notifyAdmins(int count) {
        log.info("imported {}", count);
    }

    @Transactional
    public final void cleanup() {
        repo.deleteDrafts();
    }
}

Коротко

Все аннотации в этом классе молча не работают: вызовы через this, private и final методы прокси не перехватывает. Транзакционный импорт и кеш цен стоит вынести в отдельные бины (ProductImporter, PriceService), уведомление сделать публичным методом другого бина или событием с @Async-слушателем, а final убрать.

Что должен найти ревьюер

  1. Ошибкастрока 13
    importOne вызывается через this: @Transactional не сработает, каждый save идёт в своей авто-транзакции, изменение цены после save не сохранится (сущность не управляется).
  2. Производительностьстрока 21
    currentPrice вызывается через this: @Cacheable обходится, на каждый товар идёт внешний запрос за ценой.
  3. Ошибкастрока 30
    @Async на private-методе не работает (прокси его не видит), к тому же вызов через this: уведомление выполняется синхронно.
  4. Ошибкастрока 35
    final-метод не переопределяется CGLIB-прокси: транзакции нет, а вызов на прокси-объекте может упасть с NPE на repo.
  5. Производительностьстрока 19
    Даже если починить прокси, транзакция на каждый товар и внешний HTTP-вызов внутри транзакции держат соединение; лучше получить цены заранее пачкой.

Разбор

Все аннотации в этом классе молча не работают: вызовы через this, private и final методы прокси не перехватывает. Транзакционный импорт и кеш цен стоит вынести в отдельные бины (ProductImporter, PriceService), уведомление сделать публичным методом другого бина или событием с @Async-слушателем, а final убрать.

Ответьте сами, а не прочитайте

В тренажёре вопрос попадёт в повторения и вернётся, когда начнёт забываться. Плюс билеты с зачётом: 20 вопросов, не больше 2 ошибок.

Термины по теме