行业资讯 2025年08月6日
0 收藏 0 点赞 870 浏览 8336 个字
摘要 :

文章目录 一、手动使用StopWatch 二、借助AOP实现全局方法耗时统计 三、自定义注解+AOP实现精细化控制 四、利用拦截器统计Controller接口耗时 五、借助Actuator + Micro……




  • 一、手动使用StopWatch
  • 二、借助AOP实现全局方法耗时统计
  • 三、自定义注解+AOP实现精细化控制
  • 四、利用拦截器统计Controller接口耗时
  • 五、借助Actuator + Micrometer实现细粒度监控
  • 六、通过Filter实现请求耗时统计
  • 七、利用ServletRequestHandledEvent统计请求处理耗时
  • 总结与对比

在开发Spring Boot项目时,咱们常常会遇到性能方面的问题。要想精准定位并解决这些问题,统计方法的执行时间就显得尤为重要。通过了解各个方法的耗时情况,我们能够快速找出性能瓶颈,进而优化系统响应速度。今天,我就来给大家分享在Spring Boot框架里实现方法耗时统计的七种实用方法。

一、手动使用StopWatch

在众多统计方法耗时的方式中,手动使用Spring提供的StopWatch类是最直接的一种。这种方法简单易懂,特别适合做临时性的性能测试。

import org.springframework.util.StopWatch;

@Service
public class UserService {
    
