如果你的系统启动耗时250s以上,文章思路应该可以帮到你。
一、背景
近期,在做应用启动提速相关工作的过程中,我们发现,应用启动速度主要的瓶颈在于bean的初始化过程(init,afterPropertiesSet方法的耗时)。很多中间件bean的初始化逻辑涉及到网络io,且在没有相互依赖的情况下串行执行。将这一部分中间件bean进行异步加载,是提升启动速度的一个探索方向。
二、解决方案
- 自动扫描可批量异步的中间件bean,而后,在bean的初始化阶段利用线程池并行执行其初始化逻辑。
- 允许使用方自行配置耗时bean以享受异步加速能力。(需使用方自行确认依赖关系满足异步条件)
三、原理
3.1 异步初始化原理
3.1.1 如何异步init和afterPropertiesSet?
3.1.1.1 这俩初始化方法在哪里执行的?
在AbstractAutowireCapableBeanFactory#invokeInitMethods方法(以下代码省略异常处理以及日志打印)
protected void invokeInitMethods(String beanName, final Object bean, @Nullable RootBeanDefinition mbd) throws Throwable { // 先看bean是不是实现了InitializingBean,如果是则执行afterPropertiesSet方法。 boolean isInitializingBean = (bean instanceof InitializingBean); if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) { if (System.getSecurityManager() != null) { AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> { ((InitializingBean) bean).afterPropertiesSet(); return null; }, getAccessControlContext()); } else { ((InitializingBean) bean).afterPropertiesSet(); } } // xml定义的init方法 if (mbd != null && bean.getClass() != NullBean.class) { String initMethodName = mbd.getInitMethodName(); if (StringUtils.hasLength(initMethodName) && !(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) && !mbd.isExternallyManagedInitMethod(initMethodName)) { invokeCustomInitMethod(beanName, bean, mbd); } }}
- 调用位置图
3.1.1.2 如何自定义该方法逻辑使其支持异步执行?
- 很简单的想法
有没有可能,我可以替换原有的BeanFactory,换成我自定义的一个BeanFactory,然后我继承他,只是重写invokeInitMethods方法逻辑使其支持异步?像这样:
public class AsyncInitBeanFactory extends DefaultListableBeanFactory {
private static final Logger logger = LoggerFactory.getLogger(AsyncInitBeanFactory.class);
// 省略
@Override protected void invokeInitMethods(String beanName, Object bean, RootBeanDefinition mbd) throws Throwable { if (AsyncInitBeanNameContainer.MIDDLEWARE_ASYNC_HSF_BEAN_NAME.contains(beanName)) { // hsf异步init this.asyncCallInitMethods(TaskUtil.threadPool4HsfBean, beanName, bean, mbd); } else if (AsyncInitBeanNameContainer.MIDDLEW