Как динамически определить несколько заданий в Spring Batch?

У меня есть приложение, которое использует Spring Batch для определения заданного количества заданий, которые в настоящее время все определены в XML.

Со временем мы добавляем больше заданий, что требует обновления XML, однако эти задания всегда основаны на одном и том же родительском элементе и могут быть легко определены с помощью простого запроса SQL.

Поэтому я пытался переключиться на использование некоторой комбинации конфигурации XML и конфигурации на основе Java, но быстро запутался.

Несмотря на то, что у нас много рабочих мест, каждое определение работы подпадает под одну из двух категорий. Все задания наследуются от одного или другого родительского задания и фактически идентичны, кроме того, что имеют разные имена. Имя задания используется в процессе для выбора различных данных из базы данных.

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

Полный отказ от ответственности, что я также не совсем уверен, что я делаю это правильно. Подробнее об этом в секунду; во-первых, код:

@Configuration
@EnableBatchProcessing
public class DynamicJobConfigurer extends DefaultBatchConfigurer implements InitializingBean {

    @Autowired
    private JobBuilderFactory jobBuilderFactory;
    @Autowired
    private JobRegistry jobRegistry;
    @Autowired
    private DataSource dataSource;
    @Autowired
    private CustomJobDefinitionService customJobDefinitionService;

    private Flow injectedFlow1;
    private Flow injectedFlow2;

    public void setupJobs() throws DuplicateJobException {

        List<JobDefinition> jobDefinitions = customJobDefinitionService.getAllJobDefinitions();

        for (JobDefinition jobDefinition : jobDefinitions) {

            Job job = null;
            if (jobDefinition.getType() == 1) {
                job = jobBuilderFactory.get(jobDefinition.getName())
                        .start(injectedFlow1).build()
                        .build();
            } else if (jobDefinition.getType() == 2) {
                job = jobBuilderFactory.get(jobDefinition.getName())
                        .start(injectedFlow2).build()
                        .build();
            }

            if (job != null) {
                jobRegistry.register(new ReferenceJobFactory(job));
            }
        }
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        setupJobs();
    }

    public void setInjectedFlow1(Flow injectedFlow1) {
        this.injectedFlow1 = injectedFlow1;
    }

    public void setInjectedFlow2(Flow injectedFlow2) {
        this.injectedFlow2 = injectedFlow2;
    }
}

У меня есть потоки, которые вводятся, определены в XML, очень похоже на это:

<batch:flow id="injectedFlow1">

    <batch:step id="InjectedFlow1.Step1" next="InjectedFlow1.Step2">
        <batch:flow parent="InjectedFlow.Step1" />
    </batch:step>

    <batch:step id="InjectedFlow1.Step2">
        <batch:flow parent="InjectedFlow.Step2" />
    </batch:step>

</batch:flow>

Так что, как вы можете видеть, я эффективно setupJobs() метод (который предназначен для динамического создания этих определений заданий) из afterPropertiesSet() метод InitializingBean, Я не уверен, что это правильно. Он работает, но я не уверен, есть ли другая точка входа, которая лучше предназначена для этой цели. Также я не уверен, какой смысл @Configuration аннотация должна быть честной.

Проблема, с которой я сейчас сталкиваюсь, заключается в том, как только я позвоню register() от JobRegistry, бросает следующее IllegalStateException:

Чтобы использовать BatchConfigurer по умолчанию, контекст должен содержать не более одного источника данных, найдено 2.

Примечание. На самом деле в моем проекте определены два источника данных. Первый - это bean-компонент dataSource по умолчанию, который подключается к базе данных, которую использует Spring Batch. Второй источник данных - это внешняя база данных, и этот второй содержит всю информацию, необходимую для определения моего списка заданий. Но основной использует имя по умолчанию "dataSource", так что я не совсем уверен, как еще я могу сказать ему использовать его.

2 ответа

Решение

Прежде всего - я не рекомендую использовать комбинацию XML, а также конфигурацию Java. Используйте только один, предпочтительно Java, так как это не слишком большая задача для преобразования XML-конфигурации в Java-конфигурацию. (Если у вас нет очень веских причин для этого - то, что вы не объяснили)

Я не использовал Spring Batch в одиночку, так как я всегда использовал его с Spring Boot, и у меня есть проект, в котором я определил несколько заданий, и он всегда работал хорошо для аналогичного кода, который вы показали.

Для вашей проблемы есть несколько ответов на SO, например, это или это, которые в основном пытаются сказать, что вам нужно написать свой собственный BatchConfigurer, а не полагаться на стандартный.

Теперь пришло к решению с помощью Spring Boot

С помощью Spring Boot вы должны попытаться разделить определения заданий и выполнения заданий. Сначала вы должны попытаться просто определить задания и инициализировать контекст Spring без включения заданий (spring.batch.job.enabled=false)

В основном методе Spring Boot, когда вы запускаете приложение с чем-то вроде - SpringApplication.run(Application.class, args);...ты получишь ApplicationContext ctx

Теперь вы можете получить соответствующие бины из этого контекста и запускать определенные задания, получая имена из свойств или командной строки и т. Д. И используя JobLauncher.run(...) метод.

Вы можете отослать мой ответ, если хотите заказать выполнение работы. Вы также можете писать планировщики заданий, используя Java.

Суть в том, что вы разделяете свои задачи построения / конфигурации бинов и задачи выполнения заданий.

Вызов

Сохранение нескольких заданий в одном проекте может оказаться сложной задачей, если вы пытаетесь использовать разные настройки для каждого задания, как application.properties Файл зависит от среды и не зависит от задания, т.е. свойства весенней загрузки будут применяться ко всем заданиям.

В моем конкретном случае решение было на самом деле устранить @Configurationа также @EnableBatchProcessing аннотации из моего класса выше. Что-то из этого заставило его попытаться использовать DefaultBatchConfigurer, который завершается ошибкой, когда у вас определено более одного источника данных (даже если вы четко определили их с "dataSource" в качестве основного и некоторым другим именем для дополнительного).

@Configuration Класс, в частности, не был необходим, потому что все, что он действительно делает, - это позволяет вашему классу автоматически создаваться без определения его как компонента в контексте приложения. Но так как я делал это в любом случае, это было излишним.

Один из недостатков удаления @EnableBatchProcessing было то, что я больше не мог автоматически связывать bean-компонент JobBuilderFactory. Так что вместо этого я просто должен был сделать это:

    JobRepositoryFactoryBean factory = new JobRepositoryFactoryBean();
    factory.setDataSource(dataSource);
    factory.setTransactionManager(transactionManager);
    factory.afterPropertiesSet();
    jobRepository = factory.getObject();
    jobBuilderFactory = new JobBuilderFactory(jobRepository);

Тогда, кажется, я уже на правильном пути, используя jobRegistry.register(...) определить мою работу. Так что, по сути, как только я удалил эти аннотации, все начало работать Однако я собираюсь отметить ответ Сабира как правильный, потому что он помог мне.

Другие вопросы по тегам