当前位置: 首页 > news >正文

Spring Bean加载耗时采集工具

功能介绍

Target:针对启动慢的 Spring 应用,找出 IOC 容器启动过程中,加载耗时较长的 Bean 对象进行治理。



实现原理

主要用到Spring本身提供的两个扩展接口:BeanPostProcessor ApplicationListener

这两个接口,在Spring框架中,还是比较常见的组件,原理和使用方式自提:

Spring中的BeanPostProcessor接口、Spring监听器用法与原理详解

代码示例

@Component
public class SpringBeanLoadTimeCollectorimplements BeanPostProcessor, ApplicationListener<ContextRefreshedEvent> {private final Logger logger = LoggerFactory.getLogger(this.getClass());private final ConcurrentMap<String, Instant> beanCreationStartTime = new ConcurrentHashMap<>();// Thread-safe and orderedprivate final Map<String, Long> beanLoadTime = Collections.synchronizedMap(new LinkedHashMap<>());private static final String logFileName = "springbean-loadtime.log";@Overridepublic Object postProcessBeforeInitialization(@NotNull Object bean, @NotNull String beanName) {// 环境变量方式:// java -Dproject.package.prefix=项目下的包路径前缀 -jar your-app.jar// System.getProperty("project.package.prefix", "项目下的包路径前缀")// The prefix of the package path under the projectif (bean.getClass().getPackage().getName().startsWith("com.alibaba")) {beanCreationStartTime.putIfAbsent(beanName, Instant.now());}return bean;}@Overridepublic Object postProcessAfterInitialization(@NotNull Object bean, @NotNull String beanName) {Instant startTime = beanCreationStartTime.get(beanName);if (startTime != null) {Instant endTime = Instant.now();Duration duration = Duration.between(startTime, endTime);beanLoadTime.put(beanName, duration.toMillis());}return bean;}@Overridepublic void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {// Ensure that we are in the root contextif (contextRefreshedEvent.getApplicationContext().getParent() == null) {// Now that the context is fully loaded, print the bean load time mapwriteBeanLoadTimeLog();}}private void writeBeanLoadTimeLog() {BeanLoadTimeResult result = new BeanLoadTimeResult(beanLoadTime);// Write the results to a log filedoWriteLog(result.toString());}private void doWriteLog(String log) {try (BufferedWriter writer = new BufferedWriter(new FileWriter(logFileName))) {writer.write(log);} catch (IOException e) {logger.error("Error writing bean load times to file error", e);}}public static class BeanLoadTimeResult {public final int count;public final String unit;public final Map<String, Long> beanLoadTimeMap;BeanLoadTimeResult(Map<String, Long> beanLoadTimeMap) {this.unit = "ms";this.count = beanLoadTimeMap.size();this.beanLoadTimeMap = beanLoadTimeMap;}@Overridepublic String toString() {return JSON.toJSONString(this);}}
}

采集结果

{"beanLoadTime": {"beanName_1": 10, // bean名称和加载耗时时间(单位毫秒)"beanName_2": 200,"beanName_3": 102,"beanName_4": 59,......},"total": 267, // 工程中被Spring托管的总Bean数量"unit": "ms"
}

补充知识

  • Bean 异步加载

Spring官方不推荐,核心应用不建议使用(已踩坑)。但是从使用效果看,这个可以明显提升启动速度!

  • Bean 并行加载

看过几篇相关帖子,方案是可行的,不过目前还没有落地实现。

Bean并行加载的难点:

Bean之间的循环依赖关系如何在并行加载的同时保证初始化、实例化准确无误?

对于多个Bean的复杂依赖关系(例如树状结构),在并行加载时,要考虑到多个线程之间共享一份树的遍历索引,避免重复加载。

  • Bean 懒加载

Spring Framework 5.2 和 Spring Boot 2.2 引入了 improve 全局懒初始化,可以减少启动时的 CPU 和内存开销,进而提升启动速度。

可以通过下面的配置启用:

spring.main.lazy-initialization=true


http://www.mrgr.cn/news/15632.html

相关文章:

  • python-带空格的数字层三角形
  • macos 系统 降级, 重装, 升级图文教程
  • 论文阅读——Compact Single-Feed Dual-Mode Antenna for Active RFID Tag Application
  • 华硕天选Air:开学季的性价比之巅
  • 无缝 CI/CD:如何在 Windows 环境中使用 Docker 和 Jenkins 自动化部署 .NET 应用
  • 数学符号-西格玛
  • 文件上传漏洞详解(持续更新…)
  • 加油卡系统开发,为什么加油卡系统这么受欢迎
  • Unity SceneView 相机聚焦到指定位置
  • Docker续5:docker部署nmt(mysql,nginx,tomcat)
  • 精选算法编程题
  • 《炉石传说》国服9月25日回归 预约活动上线:送金卡、皮肤
  • 金融上云方案中,国产虚拟化产品选型的重点考虑因素、自动化运维建设步骤及如何进行保障数据中心安全需求的存储设计等问题及解决方法|金融行业数字化QA合集③
  • 四通道非洲猪瘟检测仪
  • 小爱同学与大模型的完美融合:开启智能交互新时代
  • 深度学习分类模型训练代码模板
  • 【C语言从不挂科到高绩点】05-流程控制语句-switch语句
  • 【机器学习】支持向量机(SVM)的对偶性、核方法以及核技巧
  • LED的使用寿命评估
  • 值得收藏!这6招教你怎么管理客户!