Spring Batch dynamic Flow/Job construction

2019-09-10 18:34发布

I'm currently using Spring Batch to run a job that processes a file, does some stuff on each line and writes the output to another file.

This was developed in a 'core' product but now (as always) we have some client-specific requirements that mandate the inclusion of some extra steps in the job.

I've been able to do a proof-of-concept where use the common Spring features to be able to 'replace' the job with another one with the extra steps either by using distinct names for the job (if we define them in the same Configuration class) or by creating a completely distinct Configuration class and loading that as the Spring context.

What i'm asking is, and i'm 'almost' there, if it's possible to easily define a base Job (maybe with an initial step or not) and then only add the steps that make sense for that specific 'client'.

I'm using standard class inheritance to do this but it doesn't work properly with standard Spring facilities since Spring won't know which implementation of the "getSteps" method to use (code below).

abstract class JobConfig {

@Autowired
private JobBuilderFactory jobBuilderFactory;

@Autowired
protected StepBuilderFactory stepBuilderFactory;

@Bean
Job job() {
    List<Step> steps = getSteps();
    final JobBuilder jobBuilder = jobBuilderFactory.get("job")
        .incrementer(new RunIdIncrementer());

    SimpleJobBuilder builder = jobBuilder.start(steps.remove(0));
    for (Step s : steps) {
        builder = builder.next(s);
     }
     return builder.build();
  }

  protected abstract List<Step> getSteps();
}


@Configuration
@Import(BaseConfig.class)
public class Client1JobConfig extends JobConfig {

@Override
protected List<Step> getSteps() {
    List<Step> steps = new ArrayList<>();
    steps.add(step1());
    return steps;
}

Step step1() {
    return stepBuilderFactory.get("step1")
        .<Integer, Integer>chunk(1)
        .reader(dummyReader())
        .processor(processor1())
        .writer(dummyWriter())
        .build();
   }
}

@Configuration
@Import(BaseConfig.class)
public class Client2JobConfig extends JobConfig {

@Override
protected List<Step> getSteps() {
    List<Step> steps = new ArrayList<>();
    steps.add(step1());
    steps.add(step2());
    return steps;
}

Step step1() {
    return stepBuilderFactory.get("step1")
        .<Integer, Integer>chunk(1)
        .reader(dummyReader())
        .processor(processor1())
        .writer(dummyWriter())
        .build();
}


Step step2() {
    return stepBuilderFactory.get("step2")
        .<Integer, Integer>chunk(1)
        .reader(dummyReader())
        .processor(processor2())
        .writer(dummyWriter())
        .build();
    }
}

I can make it work if i load just one Configuration class into the Spring context but if i have all the Configuration classes loaded (either by component scanning, or manually adding them to the context) of course it doesn't work because there's no way to select wither one implementation of the other.

I can also make it work by having differently-named jobs like "client1" and "client2" but let's say i can't change the calling code and the job is Autowired. How can i have the 'same' some but with different steps?

Is there a better way to accomplish this?

0条回答
登录 后发表回答