Spring Batch 入门例子


Spring Batch 之 Spring Batch 简介(一)

      Spring Batch一个轻量级的,完全面向Spring的批处理框架,可以应用于企业级大量的数据处理系统。Spring BatchPOJO和大家熟知的Spring框架为基础,使开发者更容易的访问和利用企业级服务。Spring Batch可以提供大量的,可重复的数据处理功能,包括日志记录/跟踪,事务管理,作业处理统计工作重新启动、跳过,和资源管理等重要功能。

      业务方案:

1、批处理定期提交。

2、并行批处理:并行处理工作。

3、企业消息驱动处理

4、大规模的并行处理

5、手动或是有计划的重启

6、局部处理:跳过记录(如:回滚)

      技术目标:

1、利用Spring编程模型:使程序员专注于业务处理,让Spring框架管理流程。

2、明确分离批处理的执行环境和应用。

3、提供核心的,共通的接口。

4、提供开箱即用(out of the box)的简单的默认的核心执行接口。

5、提供Spring框架中配置、自定义、和扩展服务。

6、所有存在的核心服务可以很容的被替换和扩展,不影响基础层。

7、提供一个简单的部署模式,利用Maven构建独立的Jar文件。

      Spring Batch的结构:

             

 

        这种分层结构有三个重要的组成部分:应用层、核心层、基础架构层。应用层包含所有的批处理作业,通过Spring框架管理程序员自定义的代码。核心层包含了Batch启动和控制所需要的核心类,如:JobLauncherJobstep等。应用层和核心层建立在基础构架层之上,基础构架层提供共通的读(ItemReader)、写(ItemWriter)、和服务(如RetryTemplate:重试模块。可以被应用层和核心层使用)。


Spring Batch 之 框架流程简单介绍(二)

Spring Batch流程介绍:

上图描绘了Spring Batch的执行过程。说明如下:

    每个Batch都会包含一个Job。Job就像一个容器,这个容器里装了若干Step,Batch中实际干活的也就是这些Step,至于Step干什么活,无外乎读取数据,处理数据,然后将这些数据存储起来(ItemReader用来读取数据,ItemProcessor用来处理数据,ItemWriter用来写数据) 。JobLauncher用来启动Job,JobRepository是上述处理提供的一种持久化机制,它为JobLauncher,Job,和Step实例提供CRUD操作。

    外部控制器调用JobLauncher启动一个Job,Job调用自己的Step去实现对数据的操作,Step处理完成后,再将处理结果一步步返回给上一层,这就是Batch处理实现的一个简单流程。 

Step执行过程:

      从DB或是文件中取出数据的时候,read()操作每次只读取一条记录,之后将读取的这条数据传递给processor(item)处理,框架将重复做这两步操作,直到读取记录的件数达到batch配置信息中”commin-interval”设定值的时候,就会调用一次write操作。然后再重复上图的处理,直到处理完所有的数据。当这个Step的工作完成以后,或是跳到其他Step,或是结束处理。

      这就是一个SpringBatch的基本工作流程。

   将通过“Hello World”实例,与大家共同探讨SpringBatch的具体应用和实现。


Spring Batch 之 Sample(Hello World)(三)

      通过前面两篇关于Spring Batch文章的介绍,大家应该已经对Spring Batch有个初步的概念了。这篇文章,将通过一个”Hello World!”实例,和大家一起探讨关于Spring Batch的一些基本配置和实现。使大家从开发的角度对Spring Batch有一个真切的体会。

      说明:1,本实例使用的是spring-batch 2.1.8

             2,本实例没有像前面讲的那样配置ItemReader、ItemProcessor和ItemWriter,而是之间在Step中调用Tasklet,由Tasklet完成”Hello World!”的输出。

      工程结构如下图:

                    

      JobLaunch.java类用来启动Bath,writeTasklet.java用来完成输出工作。application.xml用来配置一些Spring信息,batch.xml配置Job信息。

application.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:p="http://www.springframework.org/schema/p"
    xmlns:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans  
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd  
http://www.springframework.org/schema/tx  
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd  
http://www.springframework.org/schema/aop  
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd  
http://www.springframework.org/schema/context  
http://www.springframework.org/schema/context/spring-context-2.5.xsd"
    default-autowire="byName">

    <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.MapJobRepositoryFactoryBean">
    </bean>

    <bean id="transactionManager"
        class="org.springframework.batch.support.transaction.ResourcelessTransactionManager"/>
