Pointcut — выбор точек применения аспекта
Pointcut (срез) — это выражение, определяющее, где именно в коде должен сработать аспект.
В Spring AOP чаще всего используется выражение execution(), которое описывает методы, на которые будет действовать совет (advice).
Примеры:
• execution(* com.example.service.*.*(..)) — все методы в пакете service.
• execution(public * save*(..)) — все публичные методы, начинающиеся с save.
• execution(* *(..)) — любой метод (всех классов и пакетов).
Подробное объяснение
В AOP «точкой соединения» (join point) называют место в программе, где можно вставить дополнительное действие.
Например — вызов метода, выбрасывание исключения или доступ к переменной.
Pointcut определяет набор таких точек, к которым будет применен аспект.
Без него Spring не знает, где именно выполнять ваш код из @Before, @After и других советов.
Самый распространенный способ описать pointcut — использовать выражение execution().
1. Синтаксис выражения execution()
Общий формат:
execution(modifiers-pattern? return-type-pattern declaring-type-pattern? method-name-pattern(param-pattern) throws-pattern?)
Разберем на примере:
execution(public * com.example.service.UserService.getUserById(..))
Расшифровка:
public — модификатор доступа;
* — любой возвращаемый тип;
com.example.service.UserService — класс, методы которого интересуют;
getUserById — имя метода;
( .. ) — любое количество любых параметров.
Таким образом, этот pointcut «перехватит» вызовы всех публичных методов getUserById в UserService.
2. Использование шаблонов (* и ..)
Шаблон Значение
* Любое имя или тип (например, метод любого имени, возвращаемый тип любой).
.. Любое количество параметров любого типа.
*.*(..) Все методы всех классов текущего пакета.
Примеры:
@Before("execution(* com.myapp.repository.*.*(..))")
public void beforeRepository() {
System.out.println("Вызов метода репозитория");
}
@After("execution(* save*(..))")
public void afterSaveMethods() {
System.out.println("Метод с префиксом save завершился");
}
Первый аспект отработает перед любым методом в пакете repository,
второй — после любых методов, начинающихся со слова save.
3. Объединение выражений
Можно комбинировать pointcut-условия с помощью логических операторов:
&& — И
|| — ИЛИ
! — НЕ
Пример:
@Before("execution(* com.example.service.*.*(..)) && !execution(* *.toString(..))")
public void logAllExceptToString() {
System.out.println("Метод сервиса (кроме toString) вызван");
}
Здесь аспект применяется ко всем методам в сервисах, кроме toString().
4. Вынос pointcut в отдельный метод
Чтобы не дублировать выражения, их можно вынести в отдельный метод и ссылаться по имени.
@Aspect
@Component
public class LoggingAspect {
@Pointcut("execution(* com.example.service.*.*(..))")
public void allServiceMethods() {}
@Before("allServiceMethods()")
public void beforeCall() {
System.out.println("Вызов метода сервиса");
}
}
Метод allServiceMethods() здесь пуст, он просто служит меткой для повторного использования выражения.
Это улучшает читаемость и позволяет применять один и тот же pointcut в нескольких советах.
Вывод:
Pointcut — механизм точного выбора мест, где должен сработать аспект.
С помощью выражений execution() можно настроить уровень детализации — от отдельных методов до целых пакетов.
Такой подход делает систему гибкой: аспекты применяются ровно там, где нужно, без вмешательства в исходный код.
