您的位置 首页 java

「Spring」Autowired对象为null问题排查

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

问题描述

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

问题排查

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

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

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

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

查看相关代码

找到问题

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

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

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

BeanFactoryPostProcessor

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

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

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

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

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

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

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

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

问题总结

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

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

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

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

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

文章来源:智云一二三科技

文章标题:「Spring」Autowired对象为null问题排查

文章地址:https://www.zhihuclub.com/187068.shtml

关于作者: 智云科技

热门文章

网站地图