Spring batch 系列文章
Spring Batch教程(一) 简单的介绍以及通过springbatch将xml文件转成txt文件
Spring Batch教程(二)示例:将txt文件转成xml文件以及读取xml文件内容存储到数据库mysql
Spring Batch教程(三)示例:从mysql中读取数据写入文本和从多个文本中读取内容写入mysql
Spring Batch教程(四)tasklet使用示例:spring batch的定时任务使用
Spring Batch教程(五)spring boot实现batch功能注解示例:读写文本文件
Spring Batch教程(六)spring boot实现batch功能注解示例:读文件写入mysql
文章目录
本文简单的介绍了springbatch的功能、架构、配置示例以及以读取xml文件经过转换输出成txt文件的示例。本文是该系列的第一篇,后续有更多的介绍。
本文使用的是jdk8版本,最新版本的spring core和springb batch用不了。
本文分为2个部分,即springbatch介绍和示例。
一、spring batch介绍
1、介绍
Spring Batch 作为 Spring 的子项目,是一款基于 Spring 的企业批处理框架。通过它可以构建出健壮的企业批处理应用。Spring Batch 不仅提供了统一的读写接口、丰富的任务处理方式、灵活的事务管理及并发处理,同时还支持日志、监控、任务重启与跳过等特性,大大简化了批处理应用开发,将开发人员从复杂的任务配置管理过程中解放出来,使他们可以更多地去关注核心的业务处理过程。
另外还需要知道,Spring Batch 是一款批处理应用框架,不是调度框架。它只关注批处理任务相关的问题,如事务、并发、监控、执行等,并不提供相应的调度功能。因此,如果希望批处理任务定期执行,可结合 Quartz 等成熟的调度框架实现。
2、架构
一个job由一到多个步骤组成。每个步骤可以在两种模式下工作:
- Chunk Oriented Processing or READ-PROCESS-WRITE mode
step是从资源读取,处理数据,然后将处理后的数据写入到一个输出源。在这种方法中,只有一个 ItemReader(从资源读取的读取器,无论是文件、数据库、消息队列等)、ItemProcessor(提供应用业务逻辑的钩子)和 ItemWriter(写入资源的编写器,无论是文件、数据库、消息队列等)。 - TASKLET mode
step必须执行单个操作(无论是发送电子邮件、执行存储过程、清理超过 x 天的文件等)。在这种方法中,包括一个 Tasklet 接口,该接口只有一个可以执行上述活动的方法执行。
job通过 JobLauncher 启动,JobRepository 存储有关当前正在运行的进程的元数据。请注意,步骤可以链接在一起。
3、配置示例及说明
<job id="sampleJob" job-repository="jobRepository">
<step id="step1" next="step2">
<tasklet transaction-manager="transactionManager">
<chunk reader="itemReader" writer="itemWriter" commit-interval="1"/>
</tasklet>
</step>
<step id="step2" next="step3">
<tasklet transaction-manager="transactionManager">
<chunk reader="itemReader" writer="itemWriter" commit-interval="1"/>
</tasklet>
</step>
<step id="step3">
<tasklet ref="SingleOperationTasklet"/>
</step>
</job>
上面的配置显示,示例作业中有三个步骤。
Step1 在Chunk Oriented Processing or READ-PROCESS-WRITE mode下工作,这意味着它从资源、进程读取,然后写入资源。step2将在step1完成后触发(通过下一步)。
Step2 也在Chunk Oriented Processing or READ-PROCESS-WRITE mode下运行,一旦完成,将触发 Step3。
现在 Step3 在 Tasklet 模式下工作,这意味着它将执行单个操作(ref 是指实现 Tasklet 接口的 bean,提供执行方法实现。
Spring Batch 还提供了在job和step级别添加Listeners的可能性。Listeners提供了在job和step之前或之后执行业务逻辑的y优先级。例如,如果想在执行实际文件处理作业之前创建一个目录或将现有数据移动到另一个目录,则可以使用job Listeners,它将提供在实际作业开始之前调用的hook,并在作业完成时提供hook(如果要这样做,可以执行一些其他业务逻辑)。
作业级别的侦听器包含 Before Job 的方法(将在作业开始之前调用)和 afterJob 的方法(该方法将在作业完成后调用)。同样,Step 级别的侦听器包含 Before Step 的方法,这些方法将在步骤开始之前调用,After Step 将在步骤完成后调用。
下面是一个简单的作业级的监听器配置,这里的 jobListener 是一个用户定义的 spring bean 实现 JobExecutionListener。
<job id="sampleJob" job-repository="jobRepository">
<step id="step1">
<tasklet>
<chunk reader="itemReader" writer="itemWriter" commit-interval="1"/>
</tasklet>
</step>
<listeners>
<listener ref="jobListener"/>
</listeners>
</job>
二、示例:通过springbatch实现从xml文件中读到csv文件中
1、maven依赖
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<springframework.version>5.2.25.RELEASE</springframework.version>
<joda-time.version>2.12.5</joda-time.version>
<springbatch.version>4.2.8.RELEASE</springbatch.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.batch</groupId>
<artifactId>spring-batch-core</artifactId>
<version>${springbatch.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.batch</groupId>
<artifactId>spring-batch-infrastructure</artifactId>
<version>${springbatch.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-oxm</artifactId>
<version>${springframework.version}</version>
</dependency>
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>${joda-time.version}</version>
</dependency>
</dependencies>
2、准备xml人员信息文件PersonInfo.xml
位置:/sping-batch/src/main/resources/PersonInfo.xml
<?xml version="1.0" encoding="UTF-8"?>
<PersonList>
<PersonInfo>
<birthday>1985-02-01</birthday>
<salary>76.0</salary>
<name>alanchanchn</name>
</PersonInfo>
<PersonInfo>
<birthday>1979-09-01</birthday>
<salary>61.0</salary>
<name>alan</name>
</PersonInfo>
<PersonInfo>
<birthday>1993-03-01</birthday>
<salary>92.0</salary>
<name>chan</name>
</PersonInfo>
<PersonInfo>
<birthday>1995-08-01</birthday>
<salary>83.0</salary>
<name>alanchan</name>
</PersonInfo>
</PersonList>
3、PersonInfo bean
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
/**
*
* @author alanchan
*
*/
@XmlRootElement(name = "PersonInfo")
public class PersonInfo {
private String name;
private String birthday;
private double salary;
@XmlElement(name = "name")
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@XmlElement(name = "birthday")
public String getBirthday() {
return birthday;
}
public void setBirthday(String birthday) {
this.birthday = birthday;
}
@XmlElement(name = "salary")
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
@Override
public String toString() {
return "PersonInfo [name=" + name + ", birthday=" + birthday + ", salary=" + salary + "]";
}
}
4、创建ItemProcessor实现类
本示例仅仅是过滤一下,salary小于75的设置为100。
package com.win;
import org.springframework.batch.item.ItemProcessor;
import com.win.model.PersonInfo;
/**
*
* @author alanchan
*
*/
public class PersonInfoItemProcessor implements ItemProcessor<PersonInfo, PersonInfo> {
@Override
public PersonInfo process(PersonInfo personIfo) throws Exception {
System.out.println("处理结果 :" + personIfo);
if (personIfo.getSalary() < 75) {
PersonInfo tempPersonInfo = new PersonInfo();
tempPersonInfo.setName(personIfo.getName());
tempPersonInfo.setBirthday(personIfo.getBirthday());
tempPersonInfo.setSalary(100);
personIfo = tempPersonInfo;
}
return personIfo;
}
}
5、添加Job listener(JobExecutionListener)
增加一个监听器,查看job执行情况
import java.util.List;
import org.joda.time.DateTime;
import org.springframework.batch.core.BatchStatus;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.JobExecutionListener;
/**
*
* @author alanchan
*
*/
public class PersonInfoJobListener implements JobExecutionListener {
private DateTime startTime, stopTime;
@Override
public void beforeJob(JobExecution jobExecution) {
startTime = new DateTime();
System.out.println("任务开始 at :" + startTime);
}
@Override
public void afterJob(JobExecution jobExecution) {
stopTime = new DateTime();
System.out.println("任务结束 at :" + stopTime);
System.out.println("任务耗时(毫秒) :" + getTimeInMillis(startTime, stopTime));
if (jobExecution.getStatus() == BatchStatus.COMPLETED) {
System.out.println("任务执行完成");
// Here you can perform some other business logic like cleanup
} else if (jobExecution.getStatus() == BatchStatus.FAILED) {
System.out.println("任务执行失败,异常如下 ");
List<Throwable> exceptionList = jobExecution.getAllFailureExceptions();
for (Throwable th : exceptionList) {
System.err.println("异常 :" + th.getLocalizedMessage());
}
}
}
private long getTimeInMillis(DateTime start, DateTime stop) {
return stop.getMillis() - start.getMillis();
}
}
6、进行job的配置
文件位置:/sping-batch/src/main/resources/spring-batch-context.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:batch="http://www.springframework.org/schema/batch"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/batch http://www.springframework.org/schema/batch/spring-batch-3.0.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd">
<!-- JobRepository and JobLauncher are configuration/setup classes -->
<bean id="jobRepository" class="org.springframework.batch.core.repository.support.MapJobRepositoryFactoryBean" />
<bean id="jobLauncher" class="org.springframework.batch.core.launch.support.SimpleJobLauncher">
<property name="jobRepository" ref="jobRepository" />
</bean>
<!-- ItemWriter write a line into output flat file -->
<bean id="flatFileItemWriter" class="org.springframework.batch.item.file.FlatFileItemWriter" scope="step">
<property name="resource" value="file:d:/personInfo.txt" />
<property name="lineAggregator">
<!-- An Aggregator which converts an object into delimited list of strings -->
<bean class="org.springframework.batch.item.file.transform.DelimitedLineAggregator">
<property name="delimiter" value="|" />
<property name="fieldExtractor">
<!-- Extractor which returns the value of beans property through reflection -->
<bean class="org.springframework.batch.item.file.transform.BeanWrapperFieldExtractor">
<property name="names" value="name, salary, birthday" />
</bean>
</property>
</bean>
</property>
</bean>
<!-- ItemReader which reads data from XML file -->
<bean id="xmlItemReader" class="org.springframework.batch.item.xml.StaxEventItemReader">
<property name="resource" value="classpath:PersonInfo.xml" />
<property name="fragmentRootElementName" value="PersonInfo" />
<property name="unmarshaller">
<bean class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
<property name="classesToBeBound">
<list>
<value>com.win.model.PersonInfo</value>
</list>
</property>
</bean>
</property>
</bean>
<!-- Optional ItemProcessor to perform business logic/filtering on the input records -->
<bean id="itemProcessor" class="com.win.PersonInfoItemProcessor" />
<!-- Optional JobExecutionListener to perform business logic before and after the job -->
<bean id="jobListener" class="com.win.PersonInfoJobListener" />
<!-- Step will need a transaction manager -->
<bean id="transactionManager" class="org.springframework.batch.support.transaction.ResourcelessTransactionManager" />
<!-- Actual Job -->
<batch:job id="personInfoJob">
<batch:step id="step1">
<batch:tasklet transaction-manager="transactionManager">
<batch:chunk reader="xmlItemReader" writer="flatFileItemWriter" processor="itemProcessor" commit-interval="10" />
</batch:tasklet>
</batch:step>
<batch:listeners>
<batch:listener ref="jobListener" />
</batch:listeners>
</batch:job>
</beans>
7、创建一个运行job的main类
import org.springframework.batch.core.Job;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.JobExecutionException;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
*
* @author alanchan
*
*/
public class App {
public static void main(String areg[]) {
//读取配置文件
ApplicationContext context = new ClassPathXmlApplicationContext("spring-batch-context.xml");
//获取job配置任务
JobLauncher jobLauncher = (JobLauncher) context.getBean("jobLauncher");
Job job = (Job) context.getBean("personInfoJob");
try {
//执行任务
JobExecution execution = jobLauncher.run(job, new JobParameters());
System.out.println("Job 执行结果 : " + execution.getStatus());
} catch (JobExecutionException e) {
System.out.println("job执行失败");
e.printStackTrace();
}
}
}
8、验证
运行程序 ,查看输出文件内以及控制台内容
1)、控制台输出
控制台看监听器的输出
任务开始 at :2023-07-20T15:18:58.375+08:00
处理结果 :PersonInfo [name=alanchanchn, birthday=1985-02-01, salary=76.0]
处理结果 :PersonInfo [name=alan, birthday=1979-09-01, salary=61.0]
处理结果 :PersonInfo [name=chan, birthday=1993-03-01, salary=92.0]
处理结果 :PersonInfo [name=alanchan, birthday=1995-08-01, salary=83.0]
任务结束 at :2023-07-20T15:18:58.445+08:00
任务耗时(毫秒) :70
任务执行完成
Job 执行结果 : COMPLETED
2)、程序结果输出
以上,简单的介绍了springbatch的功能、架构、配置示例以及以读取xml文件经过转换输出成txt文件的示例。