Spring 容器启动耗时统计

为了了解 Spring 为什么会启动那么久,于是看了看怎么统计一下加载 Bean 的耗时。

极简版

几行代码搞定。

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;import java.util.HashMap;
import java.util.Map;public class SpringBeanAnalyse implements BeanPostProcessor {private static final Map<String, Long> mapBeanTime = new HashMap<>();@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {mapBeanTime.put(beanName, System.currentTimeMillis());return bean;}@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {Long begin = mapBeanTime.get(beanName);if (begin != null) {long ell = System.currentTimeMillis() - begin;System.out.println(beanName + " 耗时: " + ell);}return bean;}
}

使用方法:

@Bean
SpringBeanAnalyse SpringBeanAnalyse() {return new SpringBeanAnalyse();
}

效果如图:
在这里插入图片描述
问题是没有排序,看比较费劲。

高配版

于是,高配版出场了。它更为成熟壮健,并有排序功能。

在这里插入图片描述

原理

Bean 启动时间抓取,主要是围绕 Spring Bean 生命周期。BeanPostProcessor 相关方法

  1. postProcessBeforeInstantiation: 实例化前
  2. postProcessAfterInstantiation: 实例化后
  3. postProcessBeforeInitialization: 初始化前
  4. postProcessAfterInitialization: 初始化后

注意:实现MergedBeanDefinitionPostProcessor, 主要是为了调整当前 BeanPostProcessor的执行顺序到最后, 具体参考BeanPostProcessor注册流程

org.springframework.context.support.AbstractApplicationContext#refresh
org.springframework.context.support.AbstractApplicationContext#registerBeanPostProcessors
org.springframework.context.support.PostProcessorRegistrationDelegate#registerBeanPostProcessors

,详见
在这里插入图片描述

源码

首先是一个 Bean。

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;@Builder
@Data
@AllArgsConstructor
@NoArgsConstructor
class Statistics {private String beanName;private long beforeInstantiationTime;private long afterInstantiationTime;private long beforeInitializationTime;private long afterInitializationTime;public long calculateTotalCostTime() {return calculateInstantiationCostTime() + calculateInitializationCostTime();}public long calculateInstantiationCostTime() {return afterInstantiationTime - beforeInstantiationTime;}public long calculateInitializationCostTime() {return afterInitializationTime - beforeInitializationTime;}public String toConsoleString() {return "\t" + getBeanName() + "\t" + calculateTotalCostTime() + "\t\n";}
}

StartupTimeMetric源码:

import com.ajaxjs.util.logger.LogHelper;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor;
import org.springframework.beans.factory.support.MergedBeanDefinitionPostProcessor;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.core.PriorityOrdered;import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;/*** 用于调优的处理器*/
public class StartupTimeMetric implements InstantiationAwareBeanPostProcessor, PriorityOrdered, ApplicationListener<ContextRefreshedEvent>, MergedBeanDefinitionPostProcessor {private static final LogHelper LOGGER = LogHelper.getLog(StartupTimeMetric.class);private final Map<String, Statistics> statisticsMap = new TreeMap<>();/*** InstantiationAwareBeanPostProcessor 中自定义的方法 在方法实例化之前执行 Bean 对象还没有*/@Overridepublic Object postProcessBeforeInstantiation(Class beanClass, String beanName) throws BeansException {String beanClassName = beanClass.getName();Statistics s = Statistics.builder().beanName(beanClassName).build();s.setBeforeInstantiationTime(System.currentTimeMillis());statisticsMap.put(beanClassName, s);return null;}/*** InstantiationAwareBeanPostProcessor 中自定义的方法 在方法实例化之后执行 Bean 对象已经创建出来了*/@Overridepublic boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {String beanClassName = bean.getClass().getName();Statistics s = statisticsMap.get(beanClassName);if (s != null)s.setAfterInstantiationTime(System.currentTimeMillis());return true;}/*** BeanPostProcessor 接口中的方法 在 Bean 的自定义初始化方法之前执行 Bean 对象已经存在了*/@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {String beanClassName = bean.getClass().getName();Statistics s = statisticsMap.getOrDefault(beanClassName, Statistics.builder().beanName(beanClassName).build());s.setBeforeInitializationTime(System.currentTimeMillis());statisticsMap.putIfAbsent(beanClassName, s);return bean;}/*** BeanPostProcessor 接口中的方法 在 Bean 的自定义初始化方法执行完成之后执行 Bean 对象已经存在了*/@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {String beanClassName = bean.getClass().getName();Statistics s = statisticsMap.get(beanClassName);if (s != null)s.setAfterInitializationTime(System.currentTimeMillis());return bean;}@Overridepublic int getOrder() {return PriorityOrdered.HIGHEST_PRECEDENCE;}private static final AtomicBoolean START_LOCK = new AtomicBoolean(false);@Overridepublic void onApplicationEvent(ContextRefreshedEvent event) {LOGGER.info("Spring 容器启动完成");if (START_LOCK.compareAndSet(false, true)) {List<Statistics> sList = statisticsMap.values().stream().sorted(Comparator.comparing(Statistics::calculateTotalCostTime).reversed()).collect(Collectors.toList());StringBuilder sb = new StringBuilder();sList.forEach(_s -> sb.append(_s.toConsoleString()));LOGGER.info("ApplicationStartupTimeMetric:\n" + sb);}}@Overridepublic void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {}
}

