Вопрос 15 · Раздел 5

Что такое AOP (Aspect-Oriented Programming)?

4. Статические Pointcut → быстрее динамических 5. Избегайте состояния в аспектах (Singleton!) 6. @Around → не забудьте pjp.proceed()

Версии по языкам: English Russian Ukrainian

🟢 Junior Level

AOP — способ добавить общую функциональность (логирование, транзакции) ко многим классам, не изменяя их код.

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

// Без AOP:
public void save() {
    log.info("Начало");  // Повторяется везде
    repository.save(entity);
    log.info("Конец");
}

// С AOP:
@LogExecution  // ← Одна аннотация!
public void save() {
    repository.save(entity);
}

Зачем:

  • Убирает дублирование кода
  • Бизнес-логика отдельно, инфраструктура отдельно
  • Легко включить/выключить

🟡 Middle Level

Spring AOP vs AspectJ

  Spring AOP AspectJ
Как Прокси в рантайме Изменение байт-кода
Что может Только public методы Методы, поля, конструкторы
Self-invocation ❌ Не работает ✅ Работает
Сложность Простой Сложный настройка

Spring AOP работает только с Spring-бинами (вызовы через прокси). AspectJ работает с любыми объектами, т.к. внедряется на уровне байт-кода (compile-time или load-time weaving).

Термины AOP

Aspect (Аспект)      = модуль сквозной логики
Pointcut (Точка)     = где применять (выражение)
Advice (Совет)       = что делать (логика)
Join Point (Точка соединения) = конкретный метод

Типы Advice

@Before         // До метода
@After          // После метода (всегда)
@AfterReturning // После успешного
@AfterThrowing  // При исключении
@Around         // Вместо метода (самый мощный)

Пример аспекта

@Aspect
@Component
public class LoggingAspect {
    
    @Around("@annotation(LogExecution)")
    public Object log(ProceedingJoinPoint pjp) throws Throwable {
        long start = System.currentTimeMillis();
        try {
            return pjp.proceed();  // Вызов оригинального метода
        } finally {
            long duration = System.currentTimeMillis() - start;
            log.info("Выполнено за " + duration + "ms");
        }
    }
}

🔴 Senior Level

Interceptor Chain

Вызов метода → ReflectiveMethodInvocation:
  1. SecurityInterceptor → проверка прав
  2. TransactionInterceptor → транзакция
  3. CacheInterceptor → кэш
  4. LoggingAspect → логирование
  5. Target method → ваш код
  
→ Каждый возвращает результат или бросает exception
→ Порядок определяется @Order

Производительность

Оверхед AOP:
  → Создание MethodInvocation объекта
  → Проход по списку интерцепторов
  → Рефлексия или прямой вызов
  
→ ~1-5 мкс на вызов
→ В Highload (1M RPS) → заметно!

Spring 6 + Virtual Threads

`synchronized` (ключевое слово) в аспектах → Pinning виртуальных потоков!
→ Проверьте, не блокируют ли ваши аспекты системные потоки

Production Experience

Полный пример с Around без proceed() — в файле [[16. Что такое аспект, advice, pointcut, join point]].

Best Practices

  1. Spring AOP → 95% случаев
  2. AspectJ → приватные методы, поля
  3. @Order → порядок аспектов
  4. Статические Pointcut → быстрее динамических
  5. Избегайте состояния в аспектах (Singleton!)
  6. @Around → не забудьте pjp.proceed()

Резюме для Senior

  • AOP = сквозная функциональность без дублирования
  • Spring AOP = прокси, AspectJ = байт-код
  • Interceptor Chain → порядок через @Order
  • Оверхед ~1-5 мкс на вызов
  • Pinning → осторожно с synchronized в аспектах
  • pjp.proceed() → обязательно в @Around

🎯 Шпаргалка для интервью

Обязательно знать:

  • AOP = сквозная функциональность (логирование, транзакции, кэш) без дублирования кода
  • Spring AOP работает через прокси в рантайме; AspectJ — через изменение байт-кода
  • Ключевые термины: Aspect (модуль), Pointcut (где), Advice (что), Join Point (конкретный метод)
  • 5 типов Advice: @Before, @After, @AfterReturning, @AfterThrowing, @Around
  • @Around — самый мощный, но ОБЯЗААН вызывать pjp.proceed()
  • Порядок аспектов определяется через @Order
  • Spring AOP: только public методы Spring-бинов, self-invocation НЕ работает
  • Оверхед AOP: ~1-5 мкс на вызов, в Highload суммируется

Частые уточняющие вопросы:

  • Почему Spring AOP не работает с private-методами? → Прокси перехватывает только вызовы через proxy object, private-методы недоступны
  • Когда выбрать AspectJ вместо Spring AOP? → Когда нужны private-методы, поля, конструкторы, или self-invocation
  • @After vs @AfterReturning — в чём разница? → @After = всегда (как finally), @AfterReturning = только при успешном завершении
  • Что такое ReflectiveMethodInvocation? → Pipeline Spring, последовательно проходящий все interceptors

Красные флаги (НЕ говорить):

  • «Spring AOP работает с private-методами» — не работает
  • «@Around без proceed() допустим» — метод не вызовется вообще
  • «AOP добавляет миллисекунды overhead» — наносекунды (1-5 мкс)
  • «Аспект хранит состояние в полях» — аспект = Singleton, не потокобезопасно

Связанные темы:

  • [[13. Что такое прокси в Spring]]
  • [[14. Когда Spring создаёт прокси]]
  • [[16. Что такое аспект, advice, pointcut, join point]]
  • [[17. Что делает аннотация @Transactional]]