为什么 Spring 在一台机器上而不是另一台机器上出现循环依赖问题?
Why does Spring get circular dependency issues on one machine and not another?
在我的环境中将 Spring 基于数据的应用程序获取到 运行 时遇到问题。我是 运行ning Debian,但我的同事使用 Mac 或 Ubuntu。我在我的环境变量中没有任何特殊设置,并且使用与其他人完全相同的 Java 版本。
我在日志中看到了这个,提示是循环引用问题导致实例化失败:
nested exception is
org.springframework.beans.factory.BeanCreationException: Error
creating bean with name 'flyway.CONFIGURATION_PROPERTIES':
Initialization of bean failed;
...
nested exception is
org.springframework.beans.factory.BeanCurrentlyInCreationException:
Error creating bean with name 'flyway': Requested bean is currently in
creation: Is there an unresolvable circular reference?
所以问题似乎是 flyway 需要一些依赖关系,他们需要 flyway。
问题是,为什么这只发生在我的环境中而不发生在其他任何人身上?即使在内存中使用 H2 的测试中,我也看到了问题,所以不是我的数据库有问题。
是否有可能 Spring 自动装配以某种方式混淆,并试图以错误的顺序做事,以至于当它试图设置它时存储库为空?
Spring 是否有一个糟糕的用于排序依赖的拓扑排序?
为什么它会在我的环境中出现异常?
类路径的排序会影响它的行为吗?
======================
应用程序无法启动并出现此错误:
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'contentItemRepository': FactoryBean threw exception on object creation; nested exception is java.lang.IllegalArgumentException: Repository interface must not be null on initialization!
at org.springframework.beans.factory.support.FactoryBeanRegistrySupport.doGetObjectFromFactoryBean(FactoryBeanRegistrySupport.java:175)
at org.springframework.beans.factory.support.FactoryBeanRegistrySupport.getObjectFromFactoryBean(FactoryBeanRegistrySupport.java:127)
at org.springframework.beans.factory.support.AbstractBeanFactory.getObjectForBeanInstance(AbstractBeanFactory.java:1517)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:251)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:194)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.findAutowireCandidates(DefaultListableBeanFactory.java:1127)
==============================
ContentItemRepository 签名是:
@Repository
@Transactional
public interface ContentItemRepository extends JpaRepository<ContentItem, String>, JpaSpecificationExecutor<ContentItem> {
==============================
这曾经对我有用,我能够通过遍历所有提交、执行 mvn clean install 并尝试启动服务器来识别破坏构建的提交,直到我找到 delta弄坏了。
不能为空的'contentItemRepository'是这个:
@Component
+public class UrlAliasRequestConverter implements Mapper<UrlAliasRequest, UrlAlias> {
+
+ /**
+ * The content item contentItemType repository.
+ */
+ @Autowired
+ private ContentItemRepository contentItemRepository;
您的存储库接口扩展了什么?可以看看Spring源码,看看为什么会抛出异常:
@SuppressWarnings("unchecked")
public Class<? extends T> getObjectType() {
return (Class<? extends T>) (null == repositoryInterface ? Repository.class : repositoryInterface);
}
这是我的存储库的示例:
@Repository
public interface GameRepository extends JpaRepository<Game, Long> {
我不知道为什么这个特定问题会发生在一台机器上而不是另一台机器上,但我认为问题很可能是两个 bean 都使用构造函数注入来相互传递引用,这造成了无法解决的循环依赖- A 需要 B 来构建它,而 B 又需要 A - 如果没有另一个已经创建,则无法创建。
如果两个对象都需要对另一个的引用,则需要在创建对象后通过 setter 注入来传递它。 this question 的答案与您遇到的问题相关。
我不明白为什么会这样,但这是我想出的唯一解决方案:
安装 Debian 8,它可以运行。
我在 Debian 7 的另一个干净安装中尝试过,那里的错误较少,但仍然有一些。全新安装的 Debian 8 似乎有效。
我只能得出结论,Java 一定是在调用某个系统库,这在某种程度上影响了 Spring 依赖关系的解析顺序。该库必须在 Debian 8 中升级,使我与其他开发人员和生产人员正在使用的 Ubuntu 安装保持一致。
我不知道那个库可能是什么...扫描文件系统中的文件并以不同顺序报告它们的东西?解压 .jar 文件并以不同顺序报告其内容的东西?
我们的代码对依赖项解析和注入的确切顺序如此敏感,这对我来说似乎是错误的,但事实确实如此。它看起来也不像我们的代码中有任何东西应该让它对顺序敏感,我们没有做任何疯狂的事情,并且遵循非常标准的使用模式。
太多Spring魔法让纸牌屋屹立不倒,如果你问我的话。我的其他项目在 DropWizard 上,依赖注入是手动编码的,所以没有惊喜。
===更新
我把Debian 7 box升级到8,问题依旧。因此,我关于它是库版本的假设是错误的。一定和我的环境有关。
我在盒子上创建了一个新用户。该用户的问题仍然存在。这个盒子有些东西它真的不喜欢,但我不知道它是什么。
我想找到真正的原因并了解它,但我认为我真的不能再花时间去弄清楚它了。
无论如何,全新安装 Debian 8 可以解决问题。
这很可能与class行读取文件的顺序有关
dir.listFiles()
在
PathMatchingResourcePatternResolver.doRetrieveMatchingFiles()
由于文件列表(class 文件)的顺序取决于平台,并且没有对数组进行排序,因此 classes 的加载顺序取决于平台如何returns他们
我在 Ubuntu 16.04.
遇到了同样的问题
我发现
的问题
@ComponentScan(basePackages = "com.my.app")
代码是 运行 至少 5 台不同的机器(windows、ubuntu 15.04 和 ubuntu 16.04 桌面)但没有启动我们的 CI 服务器(ubuntu 16.04 服务器)。
我改变后
@ComponentScan(basePackages = "com.my.app")
到
@ComponentScan(basePackages = {"com.my.app.service", "com.my.app.config", "com.my.app"})
代码也在 CI 服务器上 运行。
我认为这是 beans 加载器的一个 Spring 问题...
更新:
我昨天遇到了同样的问题。不知道为什么,我找到了解决问题的方法:将循环依赖树中涉及的所有bean标记为lazy-init
。不仅是那些直接相互循环依赖的,而且所有引用它们的 beans!
项目比较老,所以只用了spring 3.不知道spring以后的版本会不会出现这个问题
我们今天在几乎相同的情况下遇到了这个问题 - 应用程序由于循环引用而无法启动,显然是在构建 Spring 数据 @Repository
实例时,并且只在一个开发人员的机器上.
在我们的案例中,解决方案是重构我们的配置,以便将 ApplicationListener<BeforeSaveEvent>
从 @Configuration
class 中定义的匿名 class 移到进入顶级 @Component
.
似乎以某种不明显的方式 Spring 数据存储库为这些应用程序侦听器保留了某种句柄,并且通过将我们的作为匿名内部 class 存储库隐含地维护了一个引用其封闭的 @Configuration
(因此由于通过 @Configuration
实例化的 beans 最终自动装配存储库而检测到依赖循环)。
所有描述更改惰性初始化、更改组件扫描顺序、重新安装 OS、将其关闭然后重新打开等的答案都只是隐藏而不是真正解决问题,我建议而不是在源头上修复它:)事情在各种机器上工作或不工作的原因只是,正如@xki 暗示的那样,对象图构造顺序是不确定的。
对我们来说,问题是CPU利用率高,这个错误突然出现。
运行 linux 中的以下命令可查找 cpu 利用率:
top -b -n1 | grep ^%Cpu | awk '{cpu+=}END{print "Current CPU Utilization is : " 100-cpu/NR}'
如果它是 100 或接近 100,则意味着我们必须杀死一些当前未使用的其他微服务或升级我们的系统容量。
虽然所有的答案都集中在发生故障的机器以及如何修复它,但我想指出,我们应该想知道没有发生故障的机器。如果有循环,DI 应该会失败!我们希望这种情况在所有环境中始终如一地发生。
我们遇到了生产失败但暂存、开发和 CI 都很好的相同行为。那太可悲了。我们无法针对该问题创建最小示例。
临时拐杖解决方案:ENV spring.main.lazy-initialization=true
在我的环境中将 Spring 基于数据的应用程序获取到 运行 时遇到问题。我是 运行ning Debian,但我的同事使用 Mac 或 Ubuntu。我在我的环境变量中没有任何特殊设置,并且使用与其他人完全相同的 Java 版本。
我在日志中看到了这个,提示是循环引用问题导致实例化失败:
nested exception is
org.springframework.beans.factory.BeanCreationException: Error
creating bean with name 'flyway.CONFIGURATION_PROPERTIES':
Initialization of bean failed;
...
nested exception is
org.springframework.beans.factory.BeanCurrentlyInCreationException:
Error creating bean with name 'flyway': Requested bean is currently in
creation: Is there an unresolvable circular reference?
所以问题似乎是 flyway 需要一些依赖关系,他们需要 flyway。
问题是,为什么这只发生在我的环境中而不发生在其他任何人身上?即使在内存中使用 H2 的测试中,我也看到了问题,所以不是我的数据库有问题。
是否有可能 Spring 自动装配以某种方式混淆,并试图以错误的顺序做事,以至于当它试图设置它时存储库为空?
Spring 是否有一个糟糕的用于排序依赖的拓扑排序?
为什么它会在我的环境中出现异常?
类路径的排序会影响它的行为吗?
======================
应用程序无法启动并出现此错误:
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'contentItemRepository': FactoryBean threw exception on object creation; nested exception is java.lang.IllegalArgumentException: Repository interface must not be null on initialization!
at org.springframework.beans.factory.support.FactoryBeanRegistrySupport.doGetObjectFromFactoryBean(FactoryBeanRegistrySupport.java:175)
at org.springframework.beans.factory.support.FactoryBeanRegistrySupport.getObjectFromFactoryBean(FactoryBeanRegistrySupport.java:127)
at org.springframework.beans.factory.support.AbstractBeanFactory.getObjectForBeanInstance(AbstractBeanFactory.java:1517)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:251)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:194)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.findAutowireCandidates(DefaultListableBeanFactory.java:1127)
==============================
ContentItemRepository 签名是:
@Repository
@Transactional
public interface ContentItemRepository extends JpaRepository<ContentItem, String>, JpaSpecificationExecutor<ContentItem> {
==============================
这曾经对我有用,我能够通过遍历所有提交、执行 mvn clean install 并尝试启动服务器来识别破坏构建的提交,直到我找到 delta弄坏了。
不能为空的'contentItemRepository'是这个:
@Component
+public class UrlAliasRequestConverter implements Mapper<UrlAliasRequest, UrlAlias> {
+
+ /**
+ * The content item contentItemType repository.
+ */
+ @Autowired
+ private ContentItemRepository contentItemRepository;
您的存储库接口扩展了什么?可以看看Spring源码,看看为什么会抛出异常:
@SuppressWarnings("unchecked")
public Class<? extends T> getObjectType() {
return (Class<? extends T>) (null == repositoryInterface ? Repository.class : repositoryInterface);
}
这是我的存储库的示例:
@Repository
public interface GameRepository extends JpaRepository<Game, Long> {
我不知道为什么这个特定问题会发生在一台机器上而不是另一台机器上,但我认为问题很可能是两个 bean 都使用构造函数注入来相互传递引用,这造成了无法解决的循环依赖- A 需要 B 来构建它,而 B 又需要 A - 如果没有另一个已经创建,则无法创建。 如果两个对象都需要对另一个的引用,则需要在创建对象后通过 setter 注入来传递它。 this question 的答案与您遇到的问题相关。
我不明白为什么会这样,但这是我想出的唯一解决方案:
安装 Debian 8,它可以运行。
我在 Debian 7 的另一个干净安装中尝试过,那里的错误较少,但仍然有一些。全新安装的 Debian 8 似乎有效。
我只能得出结论,Java 一定是在调用某个系统库,这在某种程度上影响了 Spring 依赖关系的解析顺序。该库必须在 Debian 8 中升级,使我与其他开发人员和生产人员正在使用的 Ubuntu 安装保持一致。
我不知道那个库可能是什么...扫描文件系统中的文件并以不同顺序报告它们的东西?解压 .jar 文件并以不同顺序报告其内容的东西?
我们的代码对依赖项解析和注入的确切顺序如此敏感,这对我来说似乎是错误的,但事实确实如此。它看起来也不像我们的代码中有任何东西应该让它对顺序敏感,我们没有做任何疯狂的事情,并且遵循非常标准的使用模式。
太多Spring魔法让纸牌屋屹立不倒,如果你问我的话。我的其他项目在 DropWizard 上,依赖注入是手动编码的,所以没有惊喜。
===更新
我把Debian 7 box升级到8,问题依旧。因此,我关于它是库版本的假设是错误的。一定和我的环境有关。
我在盒子上创建了一个新用户。该用户的问题仍然存在。这个盒子有些东西它真的不喜欢,但我不知道它是什么。
我想找到真正的原因并了解它,但我认为我真的不能再花时间去弄清楚它了。
无论如何,全新安装 Debian 8 可以解决问题。
这很可能与class行读取文件的顺序有关
dir.listFiles()
在
PathMatchingResourcePatternResolver.doRetrieveMatchingFiles()
由于文件列表(class 文件)的顺序取决于平台,并且没有对数组进行排序,因此 classes 的加载顺序取决于平台如何returns他们
我在 Ubuntu 16.04.
遇到了同样的问题我发现
的问题@ComponentScan(basePackages = "com.my.app")
代码是 运行 至少 5 台不同的机器(windows、ubuntu 15.04 和 ubuntu 16.04 桌面)但没有启动我们的 CI 服务器(ubuntu 16.04 服务器)。
我改变后
@ComponentScan(basePackages = "com.my.app")
到
@ComponentScan(basePackages = {"com.my.app.service", "com.my.app.config", "com.my.app"})
代码也在 CI 服务器上 运行。
我认为这是 beans 加载器的一个 Spring 问题...
更新:
我昨天遇到了同样的问题。不知道为什么,我找到了解决问题的方法:将循环依赖树中涉及的所有bean标记为lazy-init
。不仅是那些直接相互循环依赖的,而且所有引用它们的 beans!
项目比较老,所以只用了spring 3.不知道spring以后的版本会不会出现这个问题
我们今天在几乎相同的情况下遇到了这个问题 - 应用程序由于循环引用而无法启动,显然是在构建 Spring 数据 @Repository
实例时,并且只在一个开发人员的机器上.
在我们的案例中,解决方案是重构我们的配置,以便将 ApplicationListener<BeforeSaveEvent>
从 @Configuration
class 中定义的匿名 class 移到进入顶级 @Component
.
似乎以某种不明显的方式 Spring 数据存储库为这些应用程序侦听器保留了某种句柄,并且通过将我们的作为匿名内部 class 存储库隐含地维护了一个引用其封闭的 @Configuration
(因此由于通过 @Configuration
实例化的 beans 最终自动装配存储库而检测到依赖循环)。
所有描述更改惰性初始化、更改组件扫描顺序、重新安装 OS、将其关闭然后重新打开等的答案都只是隐藏而不是真正解决问题,我建议而不是在源头上修复它:)事情在各种机器上工作或不工作的原因只是,正如@xki 暗示的那样,对象图构造顺序是不确定的。
对我们来说,问题是CPU利用率高,这个错误突然出现。
运行 linux 中的以下命令可查找 cpu 利用率:
top -b -n1 | grep ^%Cpu | awk '{cpu+=}END{print "Current CPU Utilization is : " 100-cpu/NR}'
如果它是 100 或接近 100,则意味着我们必须杀死一些当前未使用的其他微服务或升级我们的系统容量。
虽然所有的答案都集中在发生故障的机器以及如何修复它,但我想指出,我们应该想知道没有发生故障的机器。如果有循环,DI 应该会失败!我们希望这种情况在所有环境中始终如一地发生。
我们遇到了生产失败但暂存、开发和 CI 都很好的相同行为。那太可悲了。我们无法针对该问题创建最小示例。
临时拐杖解决方案:ENV spring.main.lazy-initialization=true