Spring FrameworkMiddleTechnical

Что такое Spring AOP (Aspect-Oriented Programming)? Какую проблему он решает?

Spring AOP позволяет выносить сквозную логику (логирование, транзакции, безопасность) в отдельные аспекты вместо её дублирования в каждом методе. Работает через прокси-объекты на основе JDK Dynamic Proxy или CGLIB.

Spring AOP: назначение и концепция

AOP (Aspect-Oriented Programming) — парадигма, дополняющая ООП для решения задач сквозной функциональности (cross-cutting concerns): логирования, аудита, транзакционности, кэширования, проверки прав доступа. Без AOP такая логика дублируется во множестве методов, нарушая принцип единственной ответственности.

Spring AOP реализует подмножество полноценного AspectJ через прокси: при запросе бина из контекста возвращается прокси-объект, перехватывающий вызовы методов.

Ключевые термины

  • Aspect — модуль, инкапсулирующий сквозную логику (@Aspect)
  • Advice — конкретный код, выполняемый в точке соединения: @Before, @After, @AfterReturning, @AfterThrowing, @Around
  • Pointcut — выражение, определяющее, к каким методам применяется advice
  • Join Point — конкретное место выполнения (в Spring AOP — всегда вызов метода)
  • Weaving — процесс применения аспектов к целевому объекту (в Spring — во время создания прокси)

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

@Aspect
@Component
public class ExecutionTimeAspect {

    private static final Logger log = LoggerFactory.getLogger(ExecutionTimeAspect.class);

    // Все public-методы в пакете com.example.service
    @Pointcut("execution(public * com.example.service..*(..))")  
    public void serviceMethods() {}

    @Around("serviceMethods()")
    public Object measureTime(ProceedingJoinPoint pjp) throws Throwable {
        long start = System.currentTimeMillis();
        try {
            return pjp.proceed(); // вызов реального метода
        } finally {
            long elapsed = System.currentTimeMillis() - start;
            log.info("{} executed in {} ms",
                pjp.getSignature().toShortString(), elapsed);
        }
    }
}

Пример: аспект аудита через аннотацию

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Audited {
    String action();
}

@Aspect
@Component
public class AuditAspect {

    @AfterReturning("@annotation(audited)")
    public void audit(JoinPoint jp, Audited audited) {
        System.out.printf("Action '%s' called on %s%n",
            audited.action(), jp.getTarget().getClass().getSimpleName());
    }
}

// Использование:
@Audited(action = "DELETE_USER")
public void deleteUser(Long id) { /* ... */ }

Включение AOP

@Configuration
@EnableAspectJAutoProxy  // создаёт прокси для @Aspect-бинов
public class AopConfig {}

В Spring Boot достаточно зависимости spring-boot-starter-aop@EnableAspectJAutoProxy подключается автоматически.

Подводные камни

  • Прокси-ограничение: вызов метода внутри того же бина (self-invocation) не проходит через прокси и аспект не применяется. Обходное решение — инжектировать сам бин через контекст или использовать AspectJ weaving.
  • Spring AOP работает только с Spring-бинами — аспекты не применяются к объектам, созданным через new.
  • @Around advice обязан вызвать pjp.proceed(), иначе реальный метод никогда не выполнится.
  • JDK Dynamic Proxy применяется только к интерфейсам; для классов без интерфейса Spring переключается на CGLIB (который требует ненулевого конструктора).
  • Порядок применения нескольких аспектов к одному методу не детерминирован без явного указания @Order.
  • Pointcut-выражения компилируются при старте контекста — синтаксическая ошибка в выражении вызывает IllegalArgumentException.
  • AspectJ-аннотации доступны через зависимость aspectjweaver, но полноценный compile-time/load-time weaving AspectJ требует отдельной конфигурации Maven/Gradle плагина.

Common mistakes

  • Путать термин «spring aop purpose» с соседним механизмом Spring Framework.
  • Не называть границу lifecycle, transaction, thread или request для «spring aop purpose».
  • Игнорировать production-эффекты «spring aop purpose»: latency, SQL shape, memory, security или observability.

What the interviewer is testing

  • Попросить объяснить механизм «spring aop purpose» на минимальном примере.
  • Проверить, видит ли кандидат failure mode и диагностику для «spring aop purpose».
  • Уточнить, какие настройки или API меняют «spring aop purpose» в реальном сервисе.

Sources

Related topics