一次Spring Bean初始化顺序问题排查记录

Posted Birding

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了一次Spring Bean初始化顺序问题排查记录相关的知识,希望对你有一定的参考价值。

最近在使用Springboot的时候需要通过静态的方法获取到Spring容器托管的bean对象,参照一些博文里写的,新建了个类,并实现ApplicationContextAware接口。代码大致如下: 

@Component
public class SpringUtils implements ApplicationContextAware {

    private static ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        if(SpringUtils.applicationContext == null) {
            SpringUtils.applicationContext = applicationContext;
        }
    }
   public static <T> T getBean(String name,Class<T> clazz){
        return getApplicationContext().getBean(name, clazz);
    }  
}

然后另外一个bean需要依赖这个静态获取bean的方法,代码大致如下:

@Component
public class TestBean{

   private Object dependencyBean = SpringUtils.getBean(OtherBean.class);  
    
}

(注: 忽略代码逻辑是否合理~~ 这些代码是为演示所用简化的逻辑,肯定有同学会说:既然都是bean了为什么不注入,而是要用静态的获取呢?这个暂时不考虑,暂认为就必须要这样搞)

这两个类的层次结构和包名大致如下: 

utils

> notice

  > TestBean

> SpringUtils


就是TestBean在SpringUtils的下一级,TestBean所在包名为notice(这这个名字很重要! 直接影响到这两个bean的加载顺序,具体原理往下看

代码就这么多,从以上代码来静态分析看,确实有些漏洞,因为没有考虑到Spring bean的加载顺序,可能导致的SpringUtils报空指针异常(在TestBean先于SpringUtils初始化的场景下),不管怎么样先执行一下看下效果,效果如下: 

macOS操作系统下代码正常

windows平台下代码空指针异常

为什么这还跟平台有关了呢?难道Spring bean的初始化顺序还跟平台有关?事实证明这个猜想是正确的。下面从Spring源代码里找原因。

普通的(什么样的bean算是普通?这里暂定用@Component注解的bean)Spring bean的加载包括两个部分,第一部分是加载bean的定义,第二部分是实例化bean。这个可以在AbstractApplicationContext.refresh方法里找到对应:

 



以上是关于一次Spring Bean初始化顺序问题排查记录的主要内容,如果未能解决你的问题,请参考以下文章

spring bean实例化过成中各种初始化的顺序 beanPost顺序和时机

spring bean实例化过成中各种初始化的顺序 beanPost顺序和时机

在Spring Bean的生命周期中各方法的执行顺序

怎样让spring重新初始化所有的bean

spring 依赖注入的bean的初始化如果需要变量参数怎么办?

获取Spring的Bean