← Назад к вопросам
Как измерить время работы всех методов @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 | Избирательно | Гибкость | Декоратор |
| Micrometer | Production мониторинг | Интеграция | Сложность |
Практические советы
- Используй System.nanoTime() вместо System.currentTimeMillis() для микросекундной точности
- Избегай логирования в критичных методах — используй метрики
- Мониторь outliers — медленные запросы
- Сложность O(n) для поиска узких мест
Лучший выбор для production: Micrometer + Spring AOP с фильтрацией по времени выполнения.