Skip to content

Spring Batch 重用现有服务教程:ItemReaderAdapter 与 ItemWriterAdapter

🎯 概述:为什么需要重用服务?

在真实业务场景中,Spring Batch 通常需要与其他应用(如在线系统、集成服务或客户端应用)协同工作。常见需求是复用已有的服务组件(如DAO层),避免重复造轮子。Spring Batch 提供了两种优雅的适配器来解决这个问题:

🔧 适配器工作原理

Spring Batch 使用委托模式实现服务复用:

  • ItemReaderAdapter:将普通服务方法适配为 ItemReader
  • ItemWriterAdapter:将普通服务方法适配为 ItemWriter

TIP

适配器就像万能转换器,让普通服务方法无缝接入批处理流程


📖 ItemReaderAdapter 使用指南

核心配置(Kotlin DSL)

kotlin
@Configuration
class BatchConfig {

    // 配置ItemReader适配器
    @Bean
    fun itemReader(): ItemReaderAdapter<Foo> {
        return ItemReaderAdapter<Foo>().apply {
            targetObject = fooService()   // [!code highlight] // 绑定目标服务
            targetMethod = "generateFoo"  // [!code highlight] // 指定服务方法
        }
    }

    // 被复用的服务
    @Bean
    fun fooService() = FooService()
}

// 示例服务类
class FooService {
    private var count = 0
    
    fun generateFoo(): Foo? {
        return if (count < 5) {
            count++
            Foo("Item-$count")
        } else null  // [!code warning] // 关键点:必须返回null表示结束
    }
}

// 数据对象
data class Foo(val name: String)

关键注意事项

必须遵守的契约

被适配的方法返回值类型必须与 read() 方法相同:

  • 正常数据:返回对象实例
  • 数据耗尽:返回 null

CAUTION

如果方法不返回 null,会导致批处理陷入无限循环或错误终止!


✍️ ItemWriterAdapter 使用指南

核心配置(Kotlin DSL)

kotlin
@Configuration
class BatchConfig {

    // 配置ItemWriter适配器
    @Bean
    fun itemWriter(): ItemWriterAdapter<Foo> {
        return ItemWriterAdapter<Foo>().apply {
            targetObject = fooService()   // [!code highlight] // 绑定目标服务
            targetMethod = "processFoo"   // [!code highlight] // 指定处理方法
        }
    }

    @Bean
    fun fooService() = FooService()
}

class FooService {
    // 处理方法:参数类型必须匹配写入的数据类型
    fun processFoo(item: Foo) {           // [!code warning] // 注意参数类型匹配
        println("Processing: ${item.name}")
        // 实际业务逻辑:保存到DB/发送消息等
    }
}

方法签名要求

被适配的写入方法必须满足:

  1. 方法参数:单个对象或对象集合
  2. 返回类型:void 或不返回
kotlin
// 适合小数据量场景
fun processItem(item: Foo) {
    // 处理单个对象
}
kotlin
// 适合大数据量高效处理
fun processItems(items: List<Foo>) {
    // 批量处理逻辑
}

🚫 常见错误与解决方案

错误1:忘记返回null导致无限循环

kotlin
// 错误实现 ❌
fun generateData(): Foo {
    return Foo("data") // 总是返回对象,不会停止
}

// 正确实现 ✅
fun generateData(): Foo? {
    return if (hasNext) Foo("data") else null
}

错误2:方法签名不匹配

kotlin
// 错误:ItemWriter需要处理对象集合 ❌
fun saveItem(item: Foo) { ... }

// 正确:参数类型匹配批处理数据 ✅
fun saveItems(items: List<Foo>) { ... }

WARNING

类型不匹配会导致 ClassCastException 运行时异常!


💡 最佳实践建议

  1. 隔离适配逻辑:为适配器创建专用配置类
  2. 方法命名规范
    • 读取方法:generateXxx(), fetchXxx()
    • 写入方法:processXxx(), saveXxx()
  3. 空值处理:使用 Optional 避免NPE
kotlin
// 安全的读取方法实现
fun safeGenerate(): Foo? {
    return try {
        // 业务逻辑
    } catch (e: Exception) {
        null // 异常时返回null终止处理
    }
}
  1. 性能优化:批量处理时配置合适的 chunk size
kotlin
@Bean
fun step(): Step {
    return stepBuilderFactory.get("myStep")
        .chunk<Foo, Foo>(50) // [!code highlight] // 每50条处理一次
        .reader(itemReader())
        .writer(itemWriter())
        .build()
}

✅ 总结:重用服务的优势

方案优点适用场景
ItemReaderAdapter复用数据生成逻辑数据加载/转换
ItemWriterAdapter复用数据持久化逻辑数据保存/推送
直接注入简单直接辅助逻辑处理

通过适配器模式,我们实现了:

  1. 逻辑复用:避免重复代码
  2. 架构统一:服务与批处理无缝集成
  3. 维护简化:业务变更只需修改服务实现

[!SUCCESS] Spring Batch 的适配器就像 万能转换插头,让现有服务轻松接入批处理系统,显著提升开发效率和系统一致性!