Эй, ребята. В этом руководстве мы увидим, как выполняется задание Spring Batch с использованием планировщика Quartz. Если вы не уверены в основах Spring Batch, вы можете посетить мой учебник здесь .
Теперь, как мы знаем, задания Spring Batch используются всякий раз, когда мы хотим запустить какой-либо специфичный для бизнеса код или запустить / сгенерировать любые отчеты в любое конкретное время / день. Есть два способа реализации рабочих мест: tasklet
и chunks
. В этом уроке я создам простую работу, используя a tasklet
, которая напечатает logger
. Основная идея здесь заключается в том, какие конфигурации необходимы для запуска этого задания. Мы будем использовать Spring Boot для начальной загрузки нашего приложения.
В pom.xml нам требуется две зависимости для наличия Spring Batch и Quartz в нашем приложении.
<!-- https://mvnrepository.com/artifact/org.springframework.batch/spring-batch-core -->
<dependency>
<groupId>org.springframework.batch</groupId>
<artifactId>spring-batch-core</artifactId>
<version>4.0.1.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.quartz-scheduler/quartz -->
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.3.0</version>
</dependency>
Теперь давайте посмотрим, какие конфигурации нам нужны в нашем коде для запуска задания.
1. BatchConfiguration.java:
package com.category.batch.configurations;
import org.springframework.batch.core.configuration.JobRegistry;
import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
import org.springframework.batch.core.configuration.support.ApplicationContextFactory;
import org.springframework.batch.core.configuration.support.AutomaticJobRegistrar;
import org.springframework.batch.core.configuration.support.DefaultJobLoader;
import org.springframework.batch.core.configuration.support.JobRegistryBeanPostProcessor;
import org.springframework.batch.core.configuration.support.MapJobRegistry;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.batch.core.launch.support.SimpleJobLauncher;
import org.springframework.batch.core.repository.JobRepository;
import org.springframework.batch.core.repository.support.MapJobRepositoryFactoryBean;
import org.springframework.batch.support.transaction.ResourcelessTransactionManager;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
@Configuration
@EnableBatchProcessing
@Import({
BatchJobsDetailedConfiguration.class
})
public class BatchConfiguration {
@Bean
public JobRegistry jobRegistry() {
return new MapJobRegistry();
}
@Bean
public ResourcelessTransactionManager transactionManager() {
return new ResourcelessTransactionManager();
}
@Bean
public JobRepository jobRepository(ResourcelessTransactionManager transactionManager) throws Exception {
MapJobRepositoryFactoryBean mapJobRepositoryFactoryBean = new MapJobRepositoryFactoryBean(transactionManager);
mapJobRepositoryFactoryBean.setTransactionManager(transactionManager);
return mapJobRepositoryFactoryBean.getObject();
}
@Bean
public JobLauncher jobLauncher(JobRepository jobRepository) throws Exception {
SimpleJobLauncher simpleJobLauncher = new SimpleJobLauncher();
simpleJobLauncher.setJobRepository(jobRepository);
simpleJobLauncher.afterPropertiesSet();
return simpleJobLauncher;
}
@Bean
public JobRegistryBeanPostProcessor jobRegistryBeanPostProcessor(JobRegistry jobRegistry) {
JobRegistryBeanPostProcessor jobRegistryBeanPostProcessor = new JobRegistryBeanPostProcessor();
jobRegistryBeanPostProcessor.setJobRegistry(jobRegistry());
return jobRegistryBeanPostProcessor;
}
}
Пойдем один за другим:
-
@Configuration
Это указывает, что этот класс будет содержать bean-компоненты и будет создан во время загрузки. -
@EnableBatchProcessing
: Это включает функции Spring Batch и предоставляет базовую конфигурацию для настройки пакетных заданий. -
@Import({BatchJobsDetailedConfiguration.class})
: Это импортирует некоторые другие необходимые конфигурации, которые мы увидим позже. -
JobRegistry
: Этот интерфейс используется для регистрации заданий. -
ResourcelessTransactionManager
: Этот класс используется, когда вы хотите запустить задание, используя любое постоянство базы данных. -
JobRepository
: Содержит все метаданные задания, которые возвращаютMapJobRepositoryFactoryBean
использованные для непостоянных реализаций DAO. -
JobLauncher
: Используется для запуска задания, требует jobRepository в качестве зависимости. -
JobRegistryBeanPostProcessor
: Используется для регистрации задания вjobRegistry
, которое возвращаетjobRegistry
.
Теперь перейдем к импортированному классу.
2. BatchJobsDetailedConfiguration.java:
package com.category.batch.configurations;
import java.util.HashMap;
import java.util.Map;
import org.springframework.batch.core.configuration.JobRegistry;
import org.springframework.batch.core.configuration.support.ApplicationContextFactory;
import org.springframework.batch.core.configuration.support.GenericApplicationContextFactory;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.batch.core.launch.NoSuchJobException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.quartz.CronTriggerFactoryBean;
import org.springframework.scheduling.quartz.JobDetailFactoryBean;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import com.category.batch.job.JobLauncherDetails;
import com.category.batch.reports.config.ReportsConfig;
@Configuration
public class BatchJobsDetailedConfiguration {
@Autowired
private JobLauncher jobLauncher;
@Bean(name = "reportsDetailContext")
public ApplicationContextFactory getApplicationContext() {
return new GenericApplicationContextFactory(ReportsConfig.class);
}
@Bean(name = "reportsDetailJob")
public JobDetailFactoryBean jobDetailFactoryBean() {
JobDetailFactoryBean jobDetailFactoryBean = new JobDetailFactoryBean();
jobDetailFactoryBean.setJobClass(JobLauncherDetails.class);
jobDetailFactoryBean.setDurability(true);
Map < String, Object > map = new HashMap < > ();
map.put("jobLauncher", jobLauncher);
map.put("jobName", ReportsConfig.jobName);
jobDetailFactoryBean.setJobDataAsMap(map);
return jobDetailFactoryBean;
}
@Bean(name = "reportsCronJob")
public CronTriggerFactoryBean cronTriggerFactoryBean() {
CronTriggerFactoryBean cronTriggerFactoryBean = new CronTriggerFactoryBean();
cronTriggerFactoryBean.setJobDetail(jobDetailFactoryBean().getObject());
cronTriggerFactoryBean.setCronExpression("0 0/1 * 1/1 * ? *");
return cronTriggerFactoryBean;
}
@Bean
public SchedulerFactoryBean schedulerFactoryBean(JobRegistry jobRegistry) throws NoSuchJobException {
SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();
schedulerFactoryBean.setTriggers(cronTriggerFactoryBean().getObject());
schedulerFactoryBean.setAutoStartup(true);
Map < String, Object > map = new HashMap < > ();
map.put("jobLauncher", jobLauncher);
map.put("jobLocator", jobRegistry);
schedulerFactoryBean.setSchedulerContextAsMap(map);
return schedulerFactoryBean;
}
}
Давайте углубимся в это:
-
ApplicationContextFactory
: Этот интерфейс в первую очередь полезен при создании нового приApplicationContext
выполнении задания. Лучше создать отдельнуюapplicationContext
работу для каждой работы. -
JobDetailFactoryBean
: Используется для создания экземпляра детализации задания Quartz. Этот класс установит класс работы, который мы увидим позже. Он создает карту, которая будет определять и устанавливать имя задания с использованием класса иjoblauncher
. -
CronTriggerFactoryBean
: Используется для созданияcron
экземпляра триггера Quartz . Это установитjobDetail
созданное ранее, а затемcron
выражение, когда будет выполняться это задание. Вы можете установитьcron
выражения в соответствии с вашими потребностями. Выражения Cron могут быть рассчитаны с http://cronmaker.com. -
SchedulerFactoryBean
: Используется для создания экземпляра планировщика Quartz и позволяет для регистрацииJobDetails
,Calendars
иTriggers
, автоматический запуск планировщика на инициализацию и закрытие его на уничтожение.
Давайте проверим JobLauncherDetails
класс:
3. JobLauncherDetails.java:
package com.category.batch.job;
import java.util.Map;
import org.quartz.JobDataMap;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.JobParametersBuilder;
import org.springframework.batch.core.JobParametersInvalidException;
import org.springframework.batch.core.configuration.JobLocator;
import org.springframework.batch.core.configuration.JobRegistry;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.batch.core.launch.NoSuchJobException;
import org.springframework.batch.core.repository.JobExecutionAlreadyRunningException;
import org.springframework.batch.core.repository.JobInstanceAlreadyCompleteException;
import org.springframework.batch.core.repository.JobRestartException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.quartz.QuartzJobBean;
public class JobLauncherDetails extends QuartzJobBean {
static final String JOB_NAME = "jobName";
public void setJobLocator(JobLocator jobLocator) {
this.jobLocator = jobLocator;
}
public void setJobLauncher(JobLauncher jobLauncher) {
this.jobLauncher = jobLauncher;
}
private JobLocator jobLocator;
private JobLauncher jobLauncher;
@Override
protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {
JobParameters jobParameters = new JobParametersBuilder().addLong("time", System.currentTimeMillis()).toJobParameters();
try {
Map < String, Object > jobDataMap = jobExecutionContext.getMergedJobDataMap();
String jobName = (String) jobDataMap.get(JOB_NAME);
jobLauncher.run(jobLocator.getJob(jobName), jobParameters);
} catch (JobExecutionAlreadyRunningException | JobRestartException | JobInstanceAlreadyCompleteException
|
JobParametersInvalidException e) {
e.printStackTrace();
} catch (NoSuchJobException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
Этот класс имеет переопределенный executeInternal
метод QuartzJobBean
класса, который берет jobdetails
из карты, которые уже были установлены перед некоторыми из jobParameters
, и затем выполняет, jobLauncher.run()
чтобы выполнить задание, как видно из кода.
Давайте посетим ReportsConfig
класс.
4.ReportsConfig.java:
package com.category.batch.reports.config;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.configuration.DuplicateJobException;
import org.springframework.batch.core.configuration.JobRegistry;
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
import org.springframework.batch.core.configuration.support.ReferenceJobFactory;
import org.springframework.batch.core.step.tasklet.Tasklet;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.category.batch.reports.tasklet.ReportTasklet;
@Configuration
public class ReportsConfig {
@Autowired
private JobRegistry jobRegistry;
public final static String jobName = "ReportsJob1";
public JobBuilderFactory getJobBuilderFactory() {
return jobBuilderFactory;
}
public void setJobBuilderFactory(JobBuilderFactory jobBuilderFactory) {
this.jobBuilderFactory = jobBuilderFactory;
}
@Autowired
private Tasklet taskletstep;
@Autowired
private JobBuilderFactory jobBuilderFactory;
@Autowired
private StepBuilderFactory stepBuilderFactory;
@Bean
public ReportTasklet reportTasklet() {
return new ReportTasklet();
}
@Bean
public Job job() throws DuplicateJobException {
Job job = getJobBuilderFactory().get(jobName).start(getStep()).build();
return job;
}
@Bean
public Step getStep() {
return stepBuilderFactory.get("step").tasklet(reportTasklet()).build();
}
}
Основное назначение класса — иметь конфигурации, связанные с каждой работой. У вас будет отдельный конфиг для каждой работы, как это. Как видите, мы создаем tasklet
здесь, что мы увидим позже. Кроме того, мы определяем и возвращаем Job
, шаг, используя JobBuilderFactory
, и StepBuilderFactory
. Эти фабрики автоматически установят JobRepository
для вас.
Давайте перейдем к тому ReportTasklet
, что наша работа должна быть запущена.
5. ReportTasklet.java:
package com.category.batch.reports.tasklet;
import java.util.logging.Logger;
import org.springframework.batch.core.StepContribution;
import org.springframework.batch.core.scope.context.ChunkContext;
import org.springframework.batch.core.step.tasklet.Tasklet;
import org.springframework.batch.repeat.RepeatStatus;
import org.springframework.context.annotation.Configuration;
@Configuration
public class ReportTasklet implements Tasklet {
private static final Logger logger = Logger.getLogger(ReportTasklet.class.getName());
@Override
public RepeatStatus execute(StepContribution arg0, ChunkContext arg1) {
try {
logger.info("Report's Job is running. Add your business logic here.........");
} catch (Exception e) {
e.printStackTrace();
}
return RepeatStatus.FINISHED;
}
}
Этот класс имеет метод execute, который будет запускаться при выполнении задания jobLauncher.run()
из JobLauncherDetails
класса. Вы можете определить свою бизнес-логику, которая должна быть выполнена здесь.
Нам понадобится некоторая конфигурация в application.properties, как показано ниже:
6. application.properties
spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
spring.batch.job.enabled=false
Первое свойство требуется для отключения источника данных — только для целей тестирования и не требуется в рабочей среде.
Второе свойство — когда перед запуском сервера выполняется задание. Чтобы избежать этого, нам нужно это свойство.
Теперь, наконец, перейдем к классу приложения. Это должно быть само за себя.
7. BatchApplication.java:
package com.category.batch;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration;
@SpringBootApplication(exclude={DataSourceAutoConfiguration.class})
public class BatchApplication {
public static void main(String[] args) {
SpringApplication.run(BatchApplication.class, args);
}
}
Достаточно конфигураций! Давайте запустим это приложение и посмотрим вывод. Мы установили на cron
1 минуту. Через 1 минуту задание будет запущено.
2018-09-14 11:04:18.648 INFO 7008 --- [ost-startStop-1] org.quartz.impl.StdSchedulerFactory : Quartz scheduler 'schedulerFactoryBean' initialized from an externally provided properties instance.
2018-09-14 11:04:18.648 INFO 7008 --- [ost-startStop-1] org.quartz.impl.StdSchedulerFactory : Quartz scheduler version: 2.3.0
2018-09-14 11:04:18.653 INFO 7008 --- [ost-startStop-1] org.quartz.core.QuartzScheduler : JobFactory set to: org.springframework.scheduling.quartz.AdaptableJobFactory@b29e7d6
2018-09-14 11:04:19.578 INFO 7008 --- [ost-startStop-1] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped URL path [/**/favicon.ico] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2018-09-14 11:04:20.986 INFO 7008 --- [ost-startStop-1] s.w.s.m.m.a.RequestMappingHandlerAdapter : Looking for @ControllerAdvice: org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@74a6e16d: startup date [Fri Sep 14 11:04:12 IST 2018]; root of context hierarchy
2018-09-14 11:04:21.264 INFO 7008 --- [ost-startStop-1] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error]}" onto public org.springframework.http.ResponseEntity<java.util.Map<java.lang.String, java.lang.Object>> org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController.error(javax.servlet.http.HttpServletRequest)
2018-09-14 11:04:21.268 INFO 7008 --- [ost-startStop-1] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error],produces=[text/html]}" onto public org.springframework.web.servlet.ModelAndView org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController.errorHtml(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)
2018-09-14 11:04:21.356 INFO 7008 --- [ost-startStop-1] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped URL path [/webjars/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2018-09-14 11:04:21.356 INFO 7008 --- [ost-startStop-1] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped URL path [/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2018-09-14 11:04:22.526 INFO 7008 --- [ost-startStop-1] o.s.j.e.a.AnnotationMBeanExporter : Registering beans for JMX exposure on startup
2018-09-14 11:04:22.555 INFO 7008 --- [ost-startStop-1] o.s.c.support.DefaultLifecycleProcessor : Starting beans in phase 2147483647
2018-09-14 11:04:22.556 INFO 7008 --- [ost-startStop-1] o.s.s.quartz.SchedulerFactoryBean : Starting Quartz Scheduler now
2018-09-14 11:04:22.556 INFO 7008 --- [ost-startStop-1] org.quartz.core.QuartzScheduler : Scheduler schedulerFactoryBean_$_NON_CLUSTERED started.
2018-09-14 11:04:22.578 INFO 7008 --- [ost-startStop-1] com.category.batch.ServletInitializer : Started ServletInitializer in 17.386 seconds (JVM running for 26.206)
2018-09-14 11:04:23.395 INFO 7008 --- [ main] org.apache.coyote.ajp.AjpNioProtocol : Starting ProtocolHandler ["ajp-nio-8009"]
2018-09-14 11:04:23.399 INFO 7008 --- [ main] org.apache.catalina.startup.Catalina : Server startup in 24866 ms
2018-09-14 11:05:02.889 INFO 7008 --- [ryBean_Worker-1] o.s.b.c.l.support.SimpleJobLauncher : Job: [SimpleJob: [name=ReportsJob1]] launched with the following parameters: [{time=1536903301155}]
2018-09-14 11:05:03.262 INFO 7008 --- [ryBean_Worker-1] o.s.batch.core.job.SimpleStepHandler : Executing step: [step]
2018-09-14 11:05:03.503 INFO 7008 --- [ryBean_Worker-1] c.c.batch.reports.tasklet.ReportTasklet : Report's Job is running. Add your business logic here.........
2018-09-14 11:05:03.524 INFO 7008 --- [ryBean_Worker-1] o.s.b.c.l.support.SimpleJobLauncher : Job: [SimpleJob: [name=ReportsJob1]] completed with the following parameters: [{time=1536903301155}] and the following status: [COMPLETED]
Жирные линии указывают на то, что ваша работа выполнена и успешно выполнена. Вот и все для этого урока. Пожалуйста, прокомментируйте, если вы хотите что-то добавить. Счастливого обучения!