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

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》

相关文章:

Spring 容器启动耗时统计

为了了解 Spring 为什么会启动那么久&#xff0c;于是看了看怎么统计一下加载 Bean 的耗时。 极简版 几行代码搞定。 import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanPostProcessor;import java.util.HashMap; imp…...

1. 优化算法学习

参考文献 1609&#xff1a;An overview of gradient descent optimization algorithms 从 SGD 到 Adam —— 深度学习优化算法概览(一) - 知乎 机器学习札记 - 知乎...

再获荣誉丨通付盾WAAP解决方案获“金鼎奖”优秀金融科技解决方案

今年四月&#xff0c;2023中国国际金融展在首钢会展中心成功落下帷幕。中国国际金融展作为金融开放创新成果的展示、交流、传播平台&#xff0c;历经多年发展,已成为展示中国金融发展成就、宣传金融改革成果、促进金融产业创新和推动金融信息化发展的有效平台。 “金鼎奖”评选…...

【腾讯云 TDSQL-C Serverless 产品测评】“橡皮筋“一样的数据库『MySQL高压篇』

【腾讯云 TDSQL-C Serverless 产品测评】"橡皮筋"一样的数据库 活动介绍服务一览何为TDSQL &#xff1f;Serverless 似曾相识&#xff1f; 降本增效&#xff0c;不再口号&#xff1f;动手环节 --- "压力"山大实验前瞻稍作简介资源扩缩范围&#xff08;CCU&…...

python http文件上传

server端代码 import os import cgi from http.server import SimpleHTTPRequestHandler, HTTPServer# 服务器地址和端口 host = 0.0.0.0 port = 8080# 处理文件上传的请求 class FileUploadHandler(SimpleHTTPRequestHandler):def do_POST(self):# 解析多部分表单数据form = …...

Android学习之路(9) Intent

Intent 是一个消息传递对象&#xff0c;您可以用来从其他应用组件请求操作。尽管 Intent 可以通过多种方式促进组件之间的通信&#xff0c;但其基本用例主要包括以下三个&#xff1a; 启动 Activity Activity 表示应用中的一个屏幕。通过将 Intent 传递给 startActivity()&…...

vue项目配置git提交规范

vue项目配置git提交规范 一、背景介绍二、husky、lint-staged、commitlint/cli1.husky2.lint-staged3.commitlint/cli 三、具体使用1.安装依赖2.运行初始化脚本3.在package.json中配置lint-staged4.根目录新增 commitlint.config.js 4.提交测试1.提示信息格式错误时2.eslint校验…...

影响交叉导轨运行速度的因素有哪些?

交叉导轨具有精度高&#xff0c;速度快&#xff0c;承载能力大、结构简单等特点&#xff0c;被广泛应用在固晶机、点胶设备、自动化设备、OA机器及其周边机器、测定器、印刷基板开孔机&#xff0c;精密机器&#xff0c;光学测试仪、光学工作台、操纵机构、X 射缐装置等的滑座部…...

List转Map

一、list转map Map<Long, User> maps userList.stream().collect(Collectors.toMap(User::getId,Function.identity())); 看来还是使用JDK 1.8方便一些。 二、另外&#xff0c;转换成map的时候&#xff0c;可能出现key一样的情况&#xff0c;如果不指定一个覆盖规则&…...

ES:一次分片设计问题导致的故障

### 现象&#xff1a; 1. 单节点CPU持续高 2.写入骤降 3.线程池队列积压&#xff0c;但没有reject 4.使用方没有记录日志 ### 排查 1.ES监控 只能看到相应的结果指标&#xff0c;无法反应出原因。 2.ES日志&#xff1a;大量日志打印相关异常&#xff08;routate等调用栈&a…...

vue 简单实验 自定义组件 综合应用 传参数 循环

1.代码 <script src"https://unpkg.com/vuenext" rel"external nofollow" ></script> <div id"todo-list-app"><ol><!--现在我们为每个 todo-item 提供 todo 对象todo 对象是变量&#xff0c;即其内容可以是动态的。…...

【OpenCV实战】2.OpenCV基本数据类型实战

OpenCV基本数据类型实战 〇、实战内容1 OpenCV helloworld1.1 文件结构类型1.2 CMakeList.txt1.3 Helloworld 2. Image的基本操作3. OpenCV 基本数据类型4. 读取图片的像素 & 遍历图片4.1 获取制定像素4.2 遍历图片 5. 图片反色5.1 方法1 &#xff1a;遍历5.2 方法2 &#…...

MyBatis进阶:告别SQL注入!MyBatis分页与特殊字符的正确使用方式

目录 引言 一、使用正确的方式实现分页 1.1.什么是分页 1.2.MyBatis中的分页实现方式 1.3.避免SQL注入的技巧 二、特殊字符的正确使用方式 2.1.什么是特殊字符 2.2.特殊字符在SQL查询中的作用 2.3.如何避免特殊字符引起的问题 2.3.1.使用CDATA区段 2.3.2.使用实体引…...

安装Node(脚手架)

目录 一&#xff0c;安装node&#xff08;脚手架&#xff09;1.1&#xff0c; 配置vue.config.js1.2&#xff0c; vue-cli3x的目录介绍1.3&#xff0c; package.json 最后 一&#xff0c;安装node&#xff08;脚手架&#xff09; 从官网直接下载安装即可&#xff0c;自带npm包管…...

R语言10-R语言中的循环结构

在R语言中&#xff0c;有几种常用的循环结构&#xff0c;可以用来多次执行特定的代码块。以下是其中的两种主要循环结构&#xff1a; for循环&#xff1a; for 循环用于按照一定的步长迭代一个序列&#xff0c;通常用于执行固定次数的循环。 for (i in 1:5) {print(i) }while…...