参见《应用启动加速-并发初始化spring bean》

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/107982.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

语言基础篇1——Python概述,Python是什么?Python能干什么?

概述 简介 Python&#xff0c;计算机高级语言&#xff0c;读作/ˈpaɪθən/&#xff08;英音&#xff09;、/ˈpaɪθɑːn/&#xff08;美音&#xff09;&#xff0c;意为蟒蛇&#xff0c;Python的logo为两条缠绕的蟒蛇 特点 Python以开发效率高而运行效率低著称 应用领域…

一键实现 Oracle 数据整库同步至 Apache Doris

在实时数据仓库建设或迁移的过程中&#xff0c;用户必须考虑如何高效便捷将关系数据库数据同步到实时数仓中来&#xff0c;Apache Doris 用户也面临这样的挑战。而对于从 Oracle 到 Doris 的数据同步&#xff0c;通常会用到以下两种常见的同步方式&#xff1a; OGG/XStream/Lo…

加油站抽烟烟火智能识别算法

加油站抽烟烟火智能识别系统通过yoloopencv网络模型图像识别分析技术&#xff0c;加油站抽烟烟火智能识别算法识别出抽烟和燃放烟火的情况&#xff0c;并发出预警信号以提醒相关人员&#xff0c;减少火灾风险。OpenCV基于C实现&#xff0c;同时提供python, Ruby, Matlab等语言的…

Linux(多进程与多线程)

目录 1、进程与线程概念 1.1 进程 1.2 线程 1.3 进程与线程区别 2、多进程 2.1多进程概念 2.2 进程相关API 2.3 多进程编程 3、多线程 3.1 多线程概念 3.2 多线程相关API 3.3 多线程编程 1、进程与线程概念 1.1 进程 在计算机科学中&#xff0c;进程是正在执行中…

不同版本.net引用同一个项目

项目文件.csproj文件内容如下&#xff1a; 重点是&#xff1a;不能有其他的 netstandard2;net40;net45;net46;net6 <Project Sdk"Microsoft.NET.Sdk"><PropertyGroup><TargetFrameworks>netstandard2;net40;net45;net46;net6</TargetFrame…

2023.08.27 学习周报

文章目录 摘要文献阅读1.题目2.重点3.引言4.方法5.实验结果6.结论 深度学习Majorization-Minimization算法1.基本思想2.要求3.示意图 总结 摘要 This week, I read a computer science on the prediction of atmospheric pollutants in urban environments based on coupled d…

Spring MVC详解

文章目录 一、SpringMVC1.1 引言1.2 MVC架构1.2.1 概念1.2.2 好处 二、开发流程2.1 导入依赖2.2 配置核心(前端)控制器2.3 后端控制器2.4 配置文件2.5 访问 三、接收请求参数3.1 基本类型参数3.2 实体收参【重点】3.3 数组收参3.4 集合收参 【了解】3.5 路径参数3.6 中文乱码 四…

postgresql 条件表达式

postgresql 条件表达式 简单CASE表达式搜索CASE表达式缩写函数nullif函数示例 coalesce函数 总结 简单CASE表达式 语法如下 case 表达式when 值1 then 结果1when 值2 then 结果2else 默认值 end;select e.first_name , e.last_name , case e.department_id when 90 then 管…

搭建web网站

