SpringBoot执行时间与异常处理方法
积累知识,胜过积蓄金银!毕竟在文章开发的过程中,会遇到各种各样的问题,往往都是一些细节知识点还没有掌握好而导致的,因此基础知识点的积累是很重要的。下面本文《Spring Boot记录执行时间与异常处理技巧》,就带大家讲解一下知识点,若是你对本文感兴趣,或者是想搞懂其中某个知识点,就请你继续往下看吧~

本文探讨了在Spring Boot应用中,如何在利用ExceptionHandler进行统一异常处理的同时,准确记录方法执行时间。文章提供了两种核心策略:一是利用Spring AOP实现横切关注点,在方法执行前后及异常捕获时统一测量时间;二是设计自定义异常,将执行时间封装传递给ExceptionHandler。通过详细的代码示例和专业分析,帮助开发者选择并实施最适合其应用场景的执行时间记录与异常处理方案。
在现代企业级应用开发中,监控方法执行时间是性能优化和问题诊断的关键环节。特别是在Spring Boot应用中,当结合统一异常处理机制(如@ExceptionHandler)时,如何在捕获异常的同时依然准确记录相关方法的执行时间,成为了一个常见的挑战。传统的try-catch块内手动计时方式,虽然直接,但在大规模应用中会导致代码冗余,且难以与ExceptionHandler无缝集成。本文将深入探讨两种高效且优雅的解决方案。
1. 利用Spring AOP实现执行时间测量与异常封装
Spring AOP(面向切面编程)是解决横切关注点(如日志记录、性能监控、事务管理等)的强大工具。通过定义切面,我们可以在不修改核心业务逻辑的情况下,在方法执行前、执行后或异常抛出时插入额外的行为。
1.1 AOP切面设计
我们可以创建一个@Aspect类,使用@Around通知来环绕目标方法的执行。在@Around通知中,我们可以精确地记录方法的开始和结束时间,并在方法执行过程中捕获任何异常。
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import java.time.Duration;
import java.time.Instant;
@Aspect
@Component
public class ExecutionTimeAspect {
private static final Logger logger = LoggerFactory.getLogger(ExecutionTimeAspect.class);
// 定义切点,可以根据实际需求调整,例如只作用于特定包下的方法
// @Pointcut("within(com.example.myapp.service..*)")
// public void serviceMethods() {}
// 使用 @Around 通知环绕目标方法
@Around("@annotation(com.example.myapp.annotation.LogExecutionTime)") // 假设我们定义了一个自定义注解
public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
Instant start = Instant.now();
Object result;
try {
// 执行目标方法
result = joinPoint.proceed();
} catch (Exception ex) {
// 捕获异常,记录执行时间
Instant end = Instant.now();
long executionTimeMillis = Duration.between(start, end).toMillis();
logger.error("方法 {} 执行异常,耗时 {} ms. 异常信息: {}",
joinPoint.getSignature().toShortString(),
executionTimeMillis,
ex.getMessage(),
ex);
// 重新抛出异常,以便ExceptionHandler可以捕获
throw ex;
}
Instant end = Instant.now();
long executionTimeMillis = Duration.between(start, end).toMillis();
logger.info("方法 {} 执行成功,耗时 {} ms.",
joinPoint.getSignature().toShortString(),
executionTimeMillis);
return result;
}
}1.2 自定义注解(可选)
为了更灵活地控制哪些方法需要被计时,我们可以定义一个自定义注解,并在切点中使用它。
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 LogExecutionTime {
}然后在需要计时的业务方法上添加此注解:
@Service
public class MyService {
@LogExecutionTime
public String doSomeFancyStuff() throws Exception {
// 模拟耗时操作
Thread.sleep(500);
// 模拟可能抛出异常
// if (true) throw new RuntimeException("Something went wrong!");
return "Task Completed";
}
}1.3 优势与注意事项
- 优势: 代码解耦,业务逻辑更纯粹;集中管理性能监控和异常日志;易于维护和扩展。
- 注意事项: AOP会引入一定的性能开销;切点表达式需要谨慎定义,避免过度匹配或漏匹配;确保AOP配置正确启用(Spring Boot通常自动配置)。
2. 通过自定义异常传递执行时间到ExceptionHandler
如果应用已经大量依赖@ExceptionHandler进行统一异常处理,并且希望将执行时间信息也传递给它,那么可以考虑定义一个包含执行时间字段的自定义异常。
2.1 定义包含执行时间的自定义异常
首先,创建一个继承自RuntimeException的自定义异常类,并添加一个字段来存储执行时间。
import java.time.Duration;
public class TimeMeasuredException extends RuntimeException {
private final Duration executionDuration;
private final Throwable originalCause; // 用于存储原始异常
public TimeMeasuredException(Duration executionDuration, Throwable originalCause) {
super("方法执行异常,耗时: " + executionDuration.toMillis() + " ms. 原始异常: " + originalCause.getMessage(), originalCause);
this.executionDuration = executionDuration;
this.originalCause = originalCause;
}
public Duration getExecutionDuration() {
return executionDuration;
}
public Throwable getOriginalCause() {
return originalCause;
}
}2.2 修改业务逻辑以抛出自定义异常
在业务方法中,使用try-catch块来测量执行时间,并在捕获到异常时,将其封装进TimeMeasuredException并重新抛出。
import org.springframework.stereotype.Service;
import java.time.Duration;
import java.time.Instant;
@Service
public class MyLegacyService {
public String doSomethingWithTimingAndException() {
Instant start = Instant.now();
try {
// 模拟一些业务逻辑
Thread.sleep(300);
if (true) { // 模拟抛出异常的条件
throw new IllegalArgumentException("Invalid input data!");
}
return "Operation successful";
} catch (Exception e) {
Instant end = Instant.now();
Duration executionTime = Duration.between(start, end);
// 封装原始异常和执行时间
throw new TimeMeasuredException(executionTime, e);
}
}
}2.3 ExceptionHandler中处理自定义异常
在全局或局部的@ControllerAdvice中,可以捕获TimeMeasuredException,并从中提取执行时间信息。
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
@ControllerAdvice
public class GlobalExceptionHandler {
private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);
@ExceptionHandler(TimeMeasuredException.class)
public ResponseEntity<ErrorResponse> handleTimeMeasuredException(TimeMeasuredException ex) {
long executionTimeMillis = ex.getExecutionDuration().toMillis();
String originalErrorMessage = ex.getOriginalCause().getMessage();
logger.error("捕获到TimeMeasuredException,方法执行耗时: {} ms. 原始错误: {}",
executionTimeMillis,
originalErrorMessage,
ex);
ErrorResponse errorResponse = new ErrorResponse(
HttpStatus.INTERNAL_SERVER_ERROR.value(),
"操作失败,耗时: " + executionTimeMillis + " ms. 详情: " + originalErrorMessage
);
return new ResponseEntity<>(errorResponse, HttpStatus.INTERNAL_SERVER_ERROR);
}
// 如果希望捕获所有异常并检查是否为TimeMeasuredException
@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResponse> handleGeneralException(Exception e) {
if (e instanceof TimeMeasuredException) {
return handleTimeMeasuredException((TimeMeasuredException) e);
}
logger.error("捕获到通用异常: {}", e.getMessage(), e);
ErrorResponse errorResponse = new ErrorResponse(
HttpStatus.INTERNAL_SERVER_ERROR.value(),
"服务器内部错误:" + e.getMessage()
);
return new ResponseEntity<>(errorResponse, HttpStatus.INTERNAL_SERVER_ERROR);
}
// 假设的错误响应类
static class ErrorResponse {
public int status;
public String message;
public ErrorResponse(int status, String message) {
this.status = status;
this.message = message;
}
}
}2.4 优势与注意事项
- 优势: 与现有ExceptionHandler机制无缝集成;不需要引入AOP框架(如果项目尚未引入);异常信息更丰富,便于调试。
- 注意事项: 业务代码中需要手动添加try-catch块来封装异常,可能导致一定程度的代码重复;如果原始异常类型很重要,需要确保TimeMeasuredException能正确传递或封装它。
总结
在Spring Boot应用中记录方法执行时间并结合异常处理,可以根据具体需求选择不同的策略:
- Spring AOP方案: 适用于需要对大量方法进行统一性能监控和日志记录的场景。它能够实现代码的完全解耦,使业务逻辑保持纯净。通过自定义注解和切点,可以灵活控制监控范围。
- 自定义异常方案: 适用于已经大量使用ExceptionHandler且不希望引入AOP,或者需要在异常处理层直接获取精确执行时间信息的场景。虽然需要在业务代码中手动封装异常,但提供了直接将时间信息传递给ExceptionHandler的途径。
在实际开发中,AOP方案通常被认为是更“Spring Style”和更具扩展性的选择,因为它将横切关注点与业务逻辑清晰分离。然而,如果项目规模较小或有特定限制,自定义异常方案也是一个可行的替代方案。选择哪种方案,应综合考虑项目的架构、团队的熟悉程度以及对代码侵入性的要求。无论选择哪种,关键在于确保执行时间能够被准确测量,并在异常发生时也能被妥善记录和处理。
以上就是本文的全部内容了,是否有顺利帮助你解决问题?若是能给你带来学习上的帮助,请大家多多支持golang学习网!更多关于文章的相关知识,也可关注golang学习网公众号。
Javaifelse条件判断简单实例教学
- 上一篇
- Javaifelse条件判断简单实例教学
- 下一篇
- Transmit秒同步新CSS,测试服颜值实测
-
- 文章 · java教程 | 6分钟前 |
- JavaStream排序技巧与使用方法
- 112浏览 收藏
-
- 文章 · java教程 | 16分钟前 |
- 线程安全懒加载集合技巧分享
- 281浏览 收藏
-
- 文章 · java教程 | 28分钟前 |
- Java实现博客发布逻辑教程详解
- 218浏览 收藏
-
- 文章 · java教程 | 44分钟前 |
- Java构造方法使用技巧分享
- 106浏览 收藏
-
- 文章 · java教程 | 55分钟前 | Java集合 反转顺序
- Java集合反转技巧:Collections.reverse详解
- 358浏览 收藏
-
- 文章 · java教程 | 1小时前 |
- Java方法重写详解与执行机制
- 411浏览 收藏
-
- 文章 · java教程 | 1小时前 |
- Java类与对象基础详解
- 323浏览 收藏
-
- 文章 · java教程 | 1小时前 |
- LibGDX敌人射击机制详解
- 409浏览 收藏
-
- 文章 · java教程 | 1小时前 | java 类加载机制
- Java类加载机制全解析
- 349浏览 收藏
-
- 文章 · java教程 | 1小时前 |
- JavaNIOPath与Files类使用详解
- 385浏览 收藏
-
- 文章 · java教程 | 1小时前 |
- Java加密环境配置与搭建教程
- 356浏览 收藏
-
- 文章 · java教程 | 1小时前 |
- Javaifelse条件判断简单实例教学
- 109浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 485次学习
-
- ChatExcel酷表
- ChatExcel酷表是由北京大学团队打造的Excel聊天机器人,用自然语言操控表格,简化数据处理,告别繁琐操作,提升工作效率!适用于学生、上班族及政府人员。
- 3320次使用
-
- Any绘本
- 探索Any绘本(anypicturebook.com/zh),一款开源免费的AI绘本创作工具,基于Google Gemini与Flux AI模型,让您轻松创作个性化绘本。适用于家庭、教育、创作等多种场景,零门槛,高自由度,技术透明,本地可控。
- 3531次使用
-
- 可赞AI
- 可赞AI,AI驱动的办公可视化智能工具,助您轻松实现文本与可视化元素高效转化。无论是智能文档生成、多格式文本解析,还是一键生成专业图表、脑图、知识卡片,可赞AI都能让信息处理更清晰高效。覆盖数据汇报、会议纪要、内容营销等全场景,大幅提升办公效率,降低专业门槛,是您提升工作效率的得力助手。
- 3563次使用
-
- 星月写作
- 星月写作是国内首款聚焦中文网络小说创作的AI辅助工具,解决网文作者从构思到变现的全流程痛点。AI扫榜、专属模板、全链路适配,助力新人快速上手,资深作者效率倍增。
- 4683次使用
-
- MagicLight
- MagicLight.ai是全球首款叙事驱动型AI动画视频创作平台,专注于解决从故事想法到完整动画的全流程痛点。它通过自研AI模型,保障角色、风格、场景高度一致性,让零动画经验者也能高效产出专业级叙事内容。广泛适用于独立创作者、动画工作室、教育机构及企业营销,助您轻松实现创意落地与商业化。
- 3936次使用
-
- 提升Java功能开发效率的有力工具:微服务架构
- 2023-10-06 501浏览
-
- 掌握Java海康SDK二次开发的必备技巧
- 2023-10-01 501浏览
-
- 如何使用java实现桶排序算法
- 2023-10-03 501浏览
-
- Java开发实战经验:如何优化开发逻辑
- 2023-10-31 501浏览
-
- 如何使用Java中的Math.max()方法比较两个数的大小?
- 2023-11-18 501浏览

