【SpringBoot】SmartLifecycle 的执行过程以及执行顺序
1 前言
之前看过 SpringBoot 启动后,Tomcat监听端口也就是正式接收请求的开启时机以及启动时向注册中心注册服务的时机,都是在刷新上下文的 finishRefresh() 阶段通过执行生命周期的 Bean 来得到执行的。
当有多个 Bean 的话他们的执行顺序有没有什么说法呢,出于这个思考,我们本节就简单看一下。
2 源码分析
我们就从 finishRefresh 看起:
protected void finishRefresh() { // 清理资源缓存 Clear context-level resource caches (such as ASM metadata from scanning). clearResourceCaches(); // 初始化LifecycleProcessor 生命周期处理器用于在启动或结束的时候处理Bean Initialize lifecycle processor for this context. initLifecycleProcessor(); // 执行我们的生命周期 也是我们本节看的 getLifecycleProcessor().onRefresh(); // 发布ContextRefreshedEvent Publish the final event. publishEvent(new ContextRefreshedEvent(this)); // Participate in LiveBeansView MBean, if active. // 如果配置了"spring.liveBeansView.mbeanDomain"的系统属性,指定了MBeanServer, // 那么会将ApplicationContext注册到MBeanServer中, // 可以通过MBeanServer,对spring容器中的bean,进行实时的查看和管理 if (!NativeDetector.inNativeImage()) { LiveBeansView.registerApplicationContext(this); } }
首先有一个初始化生命周期的处理器 initLifecycleProcessor 方法,这个主要是判断你有没有自己的 lifecycleProcessor 的Bean,没有的话就创建默认的 DefaultLifecycleProcessor。
protected void initLifecycleProcessor() { ConfigurableListableBeanFactory beanFactory = getBeanFactory(); if (beanFactory.containsLocalBean(LIFECYCLE_PROCESSOR_BEAN_NAME)) { this.lifecycleProcessor = beanFactory.getBean(LIFECYCLE_PROCESSOR_BEAN_NAME, LifecycleProcessor.class); if (logger.isTraceEnabled()) { logger.trace("Using LifecycleProcessor [" + this.lifecycleProcessor + "]"); } } else { DefaultLifecycleProcessor defaultProcessor = new DefaultLifecycleProcessor(); defaultProcessor.setBeanFactory(beanFactory); this.lifecycleProcessor = defaultProcessor; beanFactory.registerSingleton(LIFECYCLE_PROCESSOR_BEAN_NAME, this.lifecycleProcessor); if (logger.isTraceEnabled()) { logger.trace("No '" + LIFECYCLE_PROCESSOR_BEAN_NAME + "' bean, using " + "[" + this.lifecycleProcessor.getClass().getSimpleName() + "]"); } } }
然后执行到 getLifecycleProcessor().onRefresh(),我们进入到 DefaultLifecycleProcessor 的 onRefresh:
@Override public void onRefresh() { startBeans(true); this.running = true; }
接着看看内部的 startBeans 方法:
private void startBeans(boolean autoStartupOnly) { // 获取所有的 Lifecycle Bean Map<String, Lifecycle> lifecycleBeans = getLifecycleBeans(); // 存放最后要执行的Bean key是 phase值 Map<Integer, LifecycleGroup> phases = new TreeMap<>(); lifecycleBeans.forEach((beanName, bean) -> { // 如果是 SmartLifecycle 并且开启了autoStartupOnly 或者是 autoStartupOnly 为 true if (!autoStartupOnly || (bean instanceof SmartLifecycle && ((SmartLifecycle) bean).isAutoStartup())) { // 获取 Bean 的 phase 值 默认的 SmartLifecycle 的默认 phase值是 Integer.MAX int phase = getPhase(bean); // 其实就是根据 phase 分组放置 phases.computeIfAbsent( phase, p -> new LifecycleGroup(phase, this.timeoutPerShutdownPhase, lifecycleBeans, autoStartupOnly) ).add(beanName, bean); } }); if (!phases.isEmpty()) { List<Integer> keys = new ArrayList<>(phases.keySet()); // 先根据 phase 升序排序 Collections.sort(keys); // 每组执行 其实组内还会在排序一次 感觉没必要因为这个组内的 phase 值都一样的 for (Integer key : keys) { phases.get(key).start(); } } }
可以看到决定顺序的就是 phase 值(越小越先执行),phase 值又是由 getPhase 方法得出的:
protected int getPhase(Lifecycle bean) { return (bean instanceof Phased ? ((Phased) bean).getPhase() : 0); } // SmartLifecycle 继承了 Phased 并提供了默认方法: // int DEFAULT_PHASE = Integer.MAX_VALUE; default boolean isAutoStartup() { return true; } default int getPhase() { return DEFAULT_PHASE; }
所以当你的 SmartLifecycle 要先执行的话,就重写 getPhase 并把返回值设置的比 Integer.MAX_VALUE 小即可。
简单画个图捋一下:
那我们最后再简单看下 SpringBoot 默认的情况下都有哪些 SmartLifecycle:
另外其实我写这篇之前我其实真正想去写的是服务启动时的注册时机以及 Tomcat 监听端口打开这两个时机的顺序,因为你比如我服务注册上了,可是我还没打开监听端口,那是不是就会导致别的服务调用异常么?是不是。这跟 Eureka 的机制有关,但是 Eureka 源码没怎么看过,所以我也说不好结论,我百科了一下, 服务注册的时候,会有心跳检测,你比如你注册是不是会看你这服务活没活着,活着的话才会变成 UP 可用状态,没活就不会变 UP,服务发现本身默认也有心跳检测以及服务的续约,所以这俩的顺序就变的不那么重要了,是不是。
3 小结
好啦,执行顺序主要是通过 SmartLifecycle 的 getPhase 值决定的,越小越优先执行,本节就看到这里,有理解不对的地方欢迎指正哈。