Параллельное выполнение инициализирующих компонентов Spring - PullRequest
0 голосов
/ 03 марта 2020

У меня есть несколько классов Spring InitializingBean, и я бы хотел, чтобы все они запускали afterPropertiesSet() параллельно. Когда я запускаю небольшой пример, они выполняются синхронно. Есть ли способ выполнить их параллельно?

Ниже приведен пример инициализирующего компонента, который можно использовать для проверки того, что я имею в виду. При создании нескольких таких классов (например, InitBeanOne, InitBeanTwo, ...) журналы показывают, что они запускаются синхронно.

Одна идея, которую я имел в виду, состояла в том, чтобы иметь одну инициализацию Бин асинхронно инициализирует нужные классы. Однако это последний вариант, поскольку я хотел бы воспользоваться преимуществами инициализации bean-компонентов для каждого класса отдельно, а не иметь других зависимых классов.

@Component
public class InitBean implements InitializingBean {

    private final static Logger LOGGER = LoggerFactory.getLogger(InitBean.class);

    @Override
    public void afterPropertiesSet() throws Exception {
        LOGGER.info("BEGIN: InitBean");
        TimeUnit.SECONDS.sleep(5);
        LOGGER.info("END: InitBean");
    }
}

1 Ответ

1 голос
/ 03 марта 2020

Необходимо переместить код в метод прослушивания событий и пометить метод с помощью @Async.

. Убедитесь, что функция Asyn c установлена ​​правильно. См .: Как сделать @ Asyn c в Spring .

Вы должны сделать так, чтобы метод запускался, когда среда Spring запускает ApplicationReadyEvent.

@Component
public class InitBean {
    private final static Logger LOGGER = LoggerFactory.getLogger(InitBean.class);

    @Async
    @EventListener
    public void onApplicationReady(ApplicationReadyEvent event) throws Exception {
        LOGGER.info("BEGIN: onApplicationReady");
        TimeUnit.SECONDS.sleep(5);
        LOGGER.info("END: onApplicationReady");
    }
}

Предупреждение: При этом другие методы могут вызываться до / во время вызова этого метода. Если метод выполняет какую-либо инициализацию, необходимую для этих других методов, вам необходимо это обработать, например, используя CountDownLatch.


UPDATE

Если вам нужно, чтобы приложение задержало завершение последовательности запуска до завершения всех асинхронных методов, я думаю, вам нужно обработать его самостоятельно.

Создать интерфейс AsyncInitializingBean тем же методом, что и InitializingBean, затем создайте @Component с именем AsyncBeanInitializer с автоматическим подключением AsyncInitializingBean[] (или List), а затем выполните все методы, используя ExecutorService в ContextRefreshedEvent .

@Component
public class InitBean implements AsyncInitializingBean { // <== Change interface (only change needed)

    private final static Logger LOGGER = LoggerFactory.getLogger(InitBean.class);

    @Override
    public void afterPropertiesSet() throws Exception {
        LOGGER.info("BEGIN: InitBean");
        TimeUnit.SECONDS.sleep(5);
        LOGGER.info("END: InitBean");
    }
}
public interface AsyncInitializingBean {
    void afterPropertiesSet() throws Exception;
}
@Component
public class AsyncBeanInitializer {
    private final static Logger LOGGER = LoggerFactory.getLogger(AsyncBeanInitializer.class);

    @Autowired(required = false)
    private AsyncInitializingBean[] beans;

    @EventListener
    public void onContextRefreshed(@SuppressWarnings("unused") ContextRefreshedEvent event) throws Exception {
        if (this.beans == null || this.beans.length == 0)
            return;
        ExecutorService executorService = Executors.newWorkStealingPool();
        try {
            AtomicInteger failed = new AtomicInteger();
            for (AsyncInitializingBean bean : beans) {
                executorService.submit(() -> {
                    try {
                        bean.afterPropertiesSet();
                    } catch (Exception e) {
                        failed.incrementAndGet();
                        LOGGER.error("Async afterPropertiesSet() method failed: " + e, e);
                    }
                });
            }
            executorService.shutdown();
            executorService.awaitTermination(60, TimeUnit.MINUTES);
            if (failed.get() != 0)
                throw new RuntimeException(failed.get() + " Async afterPropertiesSet() methods failed. See log for details.");
        } finally {
            executorService.shutdownNow();
        }
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...