MonetaryException:未加载 MonetaryAmountsSingletonSpi

Mak*_*nin 4 java spring gradle jsr354 java-money

问题描述

我有一个 java 项目,其 gradle 依赖项来自org.javamoney:moneta:1.3.

我还有两个 Kubernetes 集群。我使用 docker-container 部署我的 java 应用程序。

当我在第一个Kubernetes 集群中部署应用程序时,一切都很好。但是当我在第二个Kubernetes 集群中部署我的应用程序(相同的 docker-container)时,出现以下错误:

javax.money.MonetaryException: No MonetaryAmountsSingletonSpi loaded.
    at javax.money.Monetary.lambda$getDefaultAmountFactory$13(Monetary.java:291)
    at java.base/java.util.Optional.orElseThrow(Optional.java:408)
    at javax.money.Monetary.getDefaultAmountFactory(Monetary.java:291)
Run Code Online (Sandbox Code Playgroud)

它出现在以下代码中:

javax.money.MonetaryException: No MonetaryAmountsSingletonSpi loaded.
    at javax.money.Monetary.lambda$getDefaultAmountFactory$13(Monetary.java:291)
    at java.base/java.util.Optional.orElseThrow(Optional.java:408)
    at javax.money.Monetary.getDefaultAmountFactory(Monetary.java:291)
Run Code Online (Sandbox Code Playgroud)

软件版本

  • 莫内塔1.3
  • 摇篮:6.0.1.
  • 基础 docker-image: openjdk:11.0.7-jdk-slim.
  • 弹簧靴:2.2.7.RELEASE.
  • Kubernetes(两个集群上的版本相同)Server Version: version.Info{Major:"1", Minor:"15", GitVersion:"v1.15.3", GitCommit:"2d3c76f9091b6bec110a5e63777c332469e0cba2", GitTreeState:"clean", BuildDate:"2019-08-19T11:05:50Z", GoVersion:"go1.12.9", Compiler:"gc", Platform:"linux/amd64"}:.
  • 爪哇:java -version openjdk version "11.0.7" 2020-04-14 OpenJDK Runtime Environment 18.9 (build 11.0.7+10) OpenJDK 64-Bit Server VM 18.9 (build 11.0.7+10, mixed mode).

我尝试过的

以不同方式声明 gradle 依赖项

我发现了这个问题,它给了我一个想法,尝试以某种不同的方式声明 gradle 依赖关系。我努力了:

  • implementation 'org.javamoney:moneta:1.3'
  • compile group: 'org.javamoney', name: 'moneta', version: '1.3', ext: 'pom'
  • compile 'org.javamoney:moneta:1.3'
  • runtimeOnly 'org.javamoney:moneta:1.3'

不幸的是,它没有给出任何积极的结果。

Moneta 的复制粘贴服务加载器配置

如此评论中提到的,我尝试将服务加载器配置从 Moneta复制到以下项目目录:src/main/resources/META-INF/services

不幸的是,这没有帮助。

没有 spring 的情况下初始化自定义货币

我尝试只在主类中执行此操作,但没有解决问题。

问题

  1. 这个问题的根本原因是什么?
  2. 这个问题的正确解决方案是什么?

Mak*_*nin 6

长话短说

问题出在 Java 11 中并发的 moneta SPI 初始化中。

问题方案

可以通过提取MonetaryAmountFactory到 spring-bean 并在需要的地方注入来解决该问题:

@Bean
public MonetaryAmountFactory<?> money() {
    return Monetary.getDefaultAmountFactory();
}


@Component
@RequiredArgsConstructor
public static class Runner implements CommandLineRunner {

    private final MonetaryAmountFactory<?> amountFactory;

    @Override
    public void run(String... args) {
        var monetaryAmount = this.amountFactory
            .setCurrency("EUR")
            .setNumber(1)
            .create();

        System.out.println("monetaryAmount = " + monetaryAmount);
    }
}
Run Code Online (Sandbox Code Playgroud)

而不是直接使用这个工厂:

public static class Runner implements CommandLineRunner {

    @Override
    public void run(String... args) {
        var monetaryAmount = Monetary.getDefaultAmountFactory()
            .setCurrency("EUR")
            .setNumber(1)
            .create();

        System.out.println("monetaryAmount = " + monetaryAmount);
    }
}
Run Code Online (Sandbox Code Playgroud)

为什么 Kubernetes 集群会出现问题?

我发现上述 Kubernetes 集群上有不同的资源限制配置。

集群异常:

Limits:
  cpu:     6
  memory:  20G
Requests:
  cpu:      3
  memory:   20G
Run Code Online (Sandbox Code Playgroud)

集群无一例外:

Limits:
  cpu:     2
  memory:  2G
Requests:
  cpu:      2
  memory:   128Mi
Run Code Online (Sandbox Code Playgroud)

似乎拥有更多资源的集群为并发 moneta 初始化提供了更多机会。

最小可重复示例

可以在此 github-repository中找到最小的可重现示例。

值得一提的是,该 bug 在 Java 8 上并未重现。