领先一步
VMware 提供培训和认证,以加速您的进步。
了解更多我们一直在努力开发 Spring Batch,为 Spring 产品组合 2.5 发布列车做准备,我认为现在是时候更新大家正在发生的事情了。在这篇文章中,我将对领域建模进行一些扩展,以及我们提高一些核心领域对象知名度并增加其责任的决定。我还将提供一些关于未来几个版本(直至 1.0 版本)中即将推出的内容的示例,以便大家有机会发表评论。
在此表示歉意:自从我上次在博客中写到 Spring Batch 以来,其内部结构发生了一些相当重大的变化,因此我感觉自己有些疏忽。在这篇文章中,我认为我无法涵盖所有变化,但我承诺将来会尽力保持更新,并且我一定会让大家在 1.0 版本发布时及时了解情况。(注意:Spring 产品组合 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 中),用户配置的事物是Job,并且当它运行时,我们创建一个新的JobInstance。对于Step和StepInstance也是如此。因此,实体原型由*Instance对象实现 - 它们在数据库中具有 ID 和主键。一个例子是 [JobInstance2008年2月2日生效],用于 ["end-of-day"Job].
其他实体原型是JobExecution和StepExecution。自 m3 以来,这些对象名称或职责没有发生变化。当一个JobInstance被执行时,我们创建一个JobExecution(首先确保实例尚未执行)。此时 [JobExecution2月2日晚上10点],[JobInstance2008年2月2日生效] 用于 ["end-of-day"Job],被启动。但是,如果它失败并在第二天重新启动,那么我们需要一个新的 [JobExecution2月3日晚上10:12] 用于相同的JobInstance(即 [JobInstance2008年2月2日生效] 用于 ["end-of-day"Job])。因此Job与JobInstance是一对多关系,后者又与JobExecution是一对多关系。这同样适用于Step*.
我们还在 1.0.0-m4 中对 API 在执行Job和Step方面进行了更改。在 1.0.0-m3 之前,我们为每个执行都有一个单独的接口 -JobExecutor执行Job,以及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 主页下载版本(固定长度示例配置也可以在这里浏览 here)。
<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>
注意如何隐藏Job和Step的实现细节 -<simple-step/>和<tasklet-step/>之间存在差异,但唯一的可见差异是用户需要了解的差异。用户不需要知道支持此配置的Step接口有不同的实现,但步骤的面向项目和面向任务的方法并没有被隐藏。我们认为这对用户很重要。当我们起草这些 XML 示例时,我们从现有的 Spring Batch 示例中提取了几个示例,并对其进行了修改,直到包含了相同配置信息,但在大多数情况下,文件的整体大小下降了 50% 或更多。这必须意味着我们走在了正确的道路上。
这只是一次关于 Spring Batch 和一些近期变化的快速但深入的探讨。我还可以与您分享更多关于我们在框架的使用和实现方面的经验的有趣见解。我的 JavaOne 演讲已被接受,这意味着您可以期待在 5 月份的旧金山了解更多信息,并听到更多关于实际批处理的示例。此外,随着 2.5 发布列车开始其旅程(从用户的角度来看),请关注此博客上的更多信息!
最后,我需要感谢 Lucas Ward 和 Ben Hale,他们在 Spring Batch 的开发过程中发挥了重要作用。我还想欢迎 Robert Kasanicky 加入我们的最新提交者行列 - Robert 为 Spring Batch 贡献了一些非常高质量的代码,我相信在完成 1.0 版本并开始下一个主要版本的开发时,他将继续这样做。如果其他人有兴趣贡献代码,Robert 遵循了在论坛和 JIRA 中闲逛、贡献想法和(至关重要)补丁的正常流程。我提名了他,我们进行了投票,现在他正式成为团队的一员。
如果您需要澄清文章中的任何内容,请随时在此处发表评论。有关 Spring Batch 的一般讨论,请使用 论坛。Spring Batch 的主页 here,或者可以轻松地从 Spring 主网站 找到。