spring boot autowired注入为null_「Spring」Autowired对象为null问题排查

今天项目组遇到一个问题让我帮忙排查,虽然是一个小众问题,但是涉及到Spring的Bean初始化的相关知识点。整理了一下,分享给大家。

问题描述

注解了@Service的对象,其内部注解了@Autowired的对象没有被注入,方法在调用时为null。并且,排查时发现包括@PostConstruct方法没有被调用。(注:以下代码均为示例代码,非业务代码)

50d380ba49cc0b2a347c4bc73ac10985.png

问题排查

思路@Autowired在启动时,如果是正常的BeanFactory创建的对象,会抛出相应异常。

03b53c5e6db7a3d1c5f099d6b7859a2a.png

所以,初步判断为该对象的实例化时机存在问题。

问题跟踪: 创建构建函数,跟踪问题对象在哪里实例化。

be9744f99bf640a1bae51fb080ddb91c.png
6d91ae7e451694b305a614b47f1a124b.png

从上图的调用堆栈中,可以非常明确的看到对象ProblemService的实例化调用的一个关系。

查看相关代码

09d37829ee1e7288493d2e1044666cc6.png
1047bf4ecd96ca724eb45436e0799cc9.png

找到问题

对象ProblemService被一个BeanFactoryPostProcessor在处理时通过Class.forName装载了,此时会执行装载的对象的静态代码块。

a864eac75f04ebff88fef3e96869f9f4.png

静态代码块,通过SpringBeans#ConfigurableListableBeanFactoryBeanFactory中加载对象。

那么,为什么加载的对象ProblemServicedepInjectBean AutoWired没有生效,并且init方法没有被调用呢?这是因为BeanFactoryPostProcessor的机制引起的。

BeanFactoryPostProcessor

在说明BeanFactoryPostProcessor之前,我们需要对SpringBean加载处理机制有一定的了解。这里我简要的说明一下。

SpringBean加载时有 定义和初始化的区分。而BeanFactoryPostProcessor的发生时机在定义完成之后,注意,此时所有的Bean只是定义完成,并没有真正的初始化完成

那么,BeanFactoryPostProcessor具体是做什么的呢?

定义:应用工厂的勾子处理,允许自定义修改应用上下文中的Bean定义的属性。但是,请不在要BeanFactoryPostProcessor时实例化任何一个Bean,否则会导致Bean过早实例早,出现不可预见的情况

再强调一次:不在要BeanFactoryPostProcessor时实例化任何一个Bean,否则会导致Bean过早实例早,出现不可预见的情况

原因是,BeanFactoryPostProcessor的发生时机在所有的Bean定义完成之后,实例化之前。也就是在发生时刻,在Spring容器中,所有的Bean已经定义好了,等待进行实例化。

如果此时,在BeanFactoryPostProcessor时机,对Bean进行了实例化,而没有完整的注入依赖关系。那么,对于Spring容器来说,在Bean实例化阶段时,会判断已经实例化而跳过这个Bean的处理。

所以,此时对于这个在BeanFactoryPostProcessor实例化的Bean,其保持着实例化时刻的所有定义。也就是没有注入的依赖一定是null,并且相关的注解,如@PostConstruct等失效。

问题总结

在上述的流程中,主要出现了二个不合理的应用:

  • 在一个BeanFactoryPostProcessor中对会一些类进行装载,导致静态代码在非预期时被执行
f1ccc2235844e261c56f2501af67da5c.png

对于开发和JVM来说,一些类在未被使用时并不需要装载和初始化。所以对开发框架的扩展或者封装,不应该破坏类应该在被需求时才进行装载和初始化,提前装载和初始化应该是有条件的!

  • 在类的静态变量中,使用一个可以通过静态方法获取Spring容器中的Bean工具类
70949d6ffbbc5d6064edb0be3fd64f9d.png

对于SpringBean对象的获取时机是要非常注意的。在这种写法中,如果这个类的方法在Spring容器未启动完成之前被调用,就会导致依赖Bean的空指针异常。如果,在静态方法中需要依赖于Spring容器中的Bean时,需要非常的注意方法的调用时机。如果不是非必须的,不应该用这种方法来实现!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值