Java _org.javamoney:moneta:1.3
_からのGradle依存関係を持つプロジェクト)があります。
また、2つのKubernetesクラスタがあります。 Javaアプリケーションをdocker-containerを使用してデプロイします。
firstKubernetesクラスターにアプリをデプロイすると、すべてがうまくいきます。しかし、アプリ(同じdocker-container)をsecondKubernetesクラスターにデプロイすると、次のエラーが表示されます。
_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)
_
次のコードに表示されます。
_MonetaryAmount amount = javax.money.Monetary.getDefaultAmountFactory()
.setCurrency("USD")
.setNumber(1L)
.create();
_
1.3
_。6.0.1
_。openjdk:11.0.7-jdk-slim
_。2.2.7.RELEASE
_。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依存関係をいくつかの異なる方法で宣言しようとするアイデアを与えました。私が試してみました:
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から を次のプロジェクトディレクトリにコピーしようとしました:_src/main/resources/META-INF/services
_。
残念ながら、それは役に立ちませんでした。
Mainクラスだけで試してみましたが、問題は解決しませんでした。
問題は同時モネタにありましたSPI初期化Java 11。
この問題は、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);
}
}
このファクトリを直接使用する代わりに:
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);
}
}
上記のKubernetes-clusterに異なる resource limit configuration があることがわかりました。
例外のあるクラスター:
Limits:
cpu: 6
memory: 20G
Requests:
cpu: 3
memory: 20G
例外なくクラスター:
Limits:
cpu: 2
memory: 2G
Requests:
cpu: 2
memory: 128Mi
より多くのリソースを持つクラスターは、同時に発生するモネタ初期化により多くの機会を与えるようです。
最小限の再現可能な例は this github-repository にあります。
バグがJava 8.で再現されないことは言及する価値があります。
回避策として、次のようなサービスプロバイダーを作成できます。
public class MyServiceLoader implements ServiceProvider {
/**
* List of services loaded, per class.
*/
private final ConcurrentHashMap<Class<?>, List<Object>> servicesLoaded = new ConcurrentHashMap<>();
private static final int PRIORITY = 10;
/**
* Returns a priority value of 10.
*
* @return 10, overriding the default provider.
*/
@Override
public int getPriority() {
return PRIORITY;
}
/**
* Loads and registers services.
*
* @param serviceType The service type.
* @param <T> the concrete type.
* @return the items found, never {@code null}.
*/
@Override
public <T> List<T> getServices(final Class<T> serviceType) {
@SuppressWarnings("unchecked")
List<T> found = (List<T>) servicesLoaded.get(serviceType);
if (found != null) {
return found;
}
return loadServices(serviceType);
}
public static int compareServices(Object o1, Object o2) {
int prio1 = 0;
int prio2 = 0;
Priority prio1Annot = o1.getClass().getAnnotation(Priority.class);
if (prio1Annot != null) {
prio1 = prio1Annot.value();
}
Priority prio2Annot = o2.getClass().getAnnotation(Priority.class);
if (prio2Annot != null) {
prio2 = prio2Annot.value();
}
if (prio1 < prio2) {
return 1;
}
if (prio2 < prio1) {
return -1;
}
return o2.getClass().getSimpleName().compareTo(o1.getClass().getSimpleName());
}
/**
* Loads and registers services.
*
* @param serviceType The service type.
* @param <T> the concrete type.
* @return the items found, never {@code null}.
*/
private <T> List<T> loadServices(final Class<T> serviceType) {
List<T> services = new ArrayList<>();
try {
for (T t : ServiceLoader.load(serviceType, Monetary.class.getClassLoader())) {
services.add(t);
}
services.sort(CbplMonetaServiceProvider::compareServices);
@SuppressWarnings("unchecked") final List<T> previousServices = (List<T>) servicesLoaded.putIfAbsent(serviceType, (List<Object>) services);
return Collections.unmodifiableList(previousServices != null ? previousServices : services);
} catch (Exception e) {
Logger.getLogger(CbplMonetaServiceProvider.class.getName()).log(Level.WARNING,
"Error loading services of type " + serviceType, e);
services.sort(CbplMonetaServiceProvider::compareServices);
return services;
}
}
}
とお金のライブラリクラスの呼び出しを使用する前に
Bootstrap.init(new CbplMonetaServiceProvider());
これにより、通貨エラーも修正されます。
priorityAwareServiceProviderと比較して追加したプロバイダーの唯一の変更された行はこの行です
for(T service:ServiceLoader.load(serviceType, Monetary.class.getClassLoader())){
クラスローダーを指定したので、Thread.getCurrentThread()。getClassLoader()の代わりに、提供されているクラスローダーを使用しています。