通过BeanPostProcessor统计每一个Bean初始化耗时
思路:
- 统计Bean耗时的基础数据结构是个Map,Map的键是Bean的名称,值是初始化耗费时间。
- postProcessBeforeInitialization在Bean初始化之前执行,因此记录初始化开始时间
- postProcessAfterInitialization在Bean初始化之后执行,因此通过初始化结束时间减去初始化开始时间得出初始化耗时
- 通过监听ContextRefreshedEvent事件判断容器加载完毕,将最耗时的K个Bean日志输出。
通过优先级队列构建小顶堆获取动态添加数列的最大TopK个值
使用PriorityQueue构建TopK算法找出容器中初始化最耗时的K个Bean
思路:
- 添加新数据进来,将新数据和堆顶的最小值进行比较
- 新数据比堆顶最小值大,新数据得到晋升,抛弃堆顶最小值
- 新数据比堆顶最小值小,忽略新数据,堆保持不变
代码示例
使用BeanPostProcessor统计每个Bean的耗时
public class LoggerBeanLoadCostPostProcessor implements BeanPostProcessor, Ordered, ApplicationListener<ContextRefreshedEvent> {
private Map<String, Long> cost = new HashMap<>(500);
private static final int TOPK = 5;
private static AtomicInteger beanCount = new AtomicInteger(0);
private TopK<CostInfo> topK = new TopK<>(TOPK);
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
cost.put(beanName, Instant.now().toEpochMilli());
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
beanCount.incrementAndGet();
Long startStamp = cost.get(beanName);
if (startStamp != null) {
Long currentCost = Instant.now().toEpochMilli