Spring容器啟動耗時分析 (Bean啟動耗時)
VSole2022-04-23 15:05:46
spring bean 的生命周期
- 實例化(instantiate), 用構造器創建一個對象
- 字段賦值(populate)
- 初始化(initialize), 執行bean配置里的init方法或者
InitializingBean#afterPropertiesSet方法 - 銷毀(destruct)
實例化和字段賦值一般都很快,但是一些重型的bean被IOC容器創建時,需要調用遠程服務或者執行耗時的操作,這些操作往往在init方法里實現。統計bean初始化耗時可以發現那些bean影響了系統的啟動效率。業務方的bean可以推動業務優化,自己的bean也可以想方法優化性能。
那么如何統計初始化的耗時呢?
spring bean初始化源碼分析
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#initializeBean 觀察執行初始化方法的邏輯
protected Object initializeBean(final String beanName, final Object bean, RootBeanDefinition mbd) {
if (System.getSecurityManager() != null) {
AccessController.doPrivileged(new PrivilegedAction() {
@Override
public Object run() {
invokeAwareMethods(beanName, bean);
return null;
}
}, getAccessControlContext());
}
else {
invokeAwareMethods(beanName, bean);
}
Object wrappedBean = bean;
if (mbd == null || !mbd.isSynthetic()) {
// 初始化前spring提供的系統鉤子
wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
}
try {
// 執行初始化方法
invokeInitMethods(beanName, wrappedBean, mbd);
}
catch (Throwable ex) {
throw new BeanCreationException(
(mbd != null ? mbd.getResourceDescription() : null),
beanName, "Invocation of init method failed", ex);
}
if (mbd == null || !mbd.isSynthetic()) {
// 初始化后spring提供的系統鉤子
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
}
return wrappedBean;
}
applyBeanPostProcessorsBeforeInitialization做了什么?
取出所有實現BeanPostProcessor的bean,逐個執行一遍postProcessBeforeInitialization方法。
同理,applyBeanPostProcessorsAfterInitialization邏輯依然,只是執行的是postProcessInitialization方法。
@Override
public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName)
throws BeansException {
Object result = existingBean;
for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {
result = beanProcessor.postProcessBeforeInitialization(result, beanName);
if (result == null) {
return result;
}
}
return result;
spring系統鉤子 BeanPostProcessor
Factory hook that allows for custom modification of new bean instances, e.g. checking for marker interfaces or wrapping them with proxies.
ApplicationContexts can autodetect BeanPostProcessor beans in their bean definitions and apply them to any beans subsequently created. Plain bean factories allow for programmatic registration of post-processors, applying to all beans created through this factory
BeanPostProcessor接口僅僅提供兩個方法,用在在初始化bean的時候進行定制開發。
public interface BeanPostProcessor {
Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
}
bean初始化耗時功能開發demo
簡單demo
package org.dubbo.server.service.tool;
import com.google.common.collect.Maps;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;
import java.util.Map;
/**
* @author LvSheng
**/
@Component
public class TimeCostBeanPostProcessor implements BeanPostProcessor {
Map costMap = Maps.newConcurrentMap();
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
costMap.put(beanName, System.currentTimeMillis());
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
long start = costMap.get(beanName);
long cost = System.currentTimeMillis() - start;
if (cost > 0) {
costMap.put(beanName, cost);
System.out.println("class: " + bean.getClass().getName()
+ "\tbean: " + beanName
+ "\ttime" + cost);
}
return bean;
}
}
推薦
主流Java進階技術(學習資料分享)
Java面試題寶典
加入Spring技術開發社區
PS:因為公眾號平臺更改了推送規則,如果不想錯過內容,記得讀完點一下“在看”,加個“星標”,這樣每次新文章推送才會第一時間出現在你的訂閱列表里。點“在看”支持我們吧!
VSole
網絡安全專家