JavaRush /Курсы /Модуль 5. Spring /Практика: настройка простого аспекта для логирования

Практика: настройка простого аспекта для логирования

Модуль 5. Spring
3 уровень , 4 лекция
Открыта

Давайте создадим практический пример, в котором настроим аспект для логирования.

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!

Теперь у вас есть инструмент для упрощения жизни. Используйте его мудро и осторожно, как волшебную палочку. 😉

Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