程序结构
pom.xml
<?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>org.lzh</groupId>
<artifactId>testSpringBatch</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<!-- https://mvnrepository.com/artifact/org.springframework.batch/spring-batch-core -->
<dependency>
<groupId>org.springframework.batch</groupId>
<artifactId>spring-batch-core</artifactId>
<version>3.0.7.RELEASE</version>
</dependency>
</dependencies>
</project>
applicationContext.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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--jobLauncher负责batch的启动工作-->
<bean id="jobLauncher" class="org.springframework.batch.core.launch.support.SimpleJobLauncher">
<!--注入jobRepository-->
<property name="jobRepository" ref="jobRepository"/>
</bean>
<!--jobRepository负责job的整个运行过程中的CRUD操作-->
<bean id="jobRepository" class="org.springframework.batch.core.repository.support.MapJobRepositoryFactoryBean"/>
<!--transactionManager负责事务的管理操作-->
<bean id="transactionManager" class="org.springframework.batch.support.transaction.ResourcelessTransactionManager"/>
</beans>
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:batch="http://www.springframework.org/schema/batch"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/batch
http://www.springframework.org/schema/batch/spring-batch.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!--导入前面的Spring配置文件-->
<import resource="applicationContext.xml"/>
<!--===================================================================================================-->
<batch:job id="csvJob">
<!--仅有这一个作业步-->
<batch:step id="csvstep">
<!--这个tasklet不直接指定ref,在其内部配置chunk来作为其实现内容-->
<batch:tasklet transaction-manager="transactionManager">
<!--chunk,ChunkOrientedTasklet类实现类"块处理(chunk processing)"-->
<!--[reader,processor,writer属性]分别是对三种处理bean的ref,它们必须是特定接口的实现类及其子类对象-->
<!--[commit-interval属性]是事务提交一次处理的items的数量,即是chunk的大小-->
<batch:chunk reader="csvItemReader" processor="csvItemProcessor" writer="csvItemWriter"
commit-interval="1"/>
</batch:tasklet>
</batch:step>
</batch:job>
<!--===================================================================================================-->
<!--Reader的bean,使用Spring Batch自带的一个实现类-->
<bean id="csvItemReader" class="org.springframework.batch.item.file.FlatFileItemReader" scope="step">
<!--该类的[resource成员]注入要读取的文件位置-->
<property name="resource" value="classpath:inputFile.csv"/>
<!--该类的[lineMapper成员]是行处理器,用对文件的每行具体处理-->
<property name="lineMapper">
<!--这里使用Spring Batch自带的DefaultLineMapper来作为行处理器-->
<bean class="org.springframework.batch.item.file.mapping.DefaultLineMapper">
<!--lineTokenizer是一个属性标记器,定义了对象转换的结构,表征了每行记录如何分割成对象的属性-->
<property name="lineTokenizer" ref="lineTokenizer"/>
<!--fieldSetMapper定义了转换结果映射,即具体映射到哪个Java类对象-->
<property name="fieldSetMapper" ref="fieldSetMapper"/>
</bean>
</property>
</bean>
<!--行处理器=>属性标记器;使用Spring Batch自带的DelimitedLineTokenizer(可以切割行字符串形成一个个属性)-->
<bean id="lineTokenizer" class="org.springframework.batch.item.file.transform.DelimitedLineTokenizer">
<!--按","符号对行进行切割-->
<property name="delimiter" value=","/>
<!--属性名称列表,将切割后的行按顺序投入-->
<property name="names">
<list>
<value>id</value>
<value>name</value>
<value>age</value>
<value>score</value>
</list>
</property>
</bean>
<!--行处理器=>结果集映射;使用Spring Batch自带的BeanWrapperFieldSetMapper-->
<bean id="fieldSetMapper" class="org.springframework.batch.item.file.mapping.BeanWrapperFieldSetMapper">
<!--注入行处理的结果集是哪个Java类的bean[[为什么这里用value而不是ref?]]-->
<property name="prototypeBeanName" value="student"/>
</bean>
<!--行转换结果的实体类-->
<bean id="student" class="org.lzh.Student"/>
<!--===================================================================================================-->
<!--csvItemProcessor用注解的方式加入Spring容器管理,见CsvItemProcessor类-->
<!--这里扫描它所在的包,该类已经打上了@Component注解,将被扫描并注册为指定id的bean-->
<context:component-scan base-package="org.lzh"/>
<!--===================================================================================================-->
<!--Writer的bean,使用Spring Batch自带的一个实现类-->
<bean id="csvItemWriter" class="org.springframework.batch.item.file.FlatFileItemWriter">
<!--要写入的文件位置,因为[classpath:]不是一个具体的目录,这里应当用[file:](从项目根目录开始)指明输出位置-->
<property name="resource" value="file:src/main/resources/outputFile.csv"/>
<!--[lineAggregator成员]指明行聚合器,用来将对象输出到文件时构造文件中的每行的格式-->
<property name="lineAggregator">
<!--这里使用Spring Batch自带的DelimitedLineAggregator来作为行聚合器(可以拼接一个个属性形成行)-->
<bean class="org.springframework.batch.item.file.transform.DelimitedLineAggregator">
<!--使用","拼接-->
<property name="delimiter" value=","/>
<!--fieldExtractor成员用来将Java类的属性组成的数组拼接成行字符串-->
<property name="fieldExtractor">
<bean class="org.springframework.batch.item.file.transform.BeanWrapperFieldExtractor">
<!--这里不妨只输出后三个属性,不输出id-->
<property name="names" value="name,age,score"/>
</bean>
</property>
</bean>
</property>
</bean>
</beans>
CsvItemProcessor类
package org.lzh;
//注意不是这个ItemProcessor
//import javax.batch.api.chunk.ItemProcessor;
import org.springframework.batch.item.ItemProcessor;
import org.springframework.stereotype.Component;
//自定义的处理类,需要实现ItemProcessor泛型接口,并实现其process方法
@Component("csvItemProcessor")
public class CsvItemProcessor implements ItemProcessor<Student,Student> {
//对Reader获取的数据进行处理,传入的是Reader以后的Java对象,返回的是处理之后的Java对象,在这里都是Student对象
public Student process(Student student) throws Exception {
//对Reader读取来的POJO对象做一些简单处理
student.setName("SB"+student.getName());
student.setAge(20);
student.setScore(student.getScore()+20);
//处理后的结果将传递给Writer
return student;
}
}
Student类
package org.lzh;
public class Student {
private int id;
private String name;
private int age;
private double score;
//getter和setter
}
Main类
package org.lzh;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.JobParametersInvalidException;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.batch.core.repository.JobExecutionAlreadyRunningException;
import org.springframework.batch.core.repository.JobInstanceAlreadyCompleteException;
import org.springframework.batch.core.repository.JobRestartException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Main {
public static void main(String[] args) {
//batch.xml导入了applicationConext.xml,所以只写batch.xml就可以一并加入上下文
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("batch.xml");
//Spring Batch的作业启动器,在applicationContext.xml中配置为了bean
JobLauncher jobLauncher = (JobLauncher) applicationContext.getBean("jobLauncher");
//在batch.xml中配置的一个作业
Job job = (Job) applicationContext.getBean("csvJob");
try {
//开始执行这个作业,获得处理结果(要运行的job,job参数对象)
JobExecution result = jobLauncher.run(job, new JobParameters());
//输出处理结果看一下
System.out.println("处理结果:" + result.toString());
} catch (JobExecutionAlreadyRunningException e) {
e.printStackTrace();
} catch (JobRestartException e) {
e.printStackTrace();
} catch (JobInstanceAlreadyCompleteException e) {
e.printStackTrace();
} catch (JobParametersInvalidException e) {
e.printStackTrace();
}
}
}
inputFile.csv
1,刘知昊,21,40
2,lzh,10,33
运行结果