阿里妹导读
应用启动速度主要的瓶颈在于bean的初始化过程,本文提供了启动速度的一个探索方向。
一、背景
二、解决方案
三、原理
3.1 异步初始化原理
protected void invokeInitMethods(String beanName, final Object bean, 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);}}}
调用位置图
很简单的想法
public class AsyncInitBeanFactory extends DefaultListableBeanFactory {private static final Logger logger = LoggerFactory.getLogger(AsyncInitBeanFactory.class);// 省略protected void invokeInitMethods(String beanName, Object bean, RootBeanDefinition mbd) throws Throwable {if (AsyncInitBeanNameContainer.MIDDLEWARE_ASYNC_HSF_BEAN_NAME.contains(beanName)) {// hsf异步initthis.asyncCallInitMethods(TaskUtil.threadPool4HsfBean, beanName, bean, mbd);} else if (AsyncInitBeanNameContainer.MIDDLEWARE_ASYNC_INIT_BEAN_NAME.contains(beanName)) {// 其他bean异步initthis.asyncCallInitMethods(TaskUtil.threadPool4NormalMBean, beanName, bean, mbd);} else {// 同步init call父类原来的invokeInitMethodstry {super.invokeInitMethods(beanName, bean, mbd);} catch (Exception e) {logger.error("middleware-bean-accelerator sync-init error: {}", e.getMessage(), e);throw e;}}}// 省略}
那现在已经有了自定义方法了,只要解决替换就行了呗?
怎么替换?
public class AsyncAccelerateInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext>, Ordered {public void initialize(ConfigurableApplicationContext context) {// 是否开启异步初始化if (ConfigUtil.isEnableAccelerate(context) && context instanceof GenericApplicationContext) {AsyncInitBeanFactory beanFactory = new AsyncInitBeanFactory(context.getBeanFactory());// 通过反射替换beanFactorytry {Field field = GenericApplicationContext.class.getDeclaredField("beanFactory");field.setAccessible(true);field.set(context, beanFactory);} catch (Throwable e) {throw new RuntimeException(e);}}}}
之后我们只需要在spring.factories文件将其注册即可。
这是哪里?
public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName)throws BeansException {Object result = existingBean;// 把BeanPostProcesss都抓出来调用一下for (BeanPostProcessor processor : getBeanPostProcessors()) {Object current = processor.postProcessBeforeInitialization(result, beanName);if (current == null) {return result;}result = current;}return result;}
调用位置图
很简单的想法
public class AsyncCommonAnnotationBeanPostProcessor extends CommonAnnotationBeanPostProcessor {private static final Logger logger = LoggerFactory.getLogger(AsyncCommonAnnotationBeanPostProcessor.class);public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {// 如果是我指定的beanName 那么走异步初始化, 把super.postProcessBeforeInitialization(bean, beanName) 放进线程池里执行if (AsyncInitBeanNameContainer.MIDDLEWARE_ASYNC_POST_CONSTRUCT_BEAN_NAME.contains(beanName)) {// 异步初始化this.asyncExecutePostConstruct(bean, beanName);} else {// 同步初始化return super.postProcessBeforeInitialization(bean, beanName);}return bean;}// 略}
那现在已经有了自定义方法了,只要解决替换就行了呗?
怎么替换?
public class OverrideAwareBeanPostProcessor extends InstantiationAwareBeanPostProcessorAdapter {public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {// 替换掉原处理@PostConstruct注解的后置处理器if ("org.springframework.context.annotation.internalCommonAnnotationProcessor".equals(beanName)) {AsyncCommonAnnotationBeanPostProcessor asyncBeanPostProcessor = new AsyncCommonAnnotationBeanPostProcessor();// 省略基础的设置return asyncBeanPostProcessor;}return super.postProcessBeforeInstantiation(beanClass, beanName);}}
之后我们只需要把这个BeanPostProcessor添加到BeanFactory,beanFactory.addBeanPostProcessor(new OverrideAwareBeanPostProcessor(beanFactory));
3.2 批量扫描&异步加载中间件Bean原理
public class XXXService {private OrderService orderService;// 省略}
public class HsfBeanNameCollector implements BeanFactoryPostProcessor, BeanClassLoaderAware {private ClassLoader classLoader;public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {// 省略for (String beanName : beanFactory.getBeanDefinitionNames()) {BeanDefinition definition = beanFactory.getBeanDefinition(beanName);String beanClassName = definition.getBeanClassName();Class<?> clazz = ClassUtils.resolveClassName(definition.getBeanClassName(), this.classLoader);ReflectionUtils.doWithFields(clazz, field -> {if (AnnotationUtils.getAnnotation(field, HSFConsumer.class) == null) {return;}// 收集HsfConsumerBeanName方便后续异步化AsyncInitStaticVariables.MIDDLEWARE_ASYNC_HSF_BEAN_NAME.add(field.getName());});}}public void setBeanClassLoader(ClassLoader classLoader) {this.classLoader = classLoader;}}
public class HSFSpringConsumerBean implements FactoryBean, InitializingBean, ApplicationListener, ApplicationContextAware {// 省略public Object getObject() throws Exception {return consumerBean.getObject();}// 省略}
而该动态代理类是如何生成的呢?答案在HSFApiConsumerBean的init方法中
如下所示:metadata.setTarget(consume(metadata));
public class HSFApiConsumerBean {// 省略/*** 初始化** @throws Exception*/public void init() throws Exception {// 省略synchronized (metadata) {// 省略metadata.init();try {// 动态代理类的设置就在这里metadata.setTarget(consume(metadata));// 省略} catch (Exception e) {// 省略} catch (Throwable t) {// 省略}// 省略}}// 省略}
public class NewHsfSpringConsumerBean extends HSFSpringConsumerBean {// 省略private Future<?> initTaskFuture;/*** 重写NewHsfSpringConsumerBean的主要目的 在此加入卡点 防止hsfSpringConsumerBean未初始化完成导致的npe** @return* @throws Exception*/public Object getObject() throws Exception {this.waitHsfInit();return super.getObject();}private void waitHsfInit() {if (this.initTaskFuture == null) {logger.warn("middleware-bean-accelerator, hsf getObject wait future is null.");return;}try {this.initTaskFuture.get();} catch (InterruptedException | ExecutionException e) {throw new RuntimeException(e);}}// 省略}
现在的问题就是我们如何将原有的HSFSpringConsumerBean替换成NewHsfSpringConsumerBean?
答案还是InstantiationAwareBeanPostProcessorAdapter接口
如下所示:
public class OverrideAwareBeanPostProcessor extends InstantiationAwareBeanPostProcessorAdapter {private final AsyncInitBeanFactory beanFactory;// 省略public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {// 修改beanDefinition 使容器创建自定义的HsfSpringConsumerBeanif (beanClass == HSFSpringConsumerBean.class) {this.reviseBeanDefinition(beanName, NewHsfSpringConsumerBean.class);// 返回null可以让实例化的任务交由spring容器return null;}return super.postProcessBeforeInstantiation(beanClass, beanName);}public boolean postProcessAfterInstantiation(Object bean, String beanName) {if (bean.getClass() == NewHsfSpringConsumerBean.class) {this.reviseBeanDefinition(beanName, HSFSpringConsumerBean.class);}return super.postProcessAfterInstantiation(bean, beanName);}/*** 修改beanDefinition* 设置NewHsfSpringConsumerBean使容器创建自定义的HsfSpringConsumerBean 实例化后设置回来** @param beanName* @return*/private void reviseBeanDefinition(String beanName, Class<?> clazz) {try {Method methodOfRootBeanDefinition = this.beanFactory.getClass().getSuperclass().getSuperclass().getSuperclass().getDeclaredMethod("getMergedLocalBeanDefinition", String.class);methodOfRootBeanDefinition.setAccessible(true);RootBeanDefinition beanDefinition = (RootBeanDefinition) methodOfRootBeanDefinition.invoke(this.beanFactory, beanName);// 重点步骤: 修改beanDefinition 使容器创建自定义的HsfSpringConsumerBean, 并在实例化后设置回来beanDefinition.setBeanClass(clazz);} catch (InvocationTargetException | NoSuchMethodException | IllegalAccessException e) {throw new RuntimeException(e);}}}
我们在实例化之前,修改beanDefinition,使容器创建自定义的HsfSpringConsumerBean。然后在实例化后的阶段将beanDefinition改回,这样就非常优雅实现了对原有HSFSpringConsumerBean的替换动作!
四、效果
4.1 性能效果
阿里云开发者社区,千万开发者的选择
阿里云开发者社区,百万精品技术内容、千节免费系统课程、丰富的体验场景、活跃的社群活动、行业专家分享交流,欢迎点击【阅读原文】加入我们。
文章引用微信公众号"阿里开发者",如有侵权,请联系管理员删除!