← Назад к вопросам

Что такое АОП?

2.0 Middle🔥 161 комментариев
#Spring Framework

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Aspect-Oriented Programming (АОП)

АОП — парадигма программирования, которая позволяет выделить crosscutting concerns (сквозные задачи) в отдельные аспекты. Это дополнение к объектно-ориентированному программированию, которое решает проблему распределённой логики по всему коду.

Проблема, которую решает АОП

В обычном коде логирование, логика безопасности, обработка транзакций распределена повсеместно:

public class UserService {
  public void createUser(User user) {
    logger.info("Creating user: " + user.getName());  // логирование
    checkPermission("CREATE_USER");                   // безопасность
    beginTransaction();                               // транзакция
    
    // Бизнес-логика
    userRepository.save(user);
    sendEmail(user);
    
    commitTransaction();                              // транзакция
    logger.info("User created");                      // логирование
  }
  
  public void deleteUser(long id) {
    logger.info("Deleting user: " + id);              // логирование повторяется
    checkPermission("DELETE_USER");                   // безопасность повторяется
    beginTransaction();                               // транзакция повторяется
    
    userRepository.deleteById(id);
    
    commitTransaction();                              // транзакция повторяется
    logger.info("User deleted");                      // логирование повторяется
  }
}

Вся эта служебная логика делает бизнес-логику грязной и сложной. АОП решает это.

Ключевые концепции АОП

1. Aspect (Аспект) — модуль кода, который содержит crosscutting concern:

@Aspect
@Component
public class LoggingAspect {
  private static final Logger logger = LoggerFactory.getLogger(LoggingAspect.class);
  
  @Before("execution(* com.example.service.*.*(..))")  // Pointcut
  public void logBefore(JoinPoint joinPoint) {
    logger.info("Method called: " + joinPoint.getSignature().getName());
  }
  
  @After("execution(* com.example.service.*.*(..))") // Pointcut
  public void logAfter(JoinPoint joinPoint) {
    logger.info("Method finished: " + joinPoint.getSignature().getName());
  }
}

2. Pointcut (Точка входа) — выражение, которое выбирает точки в коде, где применяется аспект:

// Все методы в классах пакета service
@Before("execution(* com.example.service.*.*(..))")

// Все методы, помеченные аннотацией @Cacheable
@Before("@annotation(org.springframework.cache.annotation.Cacheable)")

// Все public методы UserService
@Before("execution(public * com.example.UserService.*(..))")

// Все методы, начинающиеся на save
@Before("execution(* *.save*(..))")

3. Advice (Совет) — код, который выполняется в точке входа:

@Before      // Выполняется ДО метода
@After       // Выполняется ПОСЛЕ метода
@AfterReturning  // ПОСЛЕ успешного возврата
@AfterThrowing   // ПОСЛЕ выброса исключения
@Around      // ВОКРУГ метода (может изменить результат)

4. Join Point — точка в программе, где применяется advice:

@Around("execution(* com.example.service.*.*(..))") // Pointcut
public Object aroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable {
  long start = System.currentTimeMillis();
  
  try {
    Object result = joinPoint.proceed(); // Вызываем исходный метод
    return result;
  } finally {
    long duration = System.currentTimeMillis() - start;
    System.out.println("Method took: " + duration + "ms");
  }
}

5. Weaving (Ткачество) — процесс применения аспектов к коду. Может быть compile-time, load-time или runtime.

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

Пример 1: Логирование

@Aspect
@Component
public class LoggingAspect {
  private static final Logger logger = LoggerFactory.getLogger(LoggingAspect.class);
  
  @Around("execution(* com.example.service.*.*(..))")
  public Object logMethodExecution(ProceedingJoinPoint joinPoint) throws Throwable {
    String methodName = joinPoint.getSignature().getName();
    Object[] args = joinPoint.getArgs();
    
    logger.debug("Entering method: " + methodName + " with args: " + Arrays.toString(args));
    
    long start = System.currentTimeMillis();
    try {
      Object result = joinPoint.proceed();
      logger.debug("Method " + methodName + " returned: " + result);
      return result;
    } catch (Exception e) {
      logger.error("Method " + methodName + " threw exception: " + e.getMessage(), e);
      throw e;
    } finally {
      long duration = System.currentTimeMillis() - start;
      logger.debug("Method " + methodName + " took " + duration + "ms");
    }
  }
}

Пример 2: Транзакции (как работает @Transactional)

@Aspect
@Component
public class TransactionAspect {
  
  @Around("@annotation(org.springframework.transaction.annotation.Transactional)")
  public Object manageTransaction(ProceedingJoinPoint joinPoint) throws Throwable {
    // Начало транзакции
    Transaction tx = transactionManager.begin();
    
    try {
      Object result = joinPoint.proceed();
      // Успех — коммитим
      tx.commit();
      return result;
    } catch (Exception e) {
      // Ошибка — откатываем
      tx.rollback();
      throw e;
    }
  }
}

Пример 3: Кэширование

@Aspect
@Component
public class CachingAspect {
  private Map<String, Object> cache = new HashMap<>();
  
  @Around("@annotation(com.example.Cacheable)")
  public Object handleCache(ProceedingJoinPoint joinPoint) throws Throwable {
    String key = joinPoint.getSignature().getName() + Arrays.toString(joinPoint.getArgs());
    
    if (cache.containsKey(key)) {
      return cache.get(key);
    }
    
    Object result = joinPoint.proceed();
    cache.put(key, result);
    return result;
  }
}

Spring AOP vs AspectJ

Spring AOP:

  • Работает только с Spring бинами
  • Runtime weaving (более простой)
  • Прокси-базированный подход
  • Ограничения: работает только с public методами

AspectJ:

  • Compile-time или load-time weaving
  • Работает везде (не только Spring)
  • Мощнее, но сложнее
  • Требует компилятора AspectJ

Когда использовать АОП

✅ Логирование ✅ Обработка транзакций ✅ Кэширование ✅ Безопасность (проверка прав) ✅ Обработка исключений ✅ Аудит

❌ Бизнес-логика (должна быть явной) ❌ Частые изменения (становится сложно отладить)

Минусы АОП

  • Неявное поведение (сложнее разобраться что происходит)
  • Отладка затруднена
  • Перерасход памяти на прокси-объекты
  • Может скрывать проблемы в коде

АОП — мощный инструмент, но используй его разумно для действительно crosscutting concerns.