</beans>


      jobLauncher负责batch的启动工作,jobRepository负责job的整个运行过程中的CRUD操作,transactionManager负责事务的管理操作。

      batch.xml文件配置如下:

<?xml version="1.0" encoding="UTF-8"?>
<bean:beans xmlns="http://www.springframework.org/schema/batch"
    xmlns:bean="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:p="http://www.springframework.org/schema/p" xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd  
http://www.springframework.org/schema/tx 
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd  
http://www.springframework.org/schema/aop 
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd  
http://www.springframework.org/schema/context 
http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/batch 
http://www.springframework.org/schema/batch/spring-batch-2.1.xsd">

    <bean:import resource="applicationContext.xml"/>

    <job id="helloWorldJob">
        <step id="step_hello" next="step_world">
            <tasklet ref="hello" transaction-manager="transactionManager"></tasklet>
        </step>
        <step id="step_world">
            <tasklet ref="world" transaction-manager="transactionManager"></tasklet>
        </step>
    </job>

    <bean:bean id="hello" class="com.wanggc.springbatch.sample.helloworld.writeTasklet">
        <bean:property name="message" value="Hello "></bean:property>
    </bean:bean>

    <bean:bean id="world" class="com.wanggc.springbatch.sample.helloworld.writeTasklet">
        <bean:property name="message" value=" World!"></bean:property>
    </bean:bean>
</bean:beans>

      配置了一个ID为helloWorldJob的job,此job有两个Step : step_hello和step_world,前者负责输出“Hello ”,后者负责输出“World!”,当第一个Step完成以后,执行第二个Step。 

      writeTasklet类的代码如下:

public class writeTasklet implements Tasklet {

    /** Message */
    private String message;

    /**
     * @param message
     *            the message to set
     */
    public void setMessage(String message) {
        this.message = message;
    }

    @Override
    public RepeatStatus execute(StepContribution arg0, ChunkContext arg1)
            throws Exception {
        System.out.println(message);
        return RepeatStatus.FINISHED;
    }

}

      此类中定义了一个message属性,通过batch.xml的“hello”和“world” Bean为其注入值。 execute方法,是由Tasklet接口继承而来的,是Tasklet实现业务逻辑的地方。此实例中只是简单的输出Message信息后,直接返回。

      启动类JobLaunch类的代码如下:

public class JobLaunch {

    /**
     * @param args
     */
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext(
                "batch.xml");
        JobLauncher launcher = (JobLauncher) context.getBean("jobLauncher");
        Job job = (Job) context.getBean("helloWorldJob");

