原文地址:http://blog.csdn.net/pkjxt/article/details/54584497
一、 介绍
Spring Batch能够支持简单的、复杂的和大数据量的批处理作业,是一个批处理应用框架,不是调度框架,但需要和调度框架(Quartz, Tivoli, Control-M, Cron等)合作来构建完成批处理任务。整体架构如下:
二、 基本概念
- Job: 作业, 由多个 Step 组成,封装整个批处理操作, 是 Batch 操作的基础单元;
- Job Instance: 作业实例,每次作业执行都会生成一个实例并存放在 JobRepository 中,如果失败,再次执行还是会用同一个作业实例;
- Job Parameters: 作业参数, 不同的 Job 实例是通过 Job 参数来区分的;
- Job Execution: 作业执行器, 负责具体 Job 的执行,每次运行 Job 都会启动一个新的 Job 执行器;
- Job Repository: 作业仓库, 负责储存 Job、Step 执行过程中的状态数据及结果,为 JobLauncher、Job、Step 提供标准的 CRUD 实现;
- Job Launchher: 作业调度器,提供执行 Job 的入口;
- Step: 作业步, Job 的一个执行环节,由多个或者一个 Step 组装成 Job,封装了批处理任务中的一个独立的连续阶段;
- Step Execution: 作业步执行器,负责具体 Step 的执行,每次运行 Step 都会启动一个新的执行器;
- Tasklet: Step 中具体执行逻辑的操作,可以重复执行,可以设置具体的同步、异步操作等;
- Execution Context: 执行上下文。它是一组框架持久化与控制的 key/value 对,能够让开发者在 Step Execution 或 Job Execution 范畴保存需要进行持久化的状态;
- Item: 一条数据记录;
- Chunk: 给定数量 Item 的集合,可以定义对 Chunk 的读操作、处理操作、写操作,提交间隔等,这是 Spring Batch 框架的一个重要特性;
- ItemReader: 从数据源(文件系统、数据库、队列等)中读取 Item,一次读取一条;
- ItemProcessor: 从 Item 写入数据源之前,对数据进行处理(如:数据清洗,数据转换,数据过滤,数据校验等);
- ItemWriter: 将 Item 批量写入数据源(文件系统、数据库、队列等),一次输出一批。
注意:定义 Job 基础设施: 主要配置任务仓库、任务调度器和任务执行中用到的事务管理器。如下:
<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>
<bean id="transactionManager" class="org.springframework.batch.support.
transaction.ResoourcelessTransactionManager" />
三、 示例
commons-batch.xml (主要配置任务仓库,任务调度器和事务管理器等)
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:batch="http://www.springframework.org/schema/batch" xmlns:context="http://www.springframework.org/schema/context" xmlns:ehcache="http://ehcache-spring-annotations.googlecode.com/svn/schema/ehcache-spring" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.2.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.2.xsd http://www.springframework.org/schema/batch http://www.springframework.org/schema/batch/spring-batch-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd "> <aop:aspectj-autoproxy /> <context:component-scan base-package="com.job"> <context:exclude-filter type="aspectj" expression="(com.job..*)" /> </context:component-scan> <!-- 配置信息 --> <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> <property name="ignoreResourceNotFound" value="true" /> <property name="locations"> <list merge="true"> <!-- 配置数据库连接信息 --> <value>classpath:databaseConfigs.properties</value> <!-- 测试数据路径信息 --> <value>classpath:batch-testDatas.properties</value> </list> </property> <property name="ignoreUnresolvablePlaceholders" value="true" /> </bean> <!-- The DB connection --> <!--<import resource="classpath:db-batch.xml" /> <import resource="classpath:db-hibernate.xml" />--> <!--<bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean"> <property name="configLocation" value="classpath:ehcache.xml" /> </bean>--> <!-- 定义 Job 基础设施 --> <bean id="inMemoryJobRepository" class="org.springframework.batch.core.repository.support.MapJobRepositoryFactoryBean"> <property name="transactionManager" ref="transactionManager" /> </bean> <bean id="jobLauncher" class="org.springframework.batch.core.launch.support.SimpleJobLauncher"> <property name="jobRepository" ref="inMemoryJobRepository" /> </bean> <!-- 定义抽象 bean --> <bean id="abstractJobBatch" class="com.job.batch.services.base.AbstractJobBatch" abstract="true"/> </beans>
jobBatch.xml ( job配置 )
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:batch="http://www.springframework.org/schema/batch" xmlns:context="http://www.springframework.org/schema/context" xmlns:util="http://www.springframework.org/schema/util" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.2.xsd http://www.springframework.org/schema/batch http://www.springframework.org/schema/batch/spring-batch-3.0.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd"> <!-- Import of the commons --> <import resource="classpath:commons-batch.xml" /> <!-- job start --> <batch:job id="jobBatch" job-repository="inMemoryJobRepository"> <batch:step id="stepStart"> <!--具体的start类,所指向的是下面第四点--> <batch:tasklet ref="jobTaskletStart" /> <batch:next on="FAILED" to="stepFailed" /> <batch:next on="*" to="importData" /> </batch:step> <batch:step id="stepFailed"> <batch:tasklet ref="jobTaskletFailed" /> </batch:step> <batch:step id="stepCompleted"> <batch:tasklet ref="jobTaskletCompleted" /> </batch:step> <batch:step id="importData"> <batch:tasklet> <batch:chunk reader="testReader" processor="testProcessor" writer="testWriter" commit-interval="${batch.commit.interval}" /> </batch:tasklet> <batch:next on="FAILED" to="stepFailed" /> <batch:next on="*" to="anotherStep" /> </batch:step> <batch:step id="anotherStep"> <batch:tasklet> <batch:chunk reader="anotherFileItemReader" writer="anotherFileItemWriter" commit-interval="100" > <batch:streams> <batch:stream ref="aaaFlatFileItemWriter"/> <batch:stream ref="bbbFlatFileItemWriter"/> <batch:stream ref="cccFlatFileItemWriter"/> </batch:streams> </batch:chunk> </batch:tasklet> <batch:next on="FAILED" to="stepFailed" /> <batch:next on="*" to="..." /> </batch:step> ...
AbstractJobBatch.java ( 配置抽象类的公共方法 )
public abstract class AbstractPoiBatch{ // 注入 service, 具体实现就不写了 @Inject private JobBatchService jobBatchService; // 开始执行向数据库插入 log 信息 public Integer initLogInDatabase(final String name, ...) throws jobServiceException; // 向数据库添加具体的 log 信息 public void addLogDetail(final Integer batchId, ...) throws jobServiceException{} // 向数据库添加失败的 log 信息 public void closeFailedLog(final Integer batchId, ...) throws jobServiceException{} // 向数据库添加成功的 log 信息 public void closeCompletedLog(final Integer batchId, ...) throws jobServiceException{} }
jobTaskletStart, (具体的开始,成功,失败的类均要实现 Tasklet 的 execute 方法)
@Named public class JobTaskletStart extends AbstractJobBatch implements Tasklet { @Override public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception { contribution.setExitStatus(ExitStatus.COMPLETED); String jobName = chunkContext.getStepContext().getJobName(); Map<String, Object> batchParameters = chunkContext.getStepContext().getJobParameters(); Integer batchId = initLogInDatabase(jobName,...); // ExecutionContext 类: final Map<String, Object> map; // 所以其中可以存放jobName, batchId, timestamp, fileName... ExecutionContext jobExecutionContext = chunkContext.getStepContext().getStepExecution().getJobExecution().getExecutionContext(); jobExecutionContext.put(BATCH_NAME, jobName); return RepeatStatus.FINISHED; } }
testReader
@Named("testReader") public class TestReader extends FlatFileItemReader<TestDto> { private ExecutionContext executionContext; @BeforeStep public void beforeStep(StepExecution stepExecution){ executionContext = stepExecution.getJobExecution().getExecutionContext(); // 读取之前的一些配置,如设置 tokenizer, lineMapper, ... } @Override protected TestDto doRead() throws SfjServiceException { TestDto dto = super.doRead(); return dto; } }
testProcessor
@Named("testProcessor") public class TestProcessor implements ItemProcessor<TestDto, Test> { @Override public Test process(TestDto item) throws Exception { Test test = new Test(); // 具体转换处理的步骤 return test; } }
testWriter
@Named("testWriter") public class TestWriter implements ItemWriter<Test> { // 一般需要注入 service 来实现具体的操作 @Inject private TestService testService; @BeforeStep public void beforeStep(StepExecution stepExecution) { // 执行前的操作 ExecutionContext executionContext = stepExecution.getJobExecution().getExecutionContext(); } @Override public void write(List<? extends Test> items){ for (Test test : items) { // 具体的插入表操作等... } } @AfterStep public void afterStep(StepExecution stepExecution){ // 执行后进行的操作,主要是更新数据库 log 的状态(成功或者失败) } }