← Назад к вопросам

Как измерить время работы всех методов @Service

2.0 Middle🔥 111 комментариев
#SOLID и паттерны проектирования#Spring Framework

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

# Измерение времени работы методов @Service

1. Аспект AOP (рекомендуется)

Создаём аспект для перехвата всех методов сервисов:

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@Aspect
@Component
public class PerformanceMonitoringAspect {
    private static final Logger logger = LoggerFactory.getLogger(PerformanceMonitoringAspect.class);
    
    @Around("@within(org.springframework.stereotype.Service)")
    public Object monitorServiceMethods(ProceedingJoinPoint joinPoint) throws Throwable {
        long startTime = System.currentTimeMillis();
        
        String methodName = joinPoint.getSignature().getName();
        String className = joinPoint.getTarget().getClass().getSimpleName();
        
        try {
            Object result = joinPoint.proceed();
            return result;
        } finally {
            long duration = System.currentTimeMillis() - startTime;
            logger.info("Method {}.{} executed in {} ms", className, methodName, duration);
        }
    }
}

2. С использованием StopWatch (Spring Framework)

import org.springframework.util.StopWatch;
import org.springframework.stereotype.Service;

@Service
public class UserService {
    public void processUsers() {
        StopWatch stopWatch = new StopWatch("processUsers");
        
        stopWatch.start("fetch");
        // Получение пользователей
        List<User> users = fetchUsers();
        stopWatch.stop();
        
        stopWatch.start("process");
        // Обработка
        users.forEach(this::processUser);
        stopWatch.stop();
        
        System.out.println(stopWatch.prettyPrint());
    }
}

3. Кастомная аннотация + Аспект

// Создаём аннотацию
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Timed {
    String value() default "";
}

// Аспект для обработки
@Aspect
@Component
public class TimingAspect {
    private static final Logger logger = LoggerFactory.getLogger(TimingAspect.class);
    
    @Around("@annotation(timed)")
    public Object logExecutionTime(ProceedingJoinPoint joinPoint, Timed timed) throws Throwable {
        long startTime = System.nanoTime();
        
        Object result = joinPoint.proceed();
        
        long duration = (System.nanoTime() - startTime) / 1_000_000; // в мс
        String methodName = joinPoint.getSignature().getName();
        String operationName = timed.value().isEmpty() ? methodName : timed.value();
        
        logger.info("{} took {} ms", operationName, duration);
        
        return result;
    }
}

// Использование
@Service
public class OrderService {
    @Timed("saveOrder")
    public void saveOrder(Order order) {
        // Логика сохранения
    }
}

4. Миксованный подход с обработкой исключений

@Aspect
@Component
public class AdvancedPerformanceAspect {
    private static final Logger logger = LoggerFactory.getLogger(AdvancedPerformanceAspect.class);
    
    @Around("@within(org.springframework.stereotype.Service)")
    public Object monitorPerformance(ProceedingJoinPoint joinPoint) throws Throwable {
        String className = joinPoint.getTarget().getClass().getName();
        String methodName = joinPoint.getSignature().getName();
        String fullMethodName = className + "." + methodName;
        
        long startTime = System.currentTimeMillis();
        long startMemory = Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
        
        try {
            Object result = joinPoint.proceed();
            long duration = System.currentTimeMillis() - startTime;
            long memoryUsed = (Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()) - startMemory;
            
            logger.info(
                "SUCCESS: {} - Duration: {} ms, Memory: {} bytes",
                fullMethodName, duration, memoryUsed
            );
            return result;
        } catch (Exception e) {
            long duration = System.currentTimeMillis() - startTime;
            logger.error("FAILED: {} - Duration: {} ms, Error: {}", fullMethodName, duration, e.getMessage());
            throw e;
        }
    }
}

5. С использованием Micrometer (production-ready)

import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.Timer;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class MetricsAspect {
    private final MeterRegistry meterRegistry;
    
    public MetricsAspect(MeterRegistry meterRegistry) {
        this.meterRegistry = meterRegistry;
    }
    
    @Around("@within(org.springframework.stereotype.Service)")
    public Object recordMetrics(ProceedingJoinPoint joinPoint) throws Throwable {
        String methodName = joinPoint.getSignature().getName();
        
        return Timer.builder("service.method.execution")
            .tag("method", methodName)
            .publishPercentiles(0.5, 0.95, 0.99)
            .register(meterRegistry)
            .recordCallable(joinPoint::proceed);
    }
}

Включение AOP в pom.xml

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

Сравнение подходов

СпособПрименениеПлюсыМинусы
AOPВсе методы @ServiceГлобальное решениеOverhead
StopWatchВнутри методаТочное измерениеРучной код
@TimedИзбирательноГибкостьДекоратор
MicrometerProduction мониторингИнтеграцияСложность

Практические советы

  1. Используй System.nanoTime() вместо System.currentTimeMillis() для микросекундной точности
  2. Избегай логирования в критичных методах — используй метрики
  3. Мониторь outliers — медленные запросы
  4. Сложность O(n) для поиска узких мест

Лучший выбор для production: Micrometer + Spring AOP с фильтрацией по времени выполнения.