JavaRush /Курси /Модуль 5. Spring /Як працює AOP у Spring: анотації @Aspect, @Around, @Befor...

Як працює AOP у Spring: анотації @Aspect, @Around, @Before, @After

Модуль 5. Spring
Рівень 3 , Лекція 3
Відкрита

Анотація @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("Метод був викликаний!");
    }
}

Що тут відбувається?

  1. @Aspect: робить цей клас аспектом.
  2. @Component: дозволяє Spring зареєструвати аспект як Bean (без цього анотації @Aspect не працюватимуть).
  3. @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; // Повертаємо результат виклику методу
    }
}

Що тут відбувається?

  1. ProceedingJoinPoint: представляє метод, до якого прив'язаний аспект. З його допомогою ми можемо викликати метод або пропустити його.
  2. proceed(): виконує цільовий метод.
  3. Вимірювання часу: логування різниці часу до і після виклику методу.

Застосовуючи @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:

  1. execution(* com.example.service.*.*(..)):
    • Усі методи в пакетах com.example.service.
  2. execution(public * *(..)):
    • Усі публічні методи у всіх класах.
  3. execution(* com.example..*.*(..)):
    • Будь-який метод у будь-якому класі, розташованому під пакетом com.example.
  4. @annotation(org.springframework.transaction.annotation.Transactional):
    • Застосувати аспект до всіх методів, анотованих @Transactional.

Обмеження та типові помилки

  1. Не працює без Spring AOP: переконайся, що аспект увімкнений через конфігурацію (наприклад, за допомогою @EnableAspectJAutoProxy у класі конфігурації).
  2. Помилки в Pointcut-виразі: якщо точки з'єднання не відповідають визначенню, аспект не спрацює.
  3. Проблеми з продуктивністю: використання AOP може уповільнити виконання методу (особливо при активному логуванні).
  4. Проксі-об'єкти: пам'ятай, що AOP працює лише з проксі (потрібні Spring Beans).

Що далі?

Тепер ти знаєш, як за допомогою анотацій @Aspect, @Around, @Before і @After створювати потужні й гнучкі аспекти в Spring. Ці інструменти допомагають легко додавати крос-функціональну логіку в додаток, роблячи код чистішим і більш модульним.

Наступні кроки — застосувати ці знання на практиці: налаштування складних Pointcut-виразів і створення кастомних аспектів.

Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