网上例子很多,只是大部分都不全,而且都是复制粘贴,不是很深入。没个流程化的东西。

一.Batch框架整体初见

从网上截图

 

springbatch job 执行前 处理 springbatch step_子类

这种分层结构有三个重要的组成部分:应用层、核心层、基础架构层。应用层包含所有的批处理作业,通过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:提供了写数据的能力 

springbatch job 执行前 处理 springbatch step_spring_02

uml:

springbatch job 执行前 处理 springbatch step_spring_03


通过2副图我们可知,jobRepository必需注入到jobLauncher,job和step里,他的作用是为上述3个类做一些过滤限制及持久化机制 

四.JobRepository存储信息

JobRepository是怎么保存jobInstance,jobExecution,jobParameter等信息的呢,通过数据库或内存里Map.

查看唯一子类SimpleJobRepository源码,

springbatch job 执行前 处理 springbatch step_spring_04


我们发现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,


springbatch job 执行前 处理 springbatch step_子类_05

随便查看一下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。以后再写