领先一步
VMware 提供培训和认证,助您加速进步。
了解更多我们一直在努力开发 Spring Batch,为 Spring Portfolio 2.5 版本系列做准备,我认为现在是时候向大家更新一下情况了。在这篇文章中,我将稍微扩展一下领域模型,以及我们提高一些核心领域对象知名度、增加其职责的决定。我还会提供一些即将到来的、直至 1.0 的版本的一些预览,以便大家有机会发表评论。
作为道歉:自上次我撰写关于 Spring Batch 的博客以来,其内部发生了一些相当重大的变化,所以我感觉自己疏忽了。在这篇文章中,我认为我无法涵盖所有更改,但我保证将来会努力保持更新,并且在 1.0 版本发布临近时,我一定会让大家及时了解最新情况。(注意:Spring Portfolio 2.5 发布列车包含 Spring Batch 1.0。)
由于我们自 1.0.0-m3 以来修复了 70 多个问题,我们觉得是时候发布一些东西了。我们上周制定的计划是明天(2 月 5 日)发布 1.0.0-m4,范围比原计划略窄(例如,推迟 XML 命名空间实现)。然后 1.0.0-m5 将在大约 10-14 天后发布,这样在 3 月 20 日 1.0.0 最终发布之前,至少有时间发布一个候选版本。
Spring Batch 核心是一个非常紧凑的 API。实际上,它几乎不包含我们期望批处理应用程序开发人员实现或扩展的任何内容,因此它实际上是一个内部 API。尽管如此,它对 Spring Batch 用户产生了相当深远的影响,因为它塑造了我们思考批处理作业及其执行方式,以及(对用户来说更重要的是)其实现、配置和部署方式。
在 1.0.0-m3 之前,我们遇到了一个经典的 API 与批处理领域通用语言不匹配的案例。我们不得不一直解释说JobConfiguration是大多数人所说的“作业”(对于StepConfiguration和“步骤”也是如此)。“作业”是您配置每天运行的东西,但每次运行时,它都有一个新的标识。这是一个明确的信号,“作业”是领域概念的名称,那么我们为什么要称之为“JobConfiguration”呢?好问题。所以现在(在 1.0.0-m4 中)用户配置的东西是作业,当它运行时,我们创建一个新的JobInstance。对于Step还是StepInstance也是如此。因此,实体原型由*Instance对象实现——它们在数据库中具有 id 和主键。一个例子是 [JobInstance2008 年 2 月 2 日生效],用于 ["end-of-day"作业].
另一个实体原型是StepExecution还是StepExecution。自 m3 以来,它们的名称和职责没有改变。当一个JobInstance被执行时,我们创建一个StepExecution(首先确保该实例尚未执行)。此时,[StepExecution2 月 2 日晚上 10 点],[JobInstance2008 年 2 月 2 日生效] 的 ["end-of-day"作业] 启动。但如果它失败了,并在第二天重新启动,那么我们需要一个新的 [StepExecution2 月 3 日晚上 10:12] 用于相同的JobInstance(即 [JobInstance2008 年 2 月 2 日生效] 的 ["end-of-day"作业])。因此作业与JobInstance是一对多的关系,后者又与StepExecution是一对多的关系。这同样适用于Step*.
我们还在 1.0.0-m4 中对 API 进行了更改,涉及作业还是Step的执行。在 1.0.0-m3 之前,我们为每个执行都有一个单独的接口——JobExecutor执行作业和StepExecutor执行Step。正如我们当时设计时所认为的,这具有封装的优点——我们设想了多个StepExecutor的实现都能够执行相同的Step。在实践中,随着我们对实现轮廓的了解越来越多,我们发现这是一个人为的区别。有趣的是,信号是我们在 Java 的StepExecutor实现中进行了太多“instanceof”检查——它们总是必须针对不同的Step实现做特殊处理。最终很明显,每个Step都必须知道如何执行自己。与所有此类见解一样,当你看到它时,它是显而易见的,但在此之前,它绝不是如此。谢谢 Eric Evans。
因此,例如,Step领域的核心接口是
public interface Step {
// ... properties that the Job needs to know here ...
void execute(StepExecution stepExecution)
throws StepInterruptedException, BatchCriticalException;
}
没有必要让Step从execute方法返回任何内容,因为传入的StepExecution会在步骤执行过程中更新。它由调用者传入,如果需要,可以用于监控执行进度。如果调用者需要中断作业(因此出现StepInterruptedException),它也可以用于停止执行,只需设置一个标志setTerminateOnly()。这是几个人问过我的问题,所以这里可能值得一提:Step负责在任何可能的地方检查该标志的值,因此框架中内置了一种机制用于发出作业提前终止的信号。设置该标志的效果取决于Step的实现,但我们提供了一个SimpleStep,它会在处理完每个项目后检查该标志,并在必要时中止。它还接受一个StepInterruptPolicy策略,可用于检查其他异常情况(例如Thread.isInterrupted()).
Spring Batch 几乎是 Spring XML 命名空间如何帮助应用程序开发人员简化工作的完美示例。我们尚未实现NamespaceHandler,但它已安排在下一个里程碑 1.0.0-m5 中,所以现在是您感受示例的好时机。
座右铭是“让配置看起来像领域模型”,本着这种精神,请看这个例子,看看它是否有意义。它是新命名空间的一个草案,模仿了现有的固定长度导入示例作业,因此熟悉它的人会看到其中的相似之处。如果您需要查找现有示例,只需访问 Spring Batch 主页下载发布版本(固定长度示例配置也可以在此处浏览)。
<batch>
<job id="fixedLengthImportJob" volatile="false">
<simple-step id="step1" chunk-size="50" save-restart-data="false"
allow-start-if-complete="true" reader-ref="fileInputTemplate">
<processor>
<beans:bean
class="org.springframework.batch.sample.item.processor.TradeProcessor">
<property name="writer" ref="tradeDao" />
</beans:bean>
</processor>
<simple-completion-policy skipLimit="5" />
</simple-step>
<simple-step id="step2" chunk-size="200">
<jdbc-cursor-reader data-source-ref="dataSource">
<query><![CDATA[SELECT ID FROM T_TRADE ORDER BY ID WHERE PROCESSED='N']]></query>
</jdbc-cursor-reader>
<processor>
<beans:bean
class="org.springframework.batch.sample.item.processor.TradeUpdater">
<property name="dao" ref="tradeDao" />
</beans:bean>
</processor>
</simple-step>
<tasklet-step id="step3" chunk-size="1">
<tasklet>
<beans:bean
class="org.springframework.batch.sample.sproc.TradeSummarizer"
p:dataSource-ref="dataSource" />
</tasklet>
</tasklet-step>
</job>
<!-- INFRASTRUCTURE SETUP -->
<flat-file-reader id="fileInputTemplate"
resource="data/fixedLengthImportJob/input/20070122.teststream.ImportTradeDataStep.txt"
field-set-mapper-ref="fieldSetMapper"
validator-ref="fixedValidator">
<fixed-length-tokenizer>
<columns>
ISIN=1-12 Quantity=13-15 Price=16-20 Customer=21-29
</columns>
</fixed-length-tokenizer>
</flat-file-reader>
<beans:bean id="fixedValidator"
class="org.springframework.batch.item.validator.SpringValidator">
<property name="validator">
<bean id="tradeValidator"
class="org.springmodules.validation.valang.ValangValidator">
<property name="valang">
<value>
<![CDATA[
{ isin : length(?) < 13 : 'ISIN too long' : 'isin_length' : 12}
]]>
</value>
</property>
</bean>
</property>
</beans:bean>
<beans:bean id="tradeDao"
class="org.springframework.batch.sample.dao.JdbcTradeWriter">
<property name="jdbcTemplate" ref="jdbcTemplate" />
<property name="incrementer">
<bean parent="incrementerParent">
<property name="incrementerName" value="TRADE_SEQ" />
</bean>
</property>
</beans:bean>
<beans:bean id="fieldSetMapper"
class="org.springframework.batch.sample.mapping.TradeFieldSetMapper" />
</batch>
请注意作业还是Step的实现细节是如何隐藏的——<simple-step/>和<tasklet-step/>之间存在差异,但唯一可见的差异是用户需要知道的那些。用户不需要知道支持此配置的Step接口有不同的实现,但存在面向项目和面向任务的步骤方法这一事实并没有隐藏。这就是我们认为对用户很重要的。当我们起草这些 XML 示例时,我们从现有的 Spring Batch 示例中提取了几个例子并进行了修改,直到包含相同的配置信息,但在大多数情况下,文件的总大小减少了 50% 或更多。这一定意味着我们走在正确的轨道上。
这是一次非常快速但深入地探讨了 Spring Batch 和最近的一些变化。我还可以与您分享更多关于我们使用和实现框架的经验的有趣细节。我的 JavaOne 演讲被接受了,这意味着您可以期待在五月份在旧金山了解更多信息,并听到更多真实批处理的示例。此外,随着 2.5 版本发布列车开始其旅程(从用户的角度来看!),请关注本博客上的更多内容。
最后,我需要向 Lucas Ward 和 Ben Hale 致敬,他们在 Spring Batch 的开发过程中发挥了重要作用。我还想欢迎 Robert Kasanicky 成为我们最新的提交者——Robert 为 Spring Batch 贡献了一些非常高质量的代码,我相信在我们将 1.0 收尾并开始下一个主要版本的工作时,他将继续这样做。如果其他人有兴趣贡献,Robert 遵循了在论坛和 JIRA 上活跃、贡献想法和(关键是)补丁的正常流程。我提名了他,我们进行了投票,现在他正式成为团队的一员。
如果文章中有任何需要澄清的地方,请随时在此处发表评论。有关 Spring Batch 的一般讨论,请使用论坛。Spring Batch 的主页在此处,或者可以轻松地从Spring 主网站找到