У меня есть проект, основанный на Java 17 и Springboot 3. Недавно я добавил в проект асинхронный метод. Класс асинхронной конфигурации следующий:
@Configuration
@EnableAsync
public class AsyncConfig {
@Bean(name = "taskExecutor")
public Executor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(5);
executor.setMaxPoolSize(10);
executor.setQueueCapacity(100);
executor.setThreadNamePrefix("Async-");
executor.initialize();
return executor;
}
}
А асинхронный метод выглядит так:
@Async("taskExecutor")
public CompletableFuture<Response<Exception>> createOrUpdateEntryAsync(
.....,
@Nullable ProgressListener progressListener) {
return CompletableFuture.supplyAsync(() -> createOrUpdateEntry(
......,
progressListener));
}
Во время локальной разработки и отладки с использованием IntelliJ этот метод работает нормально. Однако после того, как я использовал Docker для упаковки его в образ (используя openjdk:17-jdk-slim в качестве базового образа) и развернул его на сервере, при вызове этого асинхронного метода возникает ошибка: «Реализация JAXB-API не была найдено по пути к модулю или пути к классам». Я искал ответ, нашел несколько подсказок и добавил следующую зависимость Maven, но это не решает проблему:
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.1</version>
</dependency>
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
<version>2.3.2</version>
</dependency>
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-core</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-impl</artifactId>
<version>2.3.3</version>
</dependency>
Вот часть трассировки исключений ниже моих кодов проекта:
java.base/java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java:1768)
java.base/java.util.concurrent.CompletableFuture$AsyncSupply.exec(CompletableFuture.java:1760)
java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:373)
java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1182)
java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1655)
java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1622)
java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:165)
Может ли кто-нибудь помочь мне в этом? Заранее благодарим за помощь!
Сейчас я использую jaxb-bom версии 4.0.2 для управления версией, но это не помогает. Я не понимаю, почему эти ошибки возникают только при использовании асинхронного метода, когда он запускается в контейнере докеров. Есть ли какой-нибудь трюк в докере, который я пропустил?
Я узнал, что пошло не так. JAXB API кажется невидимым в новом потоке, созданном асинхронным методом. Загрузчик контекстного класса должен быть установлен явно, как показано ниже:
@Async("taskExecutor")
public CompletableFuture<Response<Exception>> createOrUpdateEntryAsync(
.....,
@Nullable ProgressListener progressListener) {
ClassLoader contextClassLoader = this.getClass().getClassLoader();
log.debug("Context ClassLoader: " + contextClassLoader);
return CompletableFuture.supplyAsync(() -> {
Thread.currentThread().setContextClassLoader(contextClassLoader);
createOrUpdateEntry(
......,
progressListener));
}
}
jaxb-runtime
должно быть достаточно, но попробуйте использовать современную версию библиотеки (больше шансов быть совместимой с последней весенней загрузкой) — текущая версия4.0.5