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

Что такое точка среза в Spring AOP?

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

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

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

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

Точка среза в Spring AOP

Точка среза (Pointcut) в Spring AOP — это выражение, которое определяет, к каким методам или конструкторам объектов будет применён аспект (aspect). Это правило выбора, которое указывает: "Примени совет к этому методу/классу/пакету".

Основные концепции AOP

Прежде чем понять pointcut, нужно знать основные термины:

  • Aspect (аспект) — модуль поперечной функциональности (логирование, безопасность)
  • Pointcut (точка среза) — выражение, определяющее где применить аспект
  • Advice (совет) — код, который выполняется в точке среза (before, after, around)
  • Join point (точка соединения) — фактический момент выполнения (вызов метода)

Синтаксис Pointcut

Spring использует специальный язык выражений AspectJ для описания pointcut'ов:

execution(Модификатор Возвращаемый_тип Класс.Метод(Параметры))

Примеры Pointcut выражений

1. Простое совпадение методов

@Pointcut("execution(public void com.example.UserService.create(..))")
public void userCreateMethod() {}

2. Все методы в классе

@Pointcut("execution(* com.example.UserService.*(..))")
public void allUserServiceMethods() {}

3. Все методы в пакете

@Pointcut("execution(* com.example.service.*.*(..))")
public void allServiceMethods() {}

4. Методы по типу возвращаемого значения

@Pointcut("execution(List com.example.*.*(..))")
public void methodsReturningList() {}

5. Методы по типам параметров

@Pointcut("execution(* com.example.*.*(String, Long))")
public void methodsWithStringAndLong() {}

6. Методы с аннотацией

@Pointcut("@annotation(com.example.Loggable)")
public void annotatedWithLoggable() {}

7. Логические комбинации

@Pointcut("execution(* com.example.service.*.*(..)) && !execution(* com.example.service.*.get*(..))")
public void allServiceMethodsExceptGetters() {}

Полный пример с Pointcut и Advice

import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.ProceedingJoinPoint;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class LoggingAspect {
    
    @Pointcut("execution(public * com.example.service.*.*(..)) && !execution(* com.example.service.*.get*(..))")
    public void businessMethods() {}
    
    @Before("businessMethods()")
    public void logBefore() {
        System.out.println("[LOG] Вызов бизнес-метода");
    }
    
    @After("businessMethods()")
    public void logAfter() {
        System.out.println("[LOG] Метод завершён");
    }
    
    @Around("businessMethods()")
    public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
        String methodName = joinPoint.getSignature().getName();
        long startTime = System.currentTimeMillis();
        
        System.out.println("[LOG] Начало: " + methodName);
        
        try {
            Object result = joinPoint.proceed();
            long duration = System.currentTimeMillis() - startTime;
            System.out.println("[LOG] " + methodName + " выполнен за " + duration + "мс");
            return result;
        } catch (Throwable ex) {
            System.out.println("[LOG] Ошибка в " + methodName + ": " + ex.getMessage());
            throw ex;
        }
    }
}

Встроенные типы Pointcut

execution — самый часто используемый:

@Pointcut("execution(public * save(..))")
public void publicSaveMethods() {}

call — вызов метода:

@Pointcut("call(* Repository.*(..))")
public void repositoryCalls() {}

within — все методы внутри класса/пакета:

@Pointcut("within(com.example.service.*)") 
public void allInService() {}

target — объект, на котором выполняется метод:

@Pointcut("target(com.example.UserService)")
public void onUserService() {}

args — по типам аргументов:

@Pointcut("args(String, ..)")
public void methodsWithStringFirstArg() {}

@annotation — по аннотациям:

@Pointcut("@annotation(org.springframework.transaction.annotation.Transactional)")
public void transactionalMethods() {}

Практический пример: Логирование + Обработка ошибок

@Aspect
@Component
public class ServiceAspect {
    
    @Pointcut("execution(public * com.example.service..*(..))")
    public void serviceMethods() {}
    
    @Around("serviceMethods()")
    public Object handleServiceCall(ProceedingJoinPoint joinPoint) 
            throws Throwable {
        String methodName = joinPoint.getSignature().getName();
        long start = System.currentTimeMillis();
        
        System.out.println("Вызов: " + methodName);
        
        try {
            Object result = joinPoint.proceed();
            long duration = System.currentTimeMillis() - start;
            System.out.println("Успех: " + methodName + " за " + duration + "мс");
            return result;
        } catch (Exception ex) {
            long duration = System.currentTimeMillis() - start;
            System.out.println("Ошибка в " + methodName + ": " + ex.getMessage());
            throw ex;
        }
    }
}

Как это работает

  1. Spring анализирует все классы при запуске приложения
  2. Находит классы с @Aspect
  3. Для каждого @Pointcut находит методы, которые под него подходят
  4. Для каждого найденного метода создаёт прокси-объект
  5. Прокси-объект перехватывает вызовы и выполняет advice

Особенности

Плюсы:

  • Разделение поперечной функциональности (DRY)
  • Чистая бизнес-логика без примеси логирования/безопасности
  • Централизованное управление

Минусы:

  • Сложнее отладить (код выполняется через прокси)
  • Может повлиять на производительность
  • Pointcut выражения могут быть ошибочными
  • Не работает с приватными методами

Точки среза (Pointcut) — это мощный инструмент для реализации поперечной функциональности, но требуют осторожного использования и хорошего понимания AOP концепций.

Что такое точка среза в Spring AOP? | PrepBro