java 为什么 Spring 在一台机器上而不是另一台机器上出现循环依赖问题?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/29347723/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Why does Spring get circular dependency issues on one machine and not another?
提问by user2800708
I have a problem getting a Spring Data based application to run in my environment. I am running Debian, but my co-workers are either using Mac or Ubuntu. I have nothing special set up in my environment variables, and am using the exact same version of Java as others.
我在让基于 Spring Data 的应用程序在我的环境中运行时遇到问题。我正在运行 Debian,但我的同事使用 Mac 或 Ubuntu。我的环境变量中没有任何特殊设置,并且使用与其他人完全相同的 Java 版本。
I have seen this in the logs, suggesting that it is a circular reference problem that is leading to the instantiation failure:
我在日志中看到过这个,提示是循环引用问题导致实例化失败:
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?
So the problem appears to be that flyway needs some dependencies and they need flyway.
所以问题似乎是 flyway 需要一些依赖项,他们需要 flyway。
The question is, why does this only happen on my environment not anyone elses? Even on the tests using H2 in memory, I see the problem, so its not my database that is at fault.
问题是,为什么这只发生在我的环境而不是其他任何人?即使在内存中使用 H2 的测试中,我也看到了问题,所以不是我的数据库有问题。
Is it possible that Spring autowiring is confused somehow, and tries to do things in the wrong order, so that the repository is null when it tries to set it?
Spring 自动装配是否有可能以某种方式混淆,并尝试以错误的顺序执行操作,以便存储库在尝试设置时为空?
Does Spring have a badly implemented topological sort for ordering dependencies?
Spring 是否有一个糟糕的拓扑排序来排序依赖项?
Why would it misbehave on my environment?
为什么它会在我的环境中行为不端?
Could ordering of the classpath influence its behaviour?
类路径的排序会影响其行为吗?
======================
======================
The application will not start with this error:
应用程序不会以这个错误启动:
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)
============================
============================
The ContentItemRepository signature is:
ContentItemRepository 签名是:
@Repository
@Transactional
public interface ContentItemRepository extends JpaRepository<ContentItem, String>, JpaSpecificationExecutor<ContentItem> {
============================
============================
This used to work for me, and I was able to identify the commit that broke the build, by iterating through all commits, doing a mvn clean install, and trying to start the server, until I found the delta that broke it.
这曾经对我有用,我能够通过遍历所有提交、执行 mvn 全新安装并尝试启动服务器来识别破坏构建的提交,直到我找到破坏它的增量。
The 'contentItemRepository' that cannot be null is this one:
不能为空的“contentItemRepository”是这个:
@Component
+public class UrlAliasRequestConverter implements Mapper<UrlAliasRequest, UrlAlias> {
+
+ /**
+ * The content item contentItemType repository.
+ */
+ @Autowired
+ private ContentItemRepository contentItemRepository;
采纳答案by user2800708
I don't understand why this happened, but here is the only solution that I came up with:
我不明白为什么会发生这种情况,但这是我想出的唯一解决方案:
Install Debian 8, and it works.
安装 Debian 8,它可以工作。
I tried it in another clean install of Debian 7, and got less errors there, but still had some. A clean install of Debian 8 seemed to work.
我在 Debian 7 的另一个全新安装中尝试了它,那里的错误较少,但仍然有一些。Debian 8 的全新安装似乎有效。
I can only conclude that Java must be calling some system library, that in some way affects the order in which Spring dependencies are resolved. That library must be upgraded in Debian 8, bringing me in-line with the Ubuntu installs that other devs and production are using.
我只能得出结论,Java 必须调用某个系统库,这会以某种方式影响解析 Spring 依赖项的顺序。该库必须在 Debian 8 中升级,使我与其他开发人员和生产正在使用的 Ubuntu 安装保持一致。
I don't know what that library might be... Something that scans the files in the filesystem, reporting them in a different order? Something that unpacks a .jar file, reporting its contents in a different order?
我不知道那个库可能是什么......扫描文件系统中的文件,以不同的顺序报告它们的东西?解压 .jar 文件并以不同顺序报告其内容的东西?
It seems wrong to me that our code is so sensitive to the exact order of dependency resolution and injection, but that would appear to be the case. It also does not look like there is anything in our code that should make it sensitive to the ordering, we're not doing anything crazy, and following pretty standard patterns of usage.
在我看来,我们的代码对依赖解析和注入的确切顺序如此敏感,这似乎是错误的,但情况似乎确实如此。看起来我们的代码中没有任何东西应该使它对排序敏感,我们没有做任何疯狂的事情,并且遵循非常标准的使用模式。
Too much Spring magic keeping the house of cards standing if you ask me. My other projects are on DropWizard, and there the dependency injection is manually coded up, so no surprises.
如果你问我,太多的春天魔法使纸牌屋保持站立。我的其他项目在 DropWizard 上,依赖注入是手动编码的,所以没有什么意外。
=== Update
=== 更新
I upgraded the Debian 7 box to 8, and the problem still persists. Therefore my hypothesis about it being library version is wrong. Must be something about my environment.
我把Debian 7 box升级到8,问题依旧。因此,我关于它是库版本的假设是错误的。一定与我的环境有关。
I created a new user on the box. The problem is still there for that user. There is something about this box it really does not like, but I cannot figure out what it is.
我在盒子上创建了一个新用户。该用户的问题仍然存在。这个盒子有些东西它真的不喜欢,但我不知道它是什么。
I'd like to get to the real cause and understand it, but I don't think I can really dedicate any more time to figuring it out.
我想找到真正的原因并理解它,但我认为我真的不能再花时间去弄清楚它了。
Anyway, a clean install of Debian 8 solves the problem.
无论如何,全新安装 Debian 8 解决了这个问题。
回答by István Pató
I've the same issue on Ubuntu 16.04.
我在 Ubuntu 16.04 上有同样的问题。
I found that the problem with
我发现问题在于
@ComponentScan(basePackages = "com.my.app")
The code is running at least 5 different machine (windows, ubuntu 15.04 and ubuntu 16.04 desktop) but doesn't start our CI server (ubuntu 16.04 server).
代码运行至少 5 台不同的机器(windows、ubuntu 15.04 和 ubuntu 16.04 桌面),但没有启动我们的 CI 服务器(ubuntu 16.04 服务器)。
After I changed
我改变后
@ComponentScan(basePackages = "com.my.app")
to
到
@ComponentScan(basePackages = {"com.my.app.service", "com.my.app.config", "com.my.app"})
the code is running on CI server too.
代码也在 CI 服务器上运行。
I think this is a Spring issue with beans loader...
我认为这是 bean 加载器的 Spring 问题...
UPDATE:
更新:
https://github.com/spring-projects/spring-boot/issues/6045
https://github.com/spring-projects/spring-boot/issues/6045
回答by xki
This is very likely related to the order in which the class files are read in line
这很可能与读取类文件的顺序有关
dir.listFiles()
in
PathMatchingResourcePatternResolver.doRetrieveMatchingFiles()
dir.listFiles()
在
PathMatchingResourcePatternResolver.doRetrieveMatchingFiles()
Since the order of file listing (class files) is dependent on the platform, and there is no sorting done on the array, the order the classes are loaded in is dependent on how the platform returns them.
由于文件列表(类文件)的顺序取决于平台,并且没有对数组进行排序,因此加载类的顺序取决于平台如何返回它们。
参考:http: //forum.spring.io/forum/spring-projects/container/115998-circular-dependency-identification-inconsistent
回答by Brian Kates
What is your repository interface extending? You can look at the Spring source code and see why the exception is thrown:
你的存储库接口扩展了什么?可以看一下Spring源码,看看为什么会抛出异常:
@SuppressWarnings("unchecked")
public Class<? extends T> getObjectType() {
return (Class<? extends T>) (null == repositoryInterface ? Repository.class : repositoryInterface);
}
Here's an example of my repository:
这是我的存储库的示例:
@Repository
public interface GameRepository extends JpaRepository<Game, Long> {
回答by fifman
I got the same problem yesterday. I don't know why, but I found a way to solve the problem: mark all the beans involved in the circular dependency tree as lazy-init
. Not only the ones directly circular dependent to each other, but all the beans that reference them!
我昨天遇到了同样的问题。不知道为什么,但我找到了解决问题的方法:将循环依赖树中涉及的所有bean标记为lazy-init
. 不仅是那些直接循环依赖的 bean,还有所有引用它们的 bean!
The project is an old one thus it only uses spring 3. I am not sure whether the later versions of spring still get this problem.
该项目是一个旧项目,因此它只使用spring 3。我不确定spring的更高版本是否仍然存在此问题。
回答by ryanp
We came across this problem today, in a nearly identical situation - application failing to start due to a circular reference, apparently while constructing Spring Data @Repository
instances, and only on one developer's machine.
我们今天遇到了这个问题,在几乎相同的情况下 - 应用程序由于循环引用而无法启动,显然是在构建 Spring Data@Repository
实例时,并且仅在一个开发人员的机器上。
In our case the solution was to refactor our configuration so that an ApplicationListener<BeforeSaveEvent>
was moved from being an anonymous class defined within a @Configuration
class, and turned into a top-level @Component
.
在我们的例子中,解决方案是重构我们的配置,以便将 anApplicationListener<BeforeSaveEvent>
从在类中定义的匿名类@Configuration
转变为顶级@Component
.
It seems that in some non-obvious way Spring Data repositories keep some sort of handle to these application listeners, and by having ours as an anonymous inner class the repository was implicitly maintaining a reference to its enclosing @Configuration
(and thus a dependency loop being detected due to beans instantiated via that @Configuration
which eventually autowired the repository).
似乎以某种不明显的方式 Spring Data 存储库保留了对这些应用程序侦听器的某种处理,并且通过将我们的作为匿名内部类,存储库隐式地维护对其封闭的引用@Configuration
(因此检测到依赖循环是由于通过@Configuration
最终自动装配存储库的bean实例化)。
All of the answers describing changing lazy init, changing your component scan order, reinstalling your OS, turning it off and on again etc. are only hiding rather than really addressing the problem, and I would suggest instead fixing it at its source :) The reason things work or don't work on various machines is just that, as @xki alludes to, the object graph construction order is non-deterministic.
描述更改延迟初始化、更改组件扫描顺序、重新安装操作系统、关闭并再次打开等的所有答案都只是隐藏而不是真正解决问题,我建议改为从源头修复它:)事情在各种机器上工作或不工作的原因只是,正如@xki 所暗示的那样,对象图构建顺序是不确定的。
回答by nanospeck
For us the the problem was high CPU utilization and this error appeared out of nowhere.
对我们来说,问题是 CPU 使用率高,这个错误突然出现。
Run the below command in linux to find cpu utilization:
在 linux 中运行以下命令以查找 cpu 利用率:
top -b -n1 | grep ^%Cpu | awk '{cpu+=}END{print "Current CPU Utilization is : " 100-cpu/NR}'
If it's 100 or near, it means we have to kill some other microservice not currently used or upgrade our system capacity.
如果它是 100 或接近 100,则意味着我们必须杀死一些当前未使用的其他微服务或升级我们的系统容量。
回答by HymansonWeekes
I do not know why this particular issue would occur on one machine and not another, but I think the issue is most likely that both beans are using constructor injection to pass references to each other which is creating the unresolvable circular dependency- A needs B for its construction which in turn needs A- neither can be created without the other already having been created. If the both objects require a reference to the other, you need to instead pass it via setter injection after the objects have already been created. The answers to this questionare relevant to the problem you are having.
我不知道为什么这个特定问题会发生在一台机器上而不是另一台机器上,但我认为这个问题很可能是两个 bean 都使用构造函数注入来相互传递引用,这造成了无法解决的循环依赖——A 需要 B它的结构反过来又需要 A- 在另一个已经被创建的情况下不能被创建。如果两个对象都需要对另一个对象的引用,则需要在创建对象后通过 setter 注入来传递它。这个问题的答案与您遇到的问题有关。