1.基于域名www.openlab.com可以访问网站内容为welcome to openlab!!! (1).安装所需软件HTTPD、mod_ssl [rootserver ~]# yum install httpd mod_ssl -y 添加域名映射&#xff1a;vim /etc/hosts (2)创建网站目录及网页&#xff0c;修改主配置文件新建openlab目录网站 配置文…

java.lang.NullPointerException: null 不显示异常栈

一、问题 排查线上问题时&#xff0c;发现日志中异常输出的地方&#xff0c;仅有一行java.lang.NullPointerException: null&#xff0c;截图如下。 丢失了具体的异常栈&#xff0c;导致无法定位是哪行代码抛出了异常。 这里排除日志用法的问题&#xff0c;以前是正常能输出异…

Android BatteryManager的使用及BatteryService源码分析

当需要监控系统电量时&#xff0c;用 BatteryManager 来实现。 参考官网 监控电池电量和充电状态 获取电池信息 通过监听 Intent.ACTION_BATTERY_CHANGED 广播实现&#xff0c;在广播接收器中获取电池信息。 这是个粘性广播&#xff0c;即使过了广播发出的时间点后再注册广…

大数据Flink(六十六):Flink的重要概念和小结

文章目录 Flink的重要概念和小结 一、​​​​​​​​​​​​​​数据流图(Dataflow Graph)

尝试自主打造一个有限状态机(二)

前言 上一篇文章我们从理论角度去探索了状态机的定义、组成、作用以及设计&#xff0c;对状态机有了一个基本的认识&#xff0c;这么做有利于我们更好地去分析基于实际应用的状态机&#xff0c;以及在自主设计状态机时也能更加地有条不紊。本篇文章将从状态机的实际应用出发&am…

wazuh环境配置及案例复现

文章目录 wazuh环境配置及案例复现wazuh环境配置 案例复现 wazuh环境配置及案例复现 wazuh环境配置 进入官网下载ova软件 https://documentation.wazuh.com/current/deployment-options/virtual-machine/virtual-machine.html 打开下载的ova&#xff0c;密码和用户名会显示…

sql developer 连不上oracle数据库 报错 ORA-01031: insufficient privileges

sql developer 连不上oracle数据库 报错 ORA-01031: insufficient privileges 1、问题描述2、问题原因3、解决方法4、sql developer 连接oracle 成功 1、问题描述 使用sys账户以SYSDBA角色登录失败 报错 ORA-01031: insufficient privileges 2、问题原因 因为没有给sys账户分…

【Go 基础篇】Go语言中的defer关键字:延迟执行与资源管理

介绍 在Go语言中&#xff0c;defer 是一种用于延迟执行函数调用的关键字。它提供了一种简洁而强大的方式&#xff0c;用于在函数返回之前执行一些必要的清理操作或者释放资源。defer 的灵活性和易用性使得它在Go语言中广泛应用于资源管理、错误处理和代码结构优化等方面。&…

2023年高教社杯数学建模思路 - 复盘:人力资源安排的最优化模型

文章目录 0 赛题思路1 描述2 问题概括3 建模过程3.1 边界说明3.2 符号约定3.3 分析3.4 模型建立3.5 模型求解 4 模型评价与推广5 实现代码 建模资料 0 赛题思路 &#xff08;赛题出来以后第一时间在CSDN分享&#xff09; https://blog.csdn.net/dc_sinor?typeblog 1 描述 …

手把手教你用 ANSYS workbench

ANSYS Workbench ANSYS Workbench是一款基于有限元分析&#xff08;FEA&#xff09;的工程仿真软件。其基本概念包括&#xff1a; 工作区&#xff08;Workspace&#xff09;&#xff1a;工程仿真模块都在此区域内&#xff0c;包括几何建模、网格划分、边界条件设置、分析求解等…

Ubuntu-Server 22.04安装详细过程-图文版

一.下载Ubuntu Server镜像&#xff0c;官方地址下载即可 https://ubuntu.com/download/server 乌班图镜像网址&#xff0c;点击下载即可 二.安装乌班图镜像&#xff0c;最好自己准备u盘在ISO软件内制作完成 1.选择 Install Ubuntu Server 2.选择安装语言为英语 3.安装程序更新选…

C#,《小白学程序》第二课:数组与排序

1 文本格式 /// <summary> /// 《小白学程序》第二课&#xff1a;数组与排序 /// </summary> /// <param name"sender"></param> /// <param name"e"></param> private void button2_Click(object sender, EventArgs …