Давай створимо практичний приклад, в якому налаштуємо аспект для логування.
1. Логування: що і навіщо?
Логування — це процес запису даних про виконання додатка. Воно допомагає відстежити, що відбувається "під капотом", і стає головним артефактом для відладки і аналізу в продакшн-середовищах.
Наприклад, якщо в тебе є метод, який обробляє замовлення в інтернет-магазині, корисно знати:
- Коли цей метод був викликаний.
- Які дані передані як параметри.
- Що відбулося всередині методу: успіх чи помилка.
За допомогою AOP ми можемо впровадити логування без зміни бізнес-логіки методів. Тобто твій код лишається чистим, а функціональність додається на рівні аспекту.
2. Налаштування проєкту
Крок 1: Підключи залежності
Створи Spring Boot проєкт (якщо ще не зробив) і переконайся, що spring-boot-starter-aop є в твоєму pom.xml (або build.gradle):
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
Крок 2: Увімкни підтримку AOP
Spring Boot автоматично активує AOP, якщо ти додав залежність. Жодних додаткових налаштувань не знадобиться. Однак якщо ти використовуєш Spring Framework без Boot, переконайся, що в конфігурації увімкнено компонент @EnableAspectJAutoProxy.
3. Створення аспекту для логування
Почнемо з простого аспекту для логування методів.
Крок 1: Створи клас аспекту
Створи новий клас LoggingAspect у проєкті:
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class LoggingAspect {
@Before("execution(* com.example.demo.service.*.*(..))") // Вкажи, де застосовувати аспект
public void logBeforeMethodExecution() {
System.out.println("A method in the service layer is about to be called.");
}
}
Розбір коду:
@Aspect— вказує, що цей клас є аспектом.@Component— реєструє аспект як Spring Bean.@Before— визначає, що вказана дія (logBeforeMethodExecution) має виконатися перед викликом методу."execution(* com.example.demo.service.*.*(..))"— це вираз Pointcut. Він обирає всі методи з пакетуcom.example.demo.service.
Крок 2: Розроби сервіс для тесту
Додамо простий сервіс, щоб перевірити наше логування. Для цього в пакеті service створюємо клас OrderService:
package com.example.demo.service;
import org.springframework.stereotype.Service;
@Service
public class OrderService {
public void processOrder(String orderId) {
System.out.println("Processing order with ID: " + orderId);
}
}
Крок 3: Створи контролер
Щоб перевірити роботу аспекту, створимо контролер. Додаємо новий клас OrderController:
package com.example.demo.controller;
import com.example.demo.service.OrderService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class OrderController {
private final OrderService orderService;
public OrderController(OrderService orderService) {
this.orderService = orderService;
}
@GetMapping("/process-order")
public String processOrder(@RequestParam String orderId) {
orderService.processOrder(orderId);
return "Order processed!";
}
}
4. Тестуємо!
Запусти застосунок і відкрий браузер. Введи URL, наприклад:
http://localhost:8080/process-order?orderId=123
Ти маєш побачити результат у консолі:
A method in the service layer is about to be called.
Processing order with ID: 123
Перемога! Ти успішно додав логування в метод сервісу без зміни його коду.
5. Покращуємо наш аспект: логування аргументів
Давай додамо можливість логувати параметри виклику методу. Для цього використовуй API JoinPoint. Онови свій аспект так:
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class LoggingAspect {
@Before("execution(* com.example.demo.service.*.*(..))")
public void logBeforeMethodExecution(JoinPoint joinPoint) {
String methodName = joinPoint.getSignature().getName();
Object[] methodArguments = joinPoint.getArgs();
System.out.println("Method " + methodName + " is called with arguments: ");
for (Object arg : methodArguments) {
System.out.println(" " + arg);
}
}
}
Тепер при виклику методу в консолі ти побачиш:
Method processOrder is called with arguments:
123
Processing order with ID: 123
Це робить логування набагато інформативнішим.
6. Додаємо @Around для вимірювання часу виконання
Наступний крок: вимірювання часу роботи методів. Оновимо аспект з використанням @Around:
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 PerformanceLoggingAspect {
@Around("execution(* com.example.demo.service.*.*(..))")
public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
long startTime = System.currentTimeMillis();
Object result = joinPoint.proceed(); // Виконуємо метод
long endTime = System.currentTimeMillis();
System.out.println("Execution time of " + joinPoint.getSignature().getName() +
": " + (endTime - startTime) + "ms");
return result;
}
}
Тепер, коли ти викличеш метод, отримаєш ще й інформацію про його час виконання:
Processing order with ID: 123
Execution time of processOrder: 5ms
7. Зворотний зв'язок: типові помилки
Одна з найпоширеніших помилок при використанні AOP — неправильне визначення Pointcut. Наприклад, якщо вкажеш занадто «широкий» Pointcut, твій аспект може почати спрацьовувати на несподіваних методах, наприклад, toString(). Щоб уникнути цього, намагайся чітко вказувати пакети й класи.
8. І що далі?
Додавання рівнів логування, динамічне керування аспектами через конфігураційні файли, створення аспектів для обробки виключень... Ласкаво просимо в світ безмежних можливостей Spring AOP!
Тепер у тебе є інструмент, який спрощує життя. Використовуй його мудро й обережно, як чарівну паличку. 😉
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