Spring容器启动耗时分析 (Bean启动耗时)
1. 引入Spring容器启动耗时过长, 主要原因是有些Bean做了复杂初始化的工作.找到这些Bean, 再做针对性的优化, 才能把Spring容器启动耗时降下来.如果开发对应用代码还不太熟悉, 本文提供了一种方式, 快速找出这些启动非常耗时的Bean.2. 源码LaunchTimeBeanPostProcessor.java (Bean启动时间抓取)import org.springframew
·
1. 引入
Spring容器启动耗时过长, 主要原因是有些Bean做了复杂初始化的工作.
找到这些Bean, 再做针对性的优化, 才能把Spring容器启动耗时降下来.
如果开发对应用代码还不太熟悉, 本文提供了一种方式, 快速找出这些启动非常耗时的Bean.
2. 源码
- LaunchTimeBeanPostProcessor.java (Bean启动时间抓取)
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor;
import org.springframework.beans.factory.support.MergedBeanDefinitionPostProcessor;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.stereotype.Component;
/**
* 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
*
* @author liuxianqiang
* @since 2021/11/17
*/
@Component
public class LaunchTimeBeanPostProcessor implements BeanPostProcessor, InstantiationAwareBeanPostProcessor, MergedBeanDefinitionPostProcessor {
@Autowired
private LaunchTimeManager launchInfoManager;
@Override
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
launchInfoManager.beanStart(beanName, System.currentTimeMillis());
return null;
}
@Override
public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
return true;
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
launchInfoManager.beanEnd(beanName, System.currentTimeMillis());
return bean;
}
@Override
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
// ignore
}
}
- LaunchTimeManager.java (Bean启动时间管理器)
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Component;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
/**
* Bean启动时间管理器
*
* @author liuxianqiang
* @since 2021/11/17
*/
@Component
public class LaunchTimeManager implements ApplicationListener<ContextRefreshedEvent> {
private Map<String, LaunchTime> launchTimeMap = new ConcurrentHashMap<>();
public void beanStart(String beanName, Long startTime) {
if (Objects.nonNull(launchTimeMap.get(beanName))) {
System.out.printf("------------%s Bean重复启动 ???-------------\n", beanName);
return;
}
LaunchTime launchTime = new LaunchTime();
launchTime.setBeanName(beanName);
launchTime.setStartTime(startTime);
launchTimeMap.putIfAbsent(beanName, launchTime);
}
public void beanEnd(String beanName, Long endTime) {
if (Objects.isNull(launchTimeMap.get(beanName))) {
System.out.printf("------------%s Bean还未启动 ???-------------\n", beanName);
return;
}
launchTimeMap.computeIfPresent(beanName, (k, v) -> {
v.setEndTime(endTime);
return v;
});
}
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
System.out.println("Spring容器启动完成");
launchTimeMap.values().stream()
.sorted((e1, e2) -> Long.valueOf(e2.getCostTime() - e1.getCostTime()).intValue())
.limit(10)
.forEach(e -> {
System.out.printf("启动耗时, beanName:%s, cost:%s \n", e.getBeanName(), e.getCostTime());
});
}
/**
* Bean启动时间
*/
public static class LaunchTime {
private String beanName;
private Long startTime;
private Long endTime;
public String getBeanName() {
return beanName;
}
public void setBeanName(String beanName) {
this.beanName = beanName;
}
public Long getStartTime() {
return startTime;
}
public void setStartTime(Long startTime) {
this.startTime = startTime;
}
public Long getEndTime() {
return endTime;
}
public void setEndTime(Long endTime) {
this.endTime = endTime;
}
public Long getCostTime() {
return endTime - startTime;
}
}
}
3. 原理
Spring Bean 生命周期
4. 测试
- 控制台打印
- 测试代码
更多推荐
已为社区贡献1条内容
所有评论(0)