        try {
            /* 运行Job */
            JobExecution result = launcher.run(job, new JobParameters());
            /* 处理结束,控制台打印处理结果 */
            System.out.println(result.toString());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
      本例通过Spring配置的方式取得JobLauncher和Job对象,然后由JobLauncher的run方法启动job,参数JobParameters是标志job的一些参数,处理结束后,控制台输出处理结果。

      上面就是通过SpringBatch运行一个"Hello World”程序所需要的基本配置。由于其优势是处理大批量的数据,所以仅仅为了输出"Hello World"而编写这么多代码和配置文件,确实显得有些笨拙,也体现不出其优越性。

      下次,将通过读取一个CSV文件,经过简单的处理,再写入另外一个CSV文件的实例,与大家共同探讨SpringBatch的应用。


Spring Batch 之 Sample(CSV文件操作)(四)

      本文将通过一个完整的实例,与大家一起讨论运用Spring Batch对CSV文件的读写操作。此实例的流程是:读取一个含有四个字段的CSV文件(ID,Name,Age,Score),对读取的字段做简单的处理,然后输出到另外一个CSV文件中。

      工程结构如下图:

                  

      JobLaunch类用来启动Job, CsvItemProcessor类用来对Reader取得的数据进行处理, Student类是一个POJO类,用来存放映射的数据。 inputFile.csv是数据读取文件, outputFile.csv是数据输出文件。

      application.xml文件配置如前篇文章,不再赘述。

      batch.xml文件中Job配置如下:

<job id="csvJob">
        <step id="csvStep">
            <tasklet transaction-manager="transactionManager">
                <chunk reader="csvItemReader" writer="csvItemWriter" processor="csvItemProcessor" commit-interval="1">
                </chunk>
            </tasklet>
        </step>
    </job>

     这个文件里配置了这次运行的JOB:csvJob。本Job包含一个Step,完成一个完整的CSV文件读写功能。分别由 csvItemReader完成CSV文件的读操作,由 csvItemProcessor完成对取得数据的处理,由 csvItemWriter完成对CSV文件的写操作。

      batch.xml文件中csvItemReader配置如下:

<!-- 读取csv文件 -->
    <bean:bean id="csvItemReader"
        class="org.springframework.batch.item.file.FlatFileItemReader" scope="step">
        <bean:property name="resource" value="classpath:inputFile.csv"/>
        <bean:property name="lineMapper">
            <bean:bean
                class="org.springframework.batch.item.file.mapping.DefaultLineMapper">
                <bean:property name="lineTokenizer" ref="lineTokenizer"/>
                <bean:property name="fieldSetMapper">
                    <bean:bean
                        class="org.springframework.batch.item.file.mapping.BeanWrapperFieldSetMapper">
                        <bean:property name="prototypeBeanName" value="student"></bean:property>
                    </bean:bean>
                </bean:property>
            </bean:bean>
        </bean:property>
    </bean:bean>

    <bean:bean id="student" class="com.wanggc.springbatch.sample.csv.Student"></bean:bean>

    <!-- lineTokenizer -->
    <bean:bean id="lineTokenizer" class="org.springframework.batch.item.file.transform.DelimitedLineTokenizer">
        <bean:property name="delimiter" value=","/>
        <bean:property name="names">
            <bean:list>
                <bean:value>ID</bean:value>
                <bean:value>name</bean:value>
                <bean:value>age</bean:value>
                <bean:value>score</bean:value>
            </bean:list>
        </bean:property>
    </bean:bean>

      csvItemReader实现的是Spring Batch提供FlatFileItemReader类,此类主要用于Flat文件的读操作。它包含两个必要的属性 resource和 lineMapper。前者指定要读取的文件的位置,后者是将文件的每一行映射成一个Pojo对象。其中 lineMapper也有两个重要属性 lineTokenizer和 fieldSetMapper, lineTokenizer将文件的一行分解成一个 FieldSet,然后由 fieldSetMapper映射成Pojo对象。

      这种方式与DB的读操作非常类似。lineMapper类似于ResultSet,文件中的一行类似于Table中的一条记录,被封装成的FieldSet,类似于RowMapper。至于怎么将一条记录封装,这个工作由lineTokenizer的继承类DelimitedLineTokenizer完成。DelimitedLineTokenizer的delimiter属性决定文件的一行数据按照什么分解,默认的是“,”, names属性标示分解的每个字段的名字,传给fieldSetMapper(本实例用的是BeanWrapperFieldSetMapper)的时候,就可以按照这个名字取得相应的值。fieldSetMapper的属性prototypeBeanName,是映射Pojo类的名字。设置了此属性后,框架就会将lineTokenizer分解成的一个FieldSet映射成Pojo对象,映射是按照名字来完成的(lineTokenizer分解时标注的名字与Pojo对象中字段的名字对应)。

      总之,FlatFileItemReader读取一条记录由以下四步完成:1,从resource指定的文件中读取一条记录;2,lineTokenizer将这条记录按照delimiter分解成Fileset,每个字段的名字由names属性取得;3,将分解成的Fileset传递给fieldSetMapper,由其按照名字映射成Pojo对象;4,最终由FlatFileItemReader将映射成的Pojo对象返回,框架将返回的对象传递给Processor。

      csvItemProcessor实现的是ItemProcessor类。此类接受Reader映射成的Pojo对象,可以对此对象做相应的业务逻辑处理,然后返回,框架就会将返回的结果传递给Writer进行写操作。具体实现代码如下:

package com.wanggc.springbatch.sample.csv;

import org.springframework.batch.item.ItemProcessor;
import org.springframework.stereotype.Component;

/**
 * ItemProcessor类。
 */
@Component("csvItemProcessor")
public class CsvItemProcessor implements ItemProcessor<Student, Student> {

    /**
     * 对取到的数据进行简单的处理。
     * 
     * @param student
     *            处理前的数据。
     * @return 处理后的数据。
     * @exception Exception
     *                处理是发生的任何异常。
     */
    @Override
    public Student process(Student student) throws Exception {
        /* 合并ID和名字 */
        student.setName(student.getID() + "--" + student.getName());
        /* 年龄加2 */
        student.setAge(student.getAge() + 2);
        /* 分数加10 */
        student.setScore(student.getScore() + 10);
        /* 将处理后的结果传递给writer */
        return student;
    }
}

       batch.xml文件中csvItemReader配置如下:

<!-- 写CSV文件 -->
    <bean:bean id="csvItemWriter"
        class="org.springframework.batch.item.file.FlatFileItemWriter" scope="step">
        <bean:property name="resource" value="file:src/outputFile.csv"/>
        <bean:property name="lineAggregator">
            <bean:bean
                class="org.springframework.batch.item.file.transform.DelimitedLineAggregator">
                <bean:property name="delimiter" value=","></bean:property>
                <bean:property name="fieldExtractor">
                    <bean:bean
                        class="org.springframework.batch.item.file.transform.BeanWrapperFieldExtractor">
                        <bean:property name="names" value="name,age,score"></bean:property>
                    </bean:bean>
                </bean:property>
            </bean:bean>
        </bean:property>
    </bean:bean>

      csvItemWriter实现的是FlatFileItemWriter类。此类与FlatFileItemReader类相似,也有两个重要的属性:resource和lineAggregator。前者是要输出的文件的路径,后者和lineTokenizer类似。lineAggregator(本实例用DelimitedLineAggregator类)也有两个重要的属性:delimiter和fieldExtractor。Delimiter标示输出的字段以什么分割,后者将Pojo对象组装成由Pojo对象的字段组成的一个字符串。同样FlatFileItemWriter写一条记录也有以下四步完成:1,Processor传递过来一个对象给lineAggregator;2,lineAggregator将其这个对象转化成一个数组;3,再由lineAggregator的属性fieldExtractor将数组转化成按照delimiter分割一个字符串;4,将这个字符串输出。

      这样,一条数据的读、处理、写操作就基本完成了。当然,读和写也可以自己写类来处理,只是要注意继承FlatFileItemReader和FlatFileItemWriter就可以了。

      实例中用到的Student类代码如下:

package com.wanggc.springbatch.sample.csv;

/** Pojo类_Student */
public class Student {
    /** ID */
    private String ID = "";
    /** 名字 */
    private String name = "";
    /** 年龄 */
    private int age = 0;
    /** 分数 */
    private float score = 0;
    /*getter 和setter已删除*/
	public String getID() {
		return ID;
	}
	public void setID(String iD) {
		ID = iD;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	public float getScore() {
		return score;
	}
	public void setScore(float score) {
		this.score = score;
	}
    
    
}

public class JobLaunch {

    /**

     * @param args

     */

    public static void main(String[] args) {

        ApplicationContext context = new ClassPathXmlApplicationContext(

                "batch.xml");

        JobLauncher launcher = (JobLauncher) context.getBean("jobLauncher");

        Job job = (Job) context.getBean("csvJob");

        try {

            JobExecution result = launcher.run(job, new JobParameters());

            System.out.println(result.toString());

        } catch (Exception e) {

            e.printStackTrace();

        }

    }

}

<context:annotation-config />
<context:component-scan base-package="com.wanggc.springbatch" />
把上面的加到applicationContext.xml文件中, <context:annotation-config /> 这个制定注解,<context:component-scan > 指定要spring 扫描的包 ,里面所有的 加了@component 的都会自动成为spring管理的bean

      实例中用到的输入数据如下:

               

      实例输出结果如下:    

               

      本文的配置要注意以下两点:

      1,  注意Writer的resource要写成“file:******”形式,不能用“classpath:******”形式。

      2,  如果将Job配置中commit-interval属性配置为大于1时,每次commit的都是最后一条记录,前面读取的被覆盖了。具体原因不明,如果将Reader的fieldSetMapper属性自己重写,就可以解决这个问题。(注:student bean添加scope属性可以解决此问题:scope:"prototype".2011/12/16)


读取多个文件的方式如下:
假设有两个文件inputFile1.csv和inputFile2.csv。做如下修改:
1,batch.xml文件中添加
<bean:bean id="multiCsvFileReaders" class="org.springframework.batch.item.file.MultiResourceItemReader">
<bean:property name="resources" value="classpath:inputFile*.csv" />
<bean:property name="delegate" ref="csvItemReader" />
</bean:bean>
2,csvItemReader中的resource属性删除
3,job中的reader属性配置为上面的multiCsvFileReaders
这样就可以读多个文件了。
至于过滤一些行的内容,可以重写一下csvItemWriter,当写入时判断一下,需要过滤的行不要写入就可以了!







  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值