春季循环引用错误是否可能因竞态条件而随机发生?

huangapple 未分类评论43阅读模式
英文:

Can Spring circular reference errors happen randomly due to race conditions?

问题

我们有一些构建失败,并且出现了类似以下错误的变体:

Error creating bean with name 'cartServiceImpl': 通过字段 'addressServiceClient' 表达的不满足的依赖关系;嵌套异常是 org.springframework.beans.factory.BeanCurrentlyInCreationException:通过字段 'addressServiceClient' 表达的不满足的依赖关系;嵌套异常是 org.springframework.beans.factory.BeanCurrentlyInCreationException:创建 bean 时出错,名称为 'addressServiceClient' 的 bean;名为 'addressServiceClient' 的 bean 已被注入到其他 bean [addressInfoServiceImpl] 中,以其原始版本的形式作为循环引用的一部分,但最终已被包装。 这意味着这些其他 bean 不使用该 bean 的最终版本。 这通常是过于热切的类型匹配的结果 - 例如,考虑使用 'getBeanNamesOfType',并关闭 'allowEagerInit' 标志。

问题在于,当我们在桌面上启动服务时,我们从未见过这个错误。只有在构建在 CI 服务器上运行时,我们才会看到这个错误。事实上,当我们构建相同的代码大部分时间不会出现这个错误。我有一个测试案例,在这个案例中,它运行了四个并发构建相同的分支和提交(针对四个不同的集群进行部署),有时四个构建都成功,但有时会有一个(甚至两个)构建因为这个错误而失败。

当我确定了这个问题看起来是随机的时,我首先的理论是我们的 Docker Registry 或者 Docker Cache 存在问题,有时会给我们一个旧的镜像(实际上几周前确实有一个类似性质的相关问题)。尽管我很想把这归咎于另一个团队,但我必须假设我们可能在做一些可能会导致这种情况的事情,但也许这是随机的,因为这依赖于竞态条件。我很难相信 Spring Bean 解析会有竞态条件。

是否有可能像这样的错误会根据竞态条件而出现或不出现?

我们使用的是 Spring Framework 5.0.9 和 Spring Boot 2.0.5。

更新

请注意,我仍然无法在我的笔记本上通过普通测试重现这个问题,但是我们能够从 CI 服务器上提取构建的 JAR 文件,将其下载到我的笔记本上,然后直接运行,它确实会出现相同的错误。我们在那个 JAR 和一个“好”的 JAR 之间进行了比较,差异是微妙的,没有明显可能导致此问题的问题。我们注意到错误中提到的 AddressServiceClient 在“坏” JAR 中是类列表中的第二个,在“好” JAR 中则在列表中靠后。

然后,我想也许为 AddressServiceClient 类添加 @Lazy 可以避免这个问题(不是我说“修复”)。我尝试在本地修改那个“坏” JAR 文件,使用“zip”更新 JAR 文件以包含更新后的类文件,我发现生成的 JAR 文件并未出现这个问题。然而,当我最终合并了包含此更改的 PR 并在 CI 服务器上运行构建时,其中一个构建仍然因为相同的错误而失败。

英文:

We have some builds that are failing with variations of this error:

Error creating bean with name 'cartServiceImpl': Unsatisfied dependency expressed through field 'addressServiceClient'; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'addressServiceClient': Bean with name 'addressServiceClient' has been injected into other beans [addressInfoServiceImpl] in its raw version as part of a circular reference, but has eventually been wrapped. This means that said other beans do not use the final version of the bean. This is often the result of over-eager type matching - consider using 'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.

The thing is, we never see this error when we start up the service on our desktops. We only see this error when the build runs on the CI server. In fact, most of the time when we're building the same code, this error does not occur. I have a test case where it runs four concurrent builds of the same branch and commit (targeting for deployment to four different clusters), and sometimes all four succeed, but sometimes one (or even two) of them will fail with this error.

My first theory, when I determined the seeming randomness of this, was that there was some screwy problem with our docker registry or docker cache, which was somehow occasionally giving us an older image (there was a related problem of this nature, for real, several weeks ago). Despite my desire to hang this on another team, I have to assume that there's something we're doing that could be causing this, but perhaps it's random because this is depending on a race condition. I find it hard to believe that Spring bean resolution could have race conditions.

Is there any possibility that an error like this might occur or not occur, depending on race conditions?

We're using Spring Framework 5.0.9 with Spring Boot 2.0.5.

Update:

Note that I still can't repeat this problem with ordinary testing on my laptop, but we were able to extract the jar file constructed on the CI server and download it to my laptop, and then run that directly, and it does get the same error. We compared the contents of the jar file between that jar and a "good" one, and the differences were subtle, no obvious problems that might cause this. We did notice that the AddressServiceClient mentioned in the error is second in the list of classes in the "bad" jar, and far down the list in the "good" jar.

I then thought that perhaps adding @Lazy to the AddressServiceClient class would avoid the problem (not that I don't say "fix"). I tried modifying that "bad" jar file locally, using "zip" to update the jar file with the updated class file, and I found that that resulting jar file did NOT demonstrate the symptom. However, when I finally merged the PR with this change and the builds ran on the CI server, one of them still failed with the same error.

答案1

得分: 0

你可以使用setter注入,它使用Spring的L3缓存
例如:

    private TmsOrderService tmsOrderService;

    @Autowired
    public void setTmsOrderService(TmsOrderService tmsOrderService) {
        this.tmsOrderService = tmsOrderService;
    }

Spring的L3缓存可以避免循环依赖问题。

英文:

You can use setter injection, it use Spring L3 Cache.
For example:

    private TmsOrderService tmsOrderService;

    @Autowired
    public void setTmsOrderService(TmsOrderService tmsOrderService) {
        this.tmsOrderService = tmsOrderService;
    }

Spring L3 Cache avoid circular dependencies.

huangapple
  • 本文由 发表于 2020年4月8日 05:21:16
  • 转载请务必保留本文链接:https://java.coder-hub.com/61089599.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定