前面一节中已经介绍了基于xml配置方式整合Spring boot + Spring batch + Quartz ,本节介绍基于编码配置方式整合Spring boot + Spring batch + Quartz。具体spring batch和quartz的使用请看其它参考,下面进行整合。
案例:定时,在距当前5s后,每隔3s执行一次job任务,job任务是读取User.txt文件中内容,并把每条记录中年纪为偶数的记录打印出来。
User.txt中的内容为:
ID,NAME,AGE
1,lzj,28
2,tom,20
3,terry,30
4,lerry,18
5,bob,25
6,linda,27
7,marry,39
8,long,22
9,kin,33
10,jiken,40
一、创建一个spring boot工程,并引入下面所需要的依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.lzj</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>demo</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.10.RELEASE</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-batch</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-oxm</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!-- <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId>
<scope>runtime</scope> </dependency> -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.batch</groupId>
<artifactId>spring-batch-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
创建User.txt中每条记录对应的POJO
package com.lzj.springbatch.model;
public class User {
private String id;
private String name;
private String age;
/*省略get/set/toString方法*/
}
二、创建spring batch的job,并配置job
1、创建spring batch的读文件类。spring batch中提供了用于读文件的类
FlatFileItemReader,在此只需继承该类,并设置读文件的属性
package com.lzj.springbatch.reader;
import org.springframework.batch.item.file.FlatFileItemReader;
import org.springframework.batch.item.file.LineMapper;
import org.springframework.batch.item.file.mapping.DefaultLineMapper;
import org.springframework.batch.item.file.mapping.FieldSetMapper;
import org.springframework.batch.item.file.transform.DelimitedLineTokenizer;
import org.springframework.batch.item.file.transform.FieldSet;
import org.springframework.batch.item.file.transform.LineTokenizer;
import org.springframework.core.io.ClassPathResource;
import org.springframework.validation.BindException;
import com.lzj.springbatch.model.User;
public class MyReader extends FlatFileItemReader<User> {
public MyReader(){
createReader();
}
private void createReader(){
this.setResource(new ClassPathResource("data/User.txt"));
this.setLinesToSkip(1);
this.setLineMapper(userLineMapper());
}
private LineMapper<User> userLineMapper(){
DefaultLineMapper<User> lineMapper = new DefaultLineMapper<>();
lineMapper.setLineTokenizer(userLineTokenizer());
lineMapper.setFieldSetMapper(new UserFieldStepMapper());
lineMapper.afterPropertiesSet();
return lineMapper;
}
private LineTokenizer userLineTokenizer(){
DelimitedLineTokenizer tokenizer = new DelimitedLineTokenizer();
tokenizer.setNames(new String[]{"ID", "NAME", "AGE"});
return tokenizer;
}
private static class UserFieldStepMapper implements FieldSetMapper<User>{
@Override
public User mapFieldSet(FieldSet fieldSet) throws BindException {
return new User(fieldSet.readString("ID"),
fieldSet.readString("NAME"),
fieldSet.readString("AGE"));
}
}
}
2、创建spring batch的处理类,实现ItemProcessor接口,覆写process方法,reader方法中读取文件一条记录放入User对象中,在process方法中处理User对象,只选择年龄为偶数的User对象传给spring batch的writer类
package com.lzj.springbatch.processor;
import org.springframework.batch.item.ItemProcessor;
import com.lzj.springbatch.model.User;
public class MyProcessor implements ItemProcessor<User, User> {
@Override
public User process(User item) throws Exception {
if (Integer.parseInt(item.getAge()) % 2 == 0) {
return item;
}
return null;
}
}
3、创建spring batch的writer类,只需继承ItemWriter接口,覆写write方法即可。打印出处理类处理后的User对象
package com.lzj.springbatch.writer;
import java.util.List;
import org.springframework.batch.item.ItemWriter;
import com.lzj.springbatch.model.User;
public class MyWriter implements ItemWriter<User> {
@Override
public void write(List<? extends User> items) throws Exception {
for(User user : items){
System.out.println(user);
}
}
}
4、创建job,并配置job
package com.lzj.springbatch.config;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import com.lzj.quartz.QuartzConfiguration;
import com.lzj.springbatch.model.User;
import com.lzj.springbatch.processor.MyProcessor;
import com.lzj.springbatch.reader.MyReader;
import com.lzj.springbatch.writer.MyWriter;
@Configuration
@EnableBatchProcessing
//@Import({QuartzConfiguration.class})
public class BatchConfiguration {
@Autowired
public JobBuilderFactory jobBuilderFactory;
@Autowired
public StepBuilderFactory stepBuilderFactory;
/*创建job*/
@Bean
public Job jobMethod(){
return jobBuilderFactory.get("lzjJob")
.start(stepMethod())
.build();
}
/*创建step*/
@Bean
public Step stepMethod(){
return stepBuilderFactory.get("myStep")
.<User, User>chunk(3)
.reader(new MyReader())
.processor(new MyProcessor())
.writer(new MyWriter())
.allowStartIfComplete(true)
.build();
}
}
三、用quartz进行定时配置job任务
spring家族中的spring-context-support 的jar包整合了quartz定时,用来整合spring batch和quartz的开发。
1、创建执行spring batch的job的接口。spring-context-support包中的QuartzJobBean是quartz执行spring batch的job的接口,只需实现该接口,并覆写executeInternal方法,在executeInternal方法中启动job。并在后面介绍定时执行该接口实现类中的executeInternal方法,达到定时执行spring batch 的job的功能。
package com.lzj.quartz;
import org.quartz.JobDataMap;
import org.quartz.JobDetail;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.JobKey;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.configuration.JobLocator;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.scheduling.quartz.QuartzJobBean;
public class QuartzJobLauncher extends QuartzJobBean {
/*方式一*/
// private String jobName;
// private JobLauncher jobLauncher;
// private JobLocator jobLocator;
//
// public String getJobName() {
// return jobName;
// }
//
// public void setJobName(String jobName) {
// this.jobName = jobName;
// }
//
// public JobLauncher getJobLauncher() {
// return jobLauncher;
// }
//
// public void setJobLauncher(JobLauncher jobLauncher) {
// this.jobLauncher = jobLauncher;
// }
//
// public JobLocator getJobLocator() {
// return jobLocator;
// }
//
// public void setJobLocator(JobLocator jobLocator) {
// this.jobLocator = jobLocator;
// }
//
@Override
protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
/*方式二*/
JobDetail jobDetail = context.getJobDetail();
JobDataMap jobDataMap = jobDetail.getJobDataMap();
String jobName = jobDataMap.getString("jobName");
JobLauncher jobLauncher = (JobLauncher) jobDataMap.get("jobLauncher");
JobLocator jobLocator = (JobLocator) jobDataMap.get("jobLocator");
System.out.println("jobName : " + jobName);
System.out.println("jobLauncher : " + jobLauncher);
System.out.println("jobLocator : " + jobLocator);
JobKey key = context.getJobDetail().getKey();
System.out.println(key.getName() + " : " + key.getGroup());
try {
Job job = jobLocator.getJob(jobName);
/*启动spring batch的job*/
JobExecution jobExecution = jobLauncher.run(job, new JobParameters());
} catch (Exception e) {
e.printStackTrace();
}
}
}
2、创建定时器,定时触发spring batch 的job任务
package com.lzj.quartz;
import java.util.HashMap;
import java.util.Map;
import org.springframework.batch.core.configuration.JobLocator;
import org.springframework.batch.core.configuration.JobRegistry;
import org.springframework.batch.core.configuration.support.JobRegistryBeanPostProcessor;
import org.springframework.batch.core.launch.JobLauncher;
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;
@Configuration
public class QuartzConfiguration {
//自动注入进来的是SimpleJobLauncher
@Autowired
private JobLauncher jobLauncher;
@Autowired
private JobLocator jobLocator;
/*用来注册job*/
/*JobRegistry会自动注入进来*/
@Bean
public JobRegistryBeanPostProcessor jobRegistryBeanPostProcessor(JobRegistry jobRegistry){
JobRegistryBeanPostProcessor jobRegistryBeanPostProcessor = new JobRegistryBeanPostProcessor();
jobRegistryBeanPostProcessor.setJobRegistry(jobRegistry);
return jobRegistryBeanPostProcessor;
}
@Bean
public JobDetailFactoryBean jobDetailFactoryBean(){
JobDetailFactoryBean jobFactory = new JobDetailFactoryBean();
jobFactory.setJobClass(QuartzJobLauncher.class);
jobFactory.setGroup("my_group");
jobFactory.setName("my_job");
Map<String, Object> map = new HashMap<>();
map.put("jobName", "lzjJob");
map.put("jobLauncher", jobLauncher);
map.put("jobLocator", jobLocator);
jobFactory.setJobDataAsMap(map);
return jobFactory;
}
@Bean
public CronTriggerFactoryBean cronTriggerFactoryBean(){
CronTriggerFactoryBean cTrigger = new CronTriggerFactoryBean();
System.out.println("------- : " + jobDetailFactoryBean().getObject());
cTrigger.setJobDetail(jobDetailFactoryBean().getObject());
cTrigger.setStartDelay(5000);
cTrigger.setName("my_trigger");
cTrigger.setGroup("trigger_group");
cTrigger.setCronExpression("0/3 * * * * ? "); //每间隔5s触发一次Job任务
return cTrigger;
}
@Bean
public SchedulerFactoryBean schedulerFactoryBean(){
SchedulerFactoryBean schedulerFactor = new SchedulerFactoryBean();
schedulerFactor.setTriggers(cronTriggerFactoryBean().getObject());
return schedulerFactor;
}
}
四、Run
启动spring boot工程,启动类为
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args){
SpringApplication.run(DemoApplication.class, args);
}
}
在application.properties中添加
spring.batch.job.enabled=false
表示在启动工程的时候不执行job,在定时触发时再执行job任务。
执行启动类,输出日志如下:
……
jobName : lzjJob
jobLauncher : org.springframework.batch.core.launch.support.SimpleJobLauncher@26adcd69
jobLocator : org.springframework.batch.core.configuration.support.MapJobRegistry@62d05170
my_job : my_group
Current time : 2018-04-12 22:48:39
2018-04-12 22:48:39.066 INFO 1820 --- [ryBean_Worker-1] o.s.b.c.l.support.SimpleJobLauncher : Job: [SimpleJob: [name=lzjJob]] launched with the following parameters: [{}]
2018-04-12 22:48:39.084 INFO 1820 --- [ryBean_Worker-1] o.s.batch.core.job.SimpleStepHandler : Executing step: [myStep]
User [id=1, name=lzj, age=28]
User [id=2, name=tom, age=20]
User [id=3, name=terry, age=30]
User [id=4, name=lerry, age=18]
User [id=8, name=long, age=22]
User [id=10, name=jiken, age=40]
2018-04-12 22:48:39.119 INFO 1820 --- [ryBean_Worker-1] o.s.b.c.l.support.SimpleJobLauncher : Job: [SimpleJob: [name=lzjJob]] completed with the following parameters: [{}] and the following status: [COMPLETED]
jobName : lzjJob
jobLauncher : org.springframework.batch.core.launch.support.SimpleJobLauncher@26adcd69
jobLocator : org.springframework.batch.core.configuration.support.MapJobRegistry@62d05170
my_job : my_group
Current time : 2018-04-12 22:48:42
2018-04-12 22:48:42.021 INFO 1820 --- [ryBean_Worker-2] o.s.b.c.l.support.SimpleJobLauncher : Job: [SimpleJob: [name=lzjJob]] launched with the following parameters: [{}]
2018-04-12 22:48:42.029 INFO 1820 --- [ryBean_Worker-2] o.s.batch.core.job.SimpleStepHandler : Executing step: [myStep]
User [id=1, name=lzj, age=28]
User [id=2, name=tom, age=20]
User [id=3, name=terry, age=30]
User [id=4, name=lerry, age=18]
User [id=8, name=long, age=22]
User [id=10, name=jiken, age=40]
2018-04-12 22:48:42.046 INFO 1820 --- [ryBean_Worker-2] o.s.b.c.l.support.SimpleJobLauncher : Job: [SimpleJob: [name=lzjJob]] completed with the following parameters: [{}] and the following status: [COMPLETED]
jobName : lzjJob
jobLauncher : org.springframework.batch.core.launch.support.SimpleJobLauncher@26adcd69
jobLocator : org.springframework.batch.core.configuration.support.MapJobRegistry@62d05170
my_job : my_group
Current time : 2018-04-12 22:48:45
2018-04-12 22:48:45.009 INFO 1820 --- [ryBean_Worker-3] o.s.b.c.l.support.SimpleJobLauncher : Job: [SimpleJob: [name=lzjJob]] launched with the following parameters: [{}]
2018-04-12 22:48:45.016 INFO 1820 --- [ryBean_Worker-3] o.s.batch.core.job.SimpleStepHandler : Executing step: [myStep]
User [id=1, name=lzj, age=28]
User [id=2, name=tom, age=20]
User [id=3, name=terry, age=30]
User [id=4, name=lerry, age=18]
User [id=8, name=long, age=22]
User [id=10, name=jiken, age=40]
2018-04-12 22:48:45.039 INFO 1820 --- [ryBean_Worker-3] o.s.b.c.l.support.SimpleJobLauncher : Job: [SimpleJob: [name=lzjJob]] completed with the following parameters: [{}] and the following status: [COMPLETED]
……
从日志中可以看出,每3s解析一次User.txt文件,并把满足条件的记录打印出来。
工程目录为:
源码参考位置:https://github.com/shuniversity/springboot-quartz-springbatch