    public User findUserById(Long id) {
        // 创建StopWatch实例,用于记录时间
        StopWatch stopWatch = new StopWatch();
        // 开始计时
        stopWatch.start();
        
        // 业务逻辑,从数据库中根据id查找用户
        User user = userRepository.findById(id).orElse(null);
        
        // 停止计时
        stopWatch.stop();
        // 输出方法执行耗时
        System.out.println(\"findUserById方法耗时:\" + stopWatch.getTotalTimeMillis() + \"ms\");
        
        return user;
    }
}

这种方式的优点很明显,它简单直观,不需要额外配置其他东西。但缺点也不容忽视,它会侵入业务代码,使代码看起来不够优雅。而且,如果要监控多个方法的耗时,就需要在每个方法里都手动添加这些代码,工作量较大。

二、借助AOP实现全局方法耗时统计

AOP,也就是面向切面编程,用它来统计方法耗时是个很不错的选择。使用AOP,我们可以在不改动原有业务代码的基础上,统一处理耗时统计的逻辑。

首先,要在项目中添加AOP依赖,在pom.xml文件中添加如下代码:

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

添加完依赖后,接着创建切面类:

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import org.springframework.util.StopWatch;

@Aspect
@Component
public class MethodTimeAspect {
    // 定义切点,这里表示匹配com.example.demo.service包下所有类的所有方法
    @Pointcut(\"execution(* com.example.demo.service.*.*(..))\")
    public void serviceMethodPointcut() {}
    
    // 环绕通知,在目标方法执行前后进行计时
    @Around(\"serviceMethodPointcut()\")
    public Object timeAround(ProceedingJoinPoint joinPoint) throws Throwable {
        // 创建StopWatch实例
        StopWatch stopWatch = new StopWatch();
        // 开始计时
        stopWatch.start();
        
        // 执行目标方法
        Object result = joinPoint.proceed();
        
        // 停止计时
        stopWatch.stop();
        // 获取目标方法的名称
        String methodName = joinPoint.getSignature().getName();
        // 输出方法执行耗时
        System.out.println(\"方法[\" + methodName + \"]耗时:\" + stopWatch.getTotalTimeMillis() + \"ms\");
        
        return result;
    }
}

这种方式的优势在于,代码没有侵入性,所有的耗时统计逻辑都可以统一管理,配置起来也很灵活。不过,它也有不足的地方,对于一些特定方法的定制化需求,实现起来不是那么方便。

三、自定义注解+AOP实现精细化控制

如果想要更精确地控制哪些方法需要进行耗时统计,我们可以把自定义注解和AOP结合起来使用。

先创建自定义注解:

import java.lang.annotation.*;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TimeLog {
    // 注解的值,用于描述方法,默认为空
    String value() default \"\";
}

然后,创建切面类来处理带有该注解的方法:

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.util.StopWatch;

@Aspect
@Component
public class TimeLogAspect {
    
    @Around(\"@annotation(com.example.demo.annotation.TimeLog)\")
    public Object timeLogAround(ProceedingJoinPoint joinPoint) throws Throwable {
        // 获取方法签名
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        // 获取方法上的TimeLog注解
        TimeLog timeLog = signature.getMethod().getAnnotation(TimeLog.class);
        
        // 根据注解的值来确定方法描述,如果注解值为空,则使用方法名
        String methodDesc = timeLog.value().isEmpty() ? 
                signature.getMethod().getName() : timeLog.value();
        
        // 创建StopWatch实例
        StopWatch stopWatch = new StopWatch();
        // 开始计时
        stopWatch.start();
        
        // 执行目标方法
        Object result = joinPoint.proceed();
        
        // 停止计时
        stopWatch.stop();
        // 输出方法执行耗时
        System.out.println(\"方法[\" + methodDesc + \"]耗时:\" + stopWatch.getTotalTimeMillis() + \"ms\");
        
        return result;
    }
}

使用示例如下:

@Service
public class ProductService {
    
    @TimeLog(\"查询商品详情\")
    public Product getProductDetail(Long id) {
        // 业务逻辑,从数据库中根据id查找商品详情
        return productRepository.findById(id).orElse(null);
    }
}

这种方法的好处是可以进行更精细的控制,注解还能携带更多信息,方便我们根据不同需求进行定制。但缺点是需要手动在每个要监控的方法上添加注解。

四、利用拦截器统计Controller接口耗时

要是我们只关心Controller层接口的耗时情况,那么使用Spring的拦截器是个不错的选择。

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

@Component
public class ApiTimeInterceptor implements HandlerInterceptor {
    // 使用ThreadLocal来存储请求开始时间,避免多线程环境下的数据冲突
    private ThreadLocal<Long> startTime = new ThreadLocal<>();
    
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        // 在请求处理前,记录开始时间
        startTime.set(System.currentTimeMillis());
        return true;
    }
    
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {
        // 在请求处理后,获取结束时间
        long endTime = System.currentTimeMillis();
        // 计算请求执行时间
        long executionTime = endTime - startTime.get();
        // 获取请求的URI
        String uri = request.getRequestURI();
        // 输出接口执行耗时
        System.out.println(\"接口[\" + uri + \"]耗时:\" + executionTime + \"ms\");
        // 移除ThreadLocal中的数据,避免内存泄漏
        startTime.remove();
    }
}

别忘了注册拦截器:

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class WebConfig implements WebMvcConfigurer {
    
    private final ApiTimeInterceptor apiTimeInterceptor;
    
    public WebConfig(ApiTimeInterceptor apiTimeInterceptor) {
        this.apiTimeInterceptor = apiTimeInterceptor;
    }
    
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 添加拦截器,并指定要拦截的路径
        registry.addInterceptor(apiTimeInterceptor).addPathPatterns(\"/api/\");
    }
}

这种方式专注于Web接口的性能监控,可以对接口进行统一管理。但它只能监控Controller层的方法,对于内部服务方法的耗时监控就无能为力了。

五、借助Actuator + Micrometer实现细粒度监控

Spring Boot Actuator和Micrometer集成后,可以实现更专业的性能指标收集。

先添加相关依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
    <groupId>io.micrometer</groupId>
    <artifactId>micrometer-registry-prometheus</artifactId>
</dependency>

接着,使用Micrometer进行方法计时:

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

@Service
public class OrderService {
    
    private final MeterRegistry meterRegistry;
    
    public OrderService(MeterRegistry meterRegistry) {
        this.meterRegistry = meterRegistry;
    }
    
    public Order createOrder(OrderRequest request) {
        // 开始计时
        Timer.Sample sample = Timer.start(meterRegistry);
        
        // 业务逻辑,处理订单
        Order order = processOrder(request);
        
        // 停止计时,并记录到指定的计时器中
        sample.stop(meterRegistry.timer(\"order.creation.time\"));
        
        return order;
    }
}

最后,配置Actuator暴露指标:

management:
  endpoints:
    web:
      exposure:
        include: metrics,prometheus
  metrics:
    export:
      prometheus:
        enabled: true

这种方式可以收集专业的性能指标,还能和Prometheus、Grafana等监控系统集成,非常适合生产环境。不过,它的配置相对复杂一些,需要我们花点时间去学习和理解。

六、通过Filter实现请求耗时统计

创建一个Filter实现类,能够记录每次HTTP请求的开始时间和结束时间,进而计算出整个请求的耗时。

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.IOException;

@Component
public class TimingFilter implements Filter {
    
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) 
            throws IOException, ServletException {
        // 记录请求开始时间
        long startTime = System.currentTimeMillis();
        
        // 继续处理请求
        chain.doFilter(request, response);
        
        // 记录请求结束时间
        long endTime = System.currentTimeMillis();
        // 计算请求执行时间
        long executionTime = endTime - startTime;
        // 获取请求的URI
        String requestUri = ((HttpServletRequest) request).getRequestURI();
        // 输出请求执行耗时
        System.out.println(\"请求[\" + requestUri + \"]耗时:\" + executionTime + \"ms\");
    }
    
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {}
    
    @Override
    public void destroy() {}
}

这种方式可以全局监控所有Web请求的耗时情况,实现起来也比较简单。但它只能提供整体请求的耗时信息,没办法深入到具体业务逻辑的执行时间。

七、利用ServletRequestHandledEvent统计请求处理耗时

Spring Boot提供了ServletRequestHandledEvent事件,利用它可以监控HTTP请求的处理时间,这种方式适合对所有请求进行全局监控。

首先,创建事件监听器:

import org.springframework.context.ApplicationListener;
import org.springframework.web.context.request.ServletRequestHandledEvent;
import org.springframework.stereotype.Component;

@Component
public class RequestTimingListener implements ApplicationListener<ServletRequestHandledEvent> {

    @Override
    public void onApplicationEvent(ServletRequestHandledEvent event) {
        // 输出请求的URL和处理时间
        System.out.println(\"请求[\" + event.getRequestUrl() + \"]耗时:\" + event.getProcessingTimeMillis() + \"ms\");
    }
}

这种方法的优点是不需要修改现有代码,就能实现全局请求的耗时监控。但它不支持对请求进行自定义粒度的控制。

总结与对比

在Spring Boot项目中,这七种方法耗时统计方式各有千秋。在实际应用中,我们可以根据具体场景来选择合适的方法:

  • 如果你只是临时做个性能测试,想要快速实现方法耗时统计,那么手动使用StopWatch就很合适。
  • 要是想对整个服务层进行性能监控,全局AOP是个不错的选择。
  • 当需要精细化控制,只监控关键方法时,自定义注解+AOP的方式更能满足需求。
  • 专注于Web接口性能监控的话,使用拦截器就可以。
  • 在生产环境中,希望和专业监控系统集成,实现更专业的性能指标收集,Actuator + Micrometer是最佳选择。
  • 对于简单的全局Web请求耗时监控,Filter方式轻量级又实用。
  • 如果不想改动现有代码,只想全局监控HTTP请求处理时间,ServletRequestHandledEvent就能派上用场。

通过合理选择和运用这些方法,我们能够更高效地优化Spring Boot项目的性能。希望大家在实际开发中都能熟练运用,以后碰到耗时统计问题,都是小case啦!

微信扫一扫

支付宝扫一扫

版权: 转载请注明出处:https://www.zuozi.net/10561.html

管理员

相关推荐
2025-08-06

文章目录 一、Reader 接口概述 1.1 什么是 Reader 接口? 1.2 Reader 与 InputStream 的区别 1.3 …

988
2025-08-06

文章目录 一、事件溯源 (一)核心概念 (二)Kafka与Golang的优势 (三)完整代码实现 二、命令…

465
2025-08-06

文章目录 一、证明GC期间执行native函数的线程仍在运行 二、native线程操作Java对象的影响及处理方…

348
2025-08-06

文章目录 一、事务基础概念 二、MyBatis事务管理机制 (一)JDBC原生事务管理(JdbcTransaction)…

456
2025-08-06

文章目录 一、SnowFlake算法核心原理 二、SnowFlake算法工作流程详解 三、SnowFlake算法的Java代码…

517
2025-08-06

文章目录 一、本地Jar包的加载操作 二、本地Class的加载方法 三、远程Jar包的加载方式 你知道Groo…

832
发表评论
暂无评论

还没有评论呢,快来抢沙发~

助力内容变现

将您的收入提升到一个新的水平

点击联系客服

在线时间:08:00-23:00

客服QQ

122325244

客服电话

400-888-8888

客服邮箱

122325244@qq.com

扫描二维码

关注微信客服号