网上例子很多,只是大部分都不全,而且都是复制粘贴,不是很深入。没个流程化的东西。
一.Batch框架整体初见
从网上截图
这种分层结构有三个重要的组成部分:应用层、核心层、基础架构层。应用层包含所有的批处理作业,通过Spring框架管理程序员自定义的代码。核心层包含了Batch启动和控制所需要的核心类,如:JobLauncher、Job和step等。应用层和核心层建立在基础构架层之上,基础构架层提供共通的读(ItemReader)、写(ItemWriter)、和服务(如RetryTemplate:重试模块。可以被应用层和核心层使用)。所以使用spring-batch需引入spring-batch-core.jar
和spring-batch-infrastructure.jar(建议2.1.8.RELEASE).
二.执行流程
Batch执行流程:外部控制器调用JobLauncher启动一个Job,Job调用自己的Step去实现对数据的操作,Step处理完成后,再将处理结果一步步返回给上一层。其中Job里会配置一次批次处理数量commin-interval,read读一条传给process一条重复这2个操作直到commin-interval最大值就调用一次writer操作。然后再重复上次操作直到处理完所有的数据。当这个Step的工作完成以后,或是跳到其他Step,或是结束处理。
三.关键类介绍。
JobLauncher:接口JobLauncher只有一个子类SimpleJobLauncher.负责整个batch的启动,是入口类。使用run(Jobjob, JobParameters jobParameters)方法,该方法根据JobParameters运行job
Job:接口Job有个抽象子类AbstractJob扩展了Job功能的属性,下在有很多子类,其中SimpleJob是我们常用的子类,一个job包含若干step
Step: 接口Step有个抽象子类AbstractStep扩展了Step功能的属性,下在有很多子类,其中TaskletStep是我们常用的子类,一个Step包含一个Tasklet实例,负责具体的事件。
JobRepository:JobRepository存储job及step仓库。只有一个子类SimpleJobRepository.
而JobRepositoryFactoryBean和MapJobRepositoryFactoryBean都是JobRepository工厂类
ItemReader:提供了读取数据的能力
ItemProcessor:我们可以通过它应用业务逻辑到每一条要处理的数据
ItemWriter:提供了写数据的能力
uml:
通过2副图我们可知,jobRepository必需注入到jobLauncher,job和step里,他的作用是为上述3个类做一些过滤限制及持久化机制
四.JobRepository存储信息
JobRepository是怎么保存jobInstance,jobExecution,jobParameter等信息的呢,通过数据库或内存里Map.
查看唯一子类SimpleJobRepository源码,
我们发现jobInstanceDao有两个子类JdbcJobInstanceDao和MapJobInstanceDao。查看两个子类的getJobInstance方法。JdbcJobInstanceDao. getJobInstance().
public JobInstance getJobInstance(final String jobName,
final JobParameters jobParameters) {
Assert.notNull(jobName, "Job name must not be null.");
Assert.notNull(jobParameters, "JobParameters must not be null.");
String jobKey = createJobKey(jobParameters);
ParameterizedRowMapper<JobInstance> rowMapper = new JobInstanceRowMapper(
jobParameters);
List<JobInstance> instances;
if (StringUtils.hasLength(jobKey)) {
//通过jdbc连接数据库,执行sql语句,
//SELECT JOB_INSTANCE_ID,JOB_NAME from %PREFIX%JOB_INSTANCE where JOB_NAME = ? and //JOB_KEY = ?
instances = getJdbcTemplate().query(getQuery(FIND_JOBS_WITH_KEY),
rowMapper, jobName, jobKey);
} else {
instances = getJdbcTemplate().query(
getQuery(FIND_JOBS_WITH_EMPTY_KEY), rowMapper, jobName,
jobKey);
}
if (instances.isEmpty()) {
return null;
} else {
Assert.state(instances.size() == 1);
return instances.get(0);
}
}
MapJobInstanceDao. getJobInstance:
public JobInstance getJobInstance(String jobName, JobParameters jobParameters) {
//CopyOnWriteArraySet保存jobInstance实例
for (JobInstance instance : jobInstances) {
if (instance.getJobName().equals(jobName) && instance.getJobParameters().equals(jobParameters)) {
return instance;
}
}
return null;
}
不难发现第一个是通过数据库保存信息,第二个是集合存储。所以当我们使用第一种保存里数据库里要创建对就的表,而对应的表在Jar里有提供sql,
随便查看一下schema-mysql.sql:
-- Autogenerated: do not edit this file
CREATE TABLE BATCH_JOB_INSTANCE (
JOB_INSTANCE_ID BIGINT NOT NULL PRIMARY KEY ,
VERSION BIGINT ,
JOB_NAME VARCHAR(100) NOT NULL,
JOB_KEY VARCHAR(32) NOT NULL,
constraint JOB_INST_UN unique (JOB_NAME, JOB_KEY)
) ENGINE=InnoDB;
CREATE TABLE BATCH_JOB_EXECUTION (
JOB_EXECUTION_ID BIGINT NOT NULL PRIMARY KEY ,
VERSION BIGINT ,
JOB_INSTANCE_ID BIGINT NOT NULL,
CREATE_TIME DATETIME NOT NULL,
START_TIME DATETIME DEFAULT NULL ,
END_TIME DATETIME DEFAULT NULL ,
STATUS VARCHAR(10) ,
EXIT_CODE VARCHAR(100) ,
EXIT_MESSAGE VARCHAR(2500) ,
LAST_UPDATED DATETIME,
constraint JOB_INST_EXEC_FK foreign key (JOB_INSTANCE_ID)
references BATCH_JOB_INSTANCE(JOB_INSTANCE_ID)
) ENGINE=InnoDB;
CREATE TABLE BATCH_JOB_PARAMS (
JOB_INSTANCE_ID BIGINT NOT NULL ,
TYPE_CD VARCHAR(6) NOT NULL ,
KEY_NAME VARCHAR(100) NOT NULL ,
STRING_VAL VARCHAR(250) ,
DATE_VAL DATETIME DEFAULT NULL ,
LONG_VAL BIGINT ,
DOUBLE_VAL DOUBLE PRECISION ,
constraint JOB_INST_PARAMS_FK foreign key (JOB_INSTANCE_ID)
references BATCH_JOB_INSTANCE(JOB_INSTANCE_ID)
) ENGINE=InnoDB;……………..
这也验证了数据库存储必需先创建表。而默认是使用JdbcJobInstanceDao,因为batch就是为了批操作数据库的。这点要注意,否则会报sql异常。
例子:
先看个例子的配置
<bean id="jobLauncher"
class="org.springframework.batch.core.launch.support.SimpleJobLauncher">
<property name="jobRepository" ref="jobRepository" />
</bean>
<bean id="jobRepository"
class="org.springframework.batch.core.repository.support.JobRepositoryFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="transactionManager" ref="transactionManager" />
</bean>
<bean id="taskletStep" abstract="true"
class="org.springframework.batch.core.step.tasklet.TaskletStep">
<property name="jobRepository" ref="jobRepository" />
<property name="transactionManager" ref="transactionManager" />
</bean>
<bean id="simpleJob" class="org.springframework.batch.core.job.SimpleJob">
<property name="name" value="simpleJob" />
<property name="steps">
<list>
<bean parent="taskletStep">
<property name="tasklet" ref="hello" />
</bean>
<bean parent="taskletStep">
<property name="tasklet" ref="space" />
</bean>
<bean parent="taskletStep">
<property name="tasklet" ref="world" />
</bean>
</list>
</property>
<property name="jobRepository" ref="jobRepository" />
</bean>
<bean id="hello" class="com.cloudeport.tasklet.TestTasklet">
<property name="message" value="Hello" />
</bean>
<bean id="space" class="com.cloudeport.tasklet.TestTasklet">
<property name="message" value=" " />
</bean>
<bean id="world" class="com.cloudeport.tasklet.TestTasklet">
<property name="message" value="World!" />
</bean>
TestTasklet.java:
package com.cloudeport.tasklet;
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;
public class TestTasklet implements Tasklet {
private String message;
public void setMessage(String message) {
this.message = message;
}
@Override
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
// TODO Auto-generated method stub
System.out.print(message);
return RepeatStatus.FINISHED;
}
}
启动:joblauncher.run(simpleJob,new JobParametersBuilder().toJobParameters());
Ps:要真正能启动注意数据库里创建表。
通过上的了解及UML图我们不难发现这种配置和类很匹配,直接通过属性命来配但这真是我们常用的写法吗,刚开始接触铺天盖地都这种例子,弄的很糊涂。后来直接查看官方提供的例子,发现我们常用的配置batch是通过schema配,说白点就是提供官方提供的http://www.springframework.org/schema/batch xsd文件配。
故常用的配置是:
<job id="footballJob" xmlns="http://www.springframework.org/schema/batch">
<step id="playerload" next="gameLoad">
<tasklet>
<chunk reader="playerFileItemReader" writer="playerWriter"
commit-interval="${job.commit.interval}" />
</tasklet>
</step>
<step id="gameLoad" next="playerSummarization">
<tasklet>
<chunk reader="gameFileItemReader" writer="gameWriter"
commit-interval="${job.commit.interval}" />
</tasklet>
</step>
<step id="playerSummarization" parent="summarizationStep" />
</job>
整个流程已经了然于胸。
1. 有JobLauncher,其实只有SimpleJobLauncher
2. 有JobRepository,其中只有SimpleJobRepository,不过提供了两个工厂类。JobReository需要注入到jobLauncher,job和step里
3. 配job,job下有n个step,每个step由tasklet小程序组成包括read-process-write
4. read –write其实不用自己写,process需要自己继承实现
5. 通过joblauncher.run(simpleJob, newJobParametersBuilder().toJobParameters());方式调用。
下面的会详解ItemReader.ItemProcess,itemWriter。以后再写