这里我们讲述一下springbatch中关于step层面上面的数据共享技术。而对街的人影都浸染在一片薄荷的白色中,由于无声,都好像经过漂染,不沾人间烟火。
step的数据共享
关于springbatch里面的step的数据共享,可以有很多的实现方式。比如可以用数据库记载共享的数据、文件保存共享的数据等等。这里我们重点讨论的是springbatch与spring可以解决这类问题的方案。总的来说,可以有如下两种方式。
Execution context Use a Spring Batch execution context as a container for user data. A step writes to the execution context; then another step reads from the execution context. Holder Use a Spring bean and dependency injection. Spring injects a holder bean in the communicating beans. A first step sets values in the holder; another step reads values from the holder.
下面我们针对这同种方式做一个测试的案例。
一、使用Execution context方式共享数据
- 定义一个job,在batch.xml里面
<job id="shareDataContextJob"> <step id="setDateStep" next="getDateStep"> <tasklet transaction-manager="transactionManager" ref="setDataContextTasklet"/> </step> <step id="getDateStep"> <tasklet transaction-manager="transactionManager" ref="getDataContextTasklet"/> </step> </job>
- setDataContextTasklet与getDataContextTasklet的定义
<bean id="setDataContextTasklet" class="spring.batch.shareDataContext.SetDataContextTasklet"/> <bean id="getDataContextTasklet" class="spring.batch.shareDataContext.GetDataContextTasklet"/>
- setDataContextTasklet与getDataContextTasklet的实现
存放数据的实现类
package spring.batch.shareDataContext; 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.item.ExecutionContext; import org.springframework.batch.repeat.RepeatStatus; /** * @Author: huhx * @Date: 2017-11-02 下午 8:41 */ public class SetDataContextTasklet implements Tasklet { @Override public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception { ExecutionContext jobExecutionContext = chunkContext.getStepContext(). getStepExecution(). getJobExecution(). getExecutionContext(); jobExecutionContext.putString("username", "huhx"); System.out.println("set data tasklet."); return RepeatStatus.FINISHED; } }
得到数据的实现类
package spring.batch.shareDataContext; 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.item.ExecutionContext; import org.springframework.batch.repeat.RepeatStatus; /** * @Author: huhx * @Date: 2017-11-02 下午 8:41 */ public class GetDataContextTasklet implements Tasklet { @Override public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception { ExecutionContext jobExecutionContext = chunkContext.getStepContext(). getStepExecution(). getJobExecution(). getExecutionContext(); String username = jobExecutionContext.getString("username"); System.out.println("username = " + username); return RepeatStatus.FINISHED; } }
运行的结果,setDateStep得到的getDateStep存放的数据。
set data tasklet. username = huhx
另外我们还有另外一种写法。在基于上述的代码做如下的修改:getDataContextTasklet和定义与实现。注意scope="step"是必须的。
<bean id="getDataContextTasklet" class="spring.batch.shareDataContext.GetDataContextTasklet" scope="step"> <property name="username" value="#{jobExecutionContext['username']}"/> </bean>
getDataContextTasklet的实现类,可以直接注入username得到setDataContextTasklet里面设置的username值。
package spring.batch.shareDataContext; 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.item.ExecutionContext; import org.springframework.batch.repeat.RepeatStatus; /** * @Author: huhx * @Date: 2017-11-02 下午 8:41 */ public class GetDataContextTasklet implements Tasklet { private String username; public void setUsername(String username) { this.username = username; } @Override public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception { System.out.println("username = " + username); return RepeatStatus.FINISHED; } }
二、通过Spring holder beans共享数据
- 定义一个job,在batch.xml中
<job id="shareDataHolderJob"> <step id="setDateHolderStep" next="getDateHolderStep"> <tasklet transaction-manager="transactionManager" ref="setDataHolderTasklet"/> </step> <step id="getDateHolderStep"> <tasklet transaction-manager="transactionManager" ref="getDataHolderTasklet"/> </step> </job>
- setDataHolderTasklet、getDataHolderTasklet以及共享数据Bean类的定义
<!--通过holder分享数据--> <bean id="importMetadata" class="spring.batch.shareDataHolder.ImportMetadataHolder"/>
<bean id="setDataHolderTasklet" class="spring.batch.shareDataHolder.SetDataHolderTasklet"> <property name="importMetadataHolder" ref="importMetadata"/> </bean> <bean id="getDataHolderTasklet" class="spring.batch.shareDataHolder.GetDataHolderTasklet"> <property name="importMetadataHolder" ref="importMetadata"/> </bean>
- 上述xml定义的bean类的实现
ImportMetadataHolder:共享数据Bean类。
package spring.batch.shareDataHolder; import spring.batch.readFile.People; /** * @Author: huhx * @Date: 2017-11-03 上午 8:56 */ public class ImportMetadataHolder { private People people; public People getPeople() { return people; } public void setPeople(People people) { this.people = people; } }
SetDataHolderTasklet:设置共享数据的实现类
package spring.batch.shareDataHolder; 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; import spring.batch.readFile.People; import java.util.Date; /** * @Author: huhx * @Date: 2017-11-03 上午 9:00 */ public class SetDataHolderTasklet implements Tasklet { private ImportMetadataHolder importMetadataHolder; public void setImportMetadataHolder(ImportMetadataHolder importMetadataHolder) { this.importMetadataHolder = importMetadataHolder; } @Override public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception { People people = new People(); people.setUsername("huhx"); people.setBirthday(new Date()); people.setAddress("武汉"); people.setAge(23); importMetadataHolder.setPeople(people); return RepeatStatus.FINISHED; } }
GetDataHolderTasklet:获取共享数据的实现类
package spring.batch.shareDataHolder; 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; /** * @Author: huhx * @Date: 2017-11-03 上午 9:00 */ public class GetDataHolderTasklet implements Tasklet { private ImportMetadataHolder importMetadataHolder; public void setImportMetadataHolder(ImportMetadataHolder importMetadataHolder) { this.importMetadataHolder = importMetadataHolder; } @Override public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception { System.out.println(importMetadataHolder.getPeople()); return RepeatStatus.FINISHED; } }
运行的结果,在控制台打印:
username=huhx|age=23|address=武汉|birthday=Fri Nov 03 09:06:24 CST 2017
之所以ImportMetadataHolder类能够共享,是由于spring中Bean的默认scope是singleton单例的。如果修改scope为prototype。那么输出的结果为null。
另外这种方式也可以使用SpEL表达式的方式来注入要共享的数据。修改getDataHolderTasklet的定义和实现类以及修改importMetadata的scope=singleton。如下
<bean id="getDataHolderTasklet" class="spring.batch.shareDataHolder.GetDataHolderTasklet" scope="step"> <property name="username" value="#{importMetadata.getPeople().getUsername()}"/> </bean>
GetDataHolderTasklet可以直接注入username,打印它的结果为huhx。这里就不贴出代码。