【Spring】一次性打包学透 Spring | 阿Q送书第五期

文章目录 如何竭尽可能确保大家学透Spring1. 内容全面且细致2. 主题实用且本土化3. 案例系统且完善4. 知识有趣且深刻 关于作者丁雪丰业内专家推图书热卖留言提前获赠书 不知从何时开始&#xff0c;Spring 这个词开始频繁地出现在 Java 服务端开发者的日常工作中&#xff0c;很…...

第 7 章 排序算法(4)(插入排序)

7.7插入排序 7.7.1插入排序法介绍: 插入式排序属于内部排序法&#xff0c;是对于欲排序的元素以插入的方式找寻该元素的适当位置&#xff0c;以达到排序的目的。 7.7.2插入排序法思想: 插入排序&#xff08;Insertion Sorting&#xff09;的基本思想是&#xff1a;把n个待排…...

JavsScript知识框架

JavaScript学习框架性总结 要系统性地精通 JavaScript&#xff0c;需要涵盖广泛的知识点&#xff0c;从基础到高级。以下是一些需要掌握的关键知识点&#xff08;当然不止这些&#xff09;&#xff1a; 基础语法和核心概念&#xff1a; 变量、数据类型、运算符作用域闭包this …...

el-input添加自定义指令只允许输入中文/英文/数字,兼容输入法事件

省流 script: directives: {regexp: {inserted: (el, binding, vnode) > {let composition falseconst formatValue function (e) {if (composition) return// vnode.componentInstance组件实例vnode.componentInstance.$emit(input, e.target.value.replace(/[^\u4e00-…...

0基础学习VR全景平台篇 第89篇:智慧眼-安放热点

一、功能说明 安放热点&#xff0c;是智慧眼成员们正式进入城市化管理的第一步&#xff0c;即发现问题后以安放热点的形式进行标记&#xff0c;再由其他的角色成员对该热点内容作出如核实、处理、确认完结等操作&#xff08;具体流程根据项目实际情况而定&#xff09;。 二、…...

C++初阶-list的底层

目录 1.std::list实现的所有代码 2.list的简单介绍 2.1实现list的类 2.2_list_iterator的实现 2.2.1_list_iterator实现的原因和好处 2.2.2_list_iterator实现 2.3_list_node的实现 2.3.1. 避免递归的模板依赖 2.3.2. 内存布局一致性 2.3.3. 类型安全的替代方案 2.3.…...

基于FPGA的PID算法学习———实现PID比例控制算法

基于FPGA的PID算法学习 前言一、PID算法分析二、PID仿真分析1. PID代码2.PI代码3.P代码4.顶层5.测试文件6.仿真波形 总结 前言 学习内容&#xff1a;参考网站&#xff1a; PID算法控制 PID即&#xff1a;Proportional&#xff08;比例&#xff09;、Integral&#xff08;积分&…...

多场景 OkHttpClient 管理器 - Android 网络通信解决方案

下面是一个完整的 Android 实现&#xff0c;展示如何创建和管理多个 OkHttpClient 实例&#xff0c;分别用于长连接、普通 HTTP 请求和文件下载场景。 <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas…...

Qt Widget类解析与代码注释

#include "widget.h" #include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this); }Widget::~Widget() {delete ui; }//解释这串代码&#xff0c;写上注释 当然可以&#xff01;这段代码是 Qt …...

什么是库存周转?如何用进销存系统提高库存周转率?

你可能听说过这样一句话&#xff1a; “利润不是赚出来的&#xff0c;是管出来的。” 尤其是在制造业、批发零售、电商这类“货堆成山”的行业&#xff0c;很多企业看着销售不错&#xff0c;账上却没钱、利润也不见了&#xff0c;一翻库存才发现&#xff1a; 一堆卖不动的旧货…...

React19源码系列之 事件插件系统

事件类别 事件类型 定义 文档 Event Event 接口表示在 EventTarget 上出现的事件。 Event - Web API | MDN UIEvent UIEvent 接口表示简单的用户界面事件。 UIEvent - Web API | MDN KeyboardEvent KeyboardEvent 对象描述了用户与键盘的交互。 KeyboardEvent - Web…...

Nuxt.js 中的路由配置详解

Nuxt.js 通过其内置的路由系统简化了应用的路由配置&#xff0c;使得开发者可以轻松地管理页面导航和 URL 结构。路由配置主要涉及页面组件的组织、动态路由的设置以及路由元信息的配置。 自动路由生成 Nuxt.js 会根据 pages 目录下的文件结构自动生成路由配置。每个文件都会对…...

SpringTask-03.入门案例

一.入门案例 启动类&#xff1a; package com.sky;import lombok.extern.slf4j.Slf4j; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cache.annotation.EnableCach…...

精益数据分析(97/126):邮件营销与用户参与度的关键指标优化指南

精益数据分析&#xff08;97/126&#xff09;&#xff1a;邮件营销与用户参与度的关键指标优化指南 在数字化营销时代&#xff0c;邮件列表效度、用户参与度和网站性能等指标往往决定着创业公司的增长成败。今天&#xff0c;我们将深入解析邮件打开率、网站可用性、页面参与时…...

初学 pytest 记录

安装 pip install pytest用例可以是函数也可以是类中的方法 def test_func():print()class TestAdd: # def __init__(self): 在 pytest 中不可以使用__init__方法 # self.cc 12345 pytest.mark.api def test_str(self):res add(1, 2)assert res 12def test_int(self):r…...