Анотація @Aspect: точка входу в світ AOP
Щоб почати працювати з AOP у Spring, потрібно визначити аспект. Використовується анотація @Aspect, яка каже контейнеру Spring, що клас є аспектом. Уявіть це як сигнал: "Гей, Spring, це клас з крос-функціональною логікою — слідкуй за ним!".
Приклад: створення аспекту Припустимо, у нас є сервіс для виконання бізнес-операцій, і ми хочемо логувати виклики всіх його методів:
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@Aspect
@Component // Реєстрація аспекту як Spring Bean
public class LoggingAspect {
@Before("execution(* com.example.service.*.*(..))")
public void logBeforeMethodCall() {
System.out.println("Метод був викликаний!");
}
}
Що тут відбувається?
@Aspect:робить цей клас аспектом.@Component:дозволяє Spring зареєструвати аспект як Bean (без цього анотації@Aspectне працюватимуть).@Before:позначає метод, який має виконуватися перед вказаною точкою з'єднання.
Веселий факт: AOP використовує магію проксі-об'єктів! Але не хвилюйся, ми скоро розберемося, як це працює за кулісами.
Особливості роботи з анотацією @Around
Анотація @Around — універсальний солдат AOP. Вона дозволяє виконати логіку як до, так і після виконання методу. Більше того, з її допомогою можна керувати викликом методу або навіть змінити його результат!
Приклад: вимірювання часу виконання методів
Нехай ми хочемо виміряти, скільки часу займає виклик кожного методу в сервісі:
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class PerformanceAspect {
@Around("execution(* com.example.service.*.*(..))")
public Object measureExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
long startTime = System.currentTimeMillis();
Object result = joinPoint.proceed(); // Виконуємо цільовий метод
long endTime = System.currentTimeMillis();
System.out.println("Метод " + joinPoint.getSignature() + " виконувався "
+ (endTime - startTime) + " мс");
return result; // Повертаємо результат виклику методу
}
}
Що тут відбувається?
ProceedingJoinPoint: представляє метод, до якого прив'язаний аспект. З його допомогою ми можемо викликати метод або пропустити його.proceed(): виконує цільовий метод.- Вимірювання часу: логування різниці часу до і після виклику методу.
Застосовуючи @Around, важливо пам'ятати, що виклик proceed() обов'язковий: без нього цільовий метод просто не виконається.
Застосування анотацій @Before і @After
Якщо хочеш додати логіку тільки ДО чи ПІСЛЯ виконання методу, то анотації @Before і @After прийдуться до речі. Давай розглянемо ці анотації на конкретних прикладах.
Приклад з @Before
Припустимо, ми хочемо перевірити, чи викликано метод, що належить певному класу:
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class AuthenticationAspect {
@Before("execution(* com.example.security.*.*(..))")
public void checkAuthentication() {
System.out.println("Перевірка авторизації користувача...");
}
}
Приклад з @After
Тепер додамо якусь логіку, яка виконається ПІСЛЯ виклику методу. Наприклад, очистимо кеш:
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class CacheAspect {
@After("execution(* com.example.service.*.*(..))")
public void clearCache() {
System.out.println("Очищення кешу після виконання методу...");
}
}
@Before: виконується перед викликом методу. Чудово підходить для валідації вхідних даних, перевірки аутентифікації тощо.@After: виконується після завершення методу (успішного або неуспішного). Використовується для задач "прибирання" — закриття ресурсів, зміни стану тощо.
Приклади використання @Before і @AfterReturning
З анотацією @AfterReturning можна отримати результат виконання методу і обробити його:
Приклад: зміна поверненого результату в @AfterReturning Припустимо, ми хочемо переконатися, що всі рядки, які повертає метод, в верхньому регістрі:
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class ResultAspect {
@AfterReturning(pointcut = "execution(String com.example.service.*.*(..))",
returning = "result")
public void modifyReturnValue(String result) {
System.out.println("Повернений результат: " + result.toUpperCase());
}
}
Як налаштувати Pointcut для більшої гнучкості
Більшість анотацій в AOP залежать від Pointcut-виразів. Ці вирази — серце AOP, бо вони вказують, до якої точки з'єднання потрібно прив'язатися. Ось приклади, щоб зробити їх застосування зрозумілішим:
Шаблони для Pointcut:
execution(* com.example.service.*.*(..)):- Усі методи в пакетах
com.example.service.
- Усі методи в пакетах
execution(public * *(..)):- Усі публічні методи у всіх класах.
execution(* com.example..*.*(..)):- Будь-який метод у будь-якому класі, розташованому під пакетом
com.example.
- Будь-який метод у будь-якому класі, розташованому під пакетом
@annotation(org.springframework.transaction.annotation.Transactional):- Застосувати аспект до всіх методів, анотованих
@Transactional.
- Застосувати аспект до всіх методів, анотованих
Обмеження та типові помилки
- Не працює без
Spring AOP: переконайся, що аспект увімкнений через конфігурацію (наприклад, за допомогою@EnableAspectJAutoProxyу класі конфігурації). - Помилки в Pointcut-виразі: якщо точки з'єднання не відповідають визначенню, аспект не спрацює.
- Проблеми з продуктивністю: використання AOP може уповільнити виконання методу (особливо при активному логуванні).
- Проксі-об'єкти: пам'ятай, що AOP працює лише з проксі (потрібні Spring Beans).
Що далі?
Тепер ти знаєш, як за допомогою анотацій @Aspect, @Around, @Before і @After створювати потужні й гнучкі аспекти в Spring. Ці інструменти допомагають легко додавати крос-функціональну логіку в додаток, роблячи код чистішим і більш модульним.
Наступні кроки — застосувати ці знання на практиці: налаштування складних Pointcut-виразів і створення кастомних аспектів.
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