当前位置: 首页 > 知识库问答 >
问题:

Spring Batch-以编程方式定义读写器stepscope,无需注释

秦博延
2023-03-14

第一版

我将以编程方式定义多个Spring批处理读取器/写入器,尤其是对于StepScope。

通常使用注释@StepScope等定义阅读器,但这不是我的场景。

原样:

@Bean
public Job exporterJobForLUS149A() {
    Job jobWithStep = buildJobWithStep(LUS149A.class, readerForLUS149A(), writerForLUS149A());
    return jobWithStep;
}

@StepScope
@Bean
public EntityMongoItemReader<LUS149A> readerForLUS149A() {
    final EntityMongoItemReader<LUS149A> reader = reader(LUS149A.class);
    return reader;
}

@StepScope
@Bean
public CommonEntityToFlatFileItemWriter<LUS149A> writerForLUS149A() {
    final CommonEntityToFlatFileItemWriter<LUS149A> writerForLUS149A = writer(LUS149A.class);
    return writerForLUS149A;
}

但我有很多工作(与它的读者和作者一起)需要定义(~ 20),而且可能会达到。。。一般来说,我不会为我必须提供的每个新工作/读写器修改代码。

所以,这个想法,未来(伪代码):

for (String entityClass: entities) {
    // this provides the entityClass, in some way
    Class<? extends AccountSessionAware> entityClass = buildEntityClass(entitiesDefaultPackage, entityName, 
    output.getEntityPackage());
    
    // for readers
    EntityMongoItemReader<? extends AccountSessionAware> reader = buildReader(entityClass);
    GenericBeanDefinition readerBeanDefinition = new GenericBeanDefinition();
    readerBeanDefinition.setBeanClassName(EntityMongoItemReader.class.getName());
    readerBeanDefinition.setInstanceSupplier(() -> reader);
    // this setScope allows scope from BeanDefinition (application/singleton, infrastructure, prototype, etc...) but I would use StepScope
    readerBeanDefinition.setScope(STEP_SCOPE_I_HAVE_NOT);
    registry.registerBeanDefinition("readerFor" + entityName, readerBeanDefinition);
    log.info("registered: " + "readerFor" + entityName);

    // for writers
    CommonEntityToFlatFileItemWriter<? extends AccountSessionAware> writer = buildWriter(entityClass);
    GenericBeanDefinition writerBeanDefinition = new GenericBeanDefinition();
    writerBeanDefinition.setBeanClassName(EntityMongoItemReader.class.getName());
    writerBeanDefinition.setInstanceSupplier(() -> writer);
    // same problem as reader above
    writerBeanDefinition.setScope(STEP_SCOPE_I_HAVE_NOT);
    registry.registerBeanDefinition("writerFor" + entityName, writerBeanDefinition);
    log.info("registered: " + "writerFor" + entityName);


    Job jobWithStep = buildJobWithStep(entityClass, reader, writer);
    GenericBeanDefinition jobBeanDefinition = new GenericBeanDefinition();
    jobBeanDefinition.setBeanClassName(Job.class.getName());
    jobBeanDefinition.setInstanceSupplier(() -> jobWithStep);
    // for Job we have singleton scope 
    jobBeanDefinition.setScope(BeanDefinition.SCOPE_SINGLETON);
    registry.registerBeanDefinition("jobFor" + entityName, jobBeanDefinition);
    log.info("registered: " + "jobFor" + entityName);
}

下面是构建阅读器和写入器的常见方法,这些方法在AS-IS和TO-BE场景中很常见:

private public <eASAx extends AccountSessionAware> EntityMongoItemReader<eASAx> reader(final Class<eASAx> entityToReadClass) {
    LinkedHashMap<String, SortType> commonSort = outputFromDomain.getSort().getCommon();
    final LinkedHashMap<String, SortType> furtherSort = csvExporterType.getOutputs().get(entityToReadClass.getSimpleName()).getAdditionalSort();
    final EntityMongoItemReader<eASAx> reader = new EntityMongoItemReader<>(entityToReadClass, readingPageSize, commonSort, furtherSort,            mongoTemplate);
    return reader;
}

private <eASAx extends AccountSessionAware> CommonEntityToFlatFileItemWriter<eASAx> writer(final Class<eASAx> eASAxxxClass) {
    final String[] headerColumnNames = csvExporterType.getOutputs().get(eASAxxxClass.getSimpleName()).getColumns().split(ExporterConstants.COMMA);
    final String outputFileName = eASAxxxClass.getSimpleName();

    final EntityToFlatFileItemWriter<eASAx> writer = new EntityToFlatFileItemWriter<>(eASAxxxClass, headerColumnNames, outputDir, outputFileName,   fieldsSeparator, writingChunkSize);

    // if required, create multifile writer instance
    if (resourceLimit > 0) {
        final EntityToMultiResourceFlatFileItemWriter<eASAx> entityToMultiResourceFlatFileItemWriter = new EntityToMultiResourceFlatFileItemWriter<>(writer, resourceLimit);
        return entityToMultiResourceFlatFileItemWriter;
    }

    return writer;
}

遗憾的是,我无法找到一种方法来指定要应用于读写器GenericBeanDefinition的StepScope。还有,我找到了org。springframework。一批果心范围StepScope类,它似乎负责将该作用域应用于@StepScope注释bean,并在@Configuration注释类中声明,在@Configuration注释类中,在类本身的顶部还具有@EnableBatchProcessing注释。不幸的是,我也找不到使用StepScope的方法。我需要读写器的StepScope,因为它们需要来自作业上下文的一些参数,例如:

@Value("#{jobParameters['session']}")
private String session;
@Value("#{jobParameters['sort']}")
private boolean toSort;

当然,如果不为读写器提供步骤范围,那么这些注入将不起作用,并且我将具有空值,因为步骤之间传递的数据将不起作用(使用StepExecutionContext)。

那么,有什么建议吗?当然,也用其他方式。。。与我不同的方法。

第二版(更新)

现在代码是这样的:

@EnableBatchProcessing
@Configuration
public class ExporterBatchConfigDynamic extends DefaultBatchConfigurer {

  @Autowired
  private GenericApplicationContext genericApplicationContext;

[...]

  @PostConstruct
  public void init() {
    for (String entityClass: entities) {

      GenericEntityMongoItemReader reader = reader(entityClass);                 
      genericApplicationContext.registerBean("readerFor" + entityName, GenericEntityMongoItemReader.class, () -> reader, Customizers::stepScoped);

      
CommonEntityToFlatFileItemWriter<AccountSessionAware> writer = writer(entityClass);
      genericApplicationContext.registerBean("writerFor" + entityName, CommonEntityToFlatFileItemWriter.class, () -> writer, Customizers::stepScoped);

      Job jobWithStep = buildJobWithStep(entityClass, reader, writer, namesToJobsMapping());
      genericApplicationContext.registerBean("jobFor" + entityName, Job.class, () -> jobWithStep, Customizers::jobScoped);
      log.info(entityName + ":: registered: " + "readerFor" + entityName + ", writerFor" + entityName + ", jobFor" + entityName);       
    }
  }
  
  [...]

  public static class Customizers {
    public static void stepScoped(final BeanDefinition bd) {
      bd.setScope(SCOPE_STEP);
    }

    public static void jobScoped(final BeanDefinition bd) {
      bd.setScope(SCOPE_JOB);
    }
  }

  // after this, also the ScopeConfiguration is declared, including Step and Job from comment below
}

现在读者和作者(显然)都在分步思考。。该应用程序可以工作,但只适用于第一次调用。在第二次调用时,读取器读取0个条目,并将0个块传递给writer,当然,writer会写入0。我知道当读写器不在步骤范围内时会发生这种情况:所以,我用于bean注册的定制器工作不正常,或者通常注册方式不正确。

还有什么建议吗?

谢谢

共有1个答案

宰父衡
2023-03-14

您可以使用GenericBeanDefinition#设置范围(字符串)。因此,在你的情况下,可能是这样的:

readerBeanDefinition.setScope("step");

注意,范围“步骤”应该在应用程序上下文中注册,因为默认情况下它没有注册(它是Spring Batch中的自定义范围)。

 类似资料:
  • 问题 你在写一段代码,最终需要创建一个新的类对象。你考虑将类的定义源代码以字符串的形式发布出去。 并且使用函数比如 exec() 来执行它,但是你想寻找一个更加优雅的解决方案。 解决方案 你可以使用函数 types.new_class() 来初始化新的类对象。 你需要做的只是提供类的名字、父类元组、关键字参数,以及一个用成员变量填充类字典的回调函数。例如: # stock.py # Example

  • 在 imi 框架中,使用注解可以实现很多功能。比如:路由、模型定义、事务、缓存等等 除了内置的注解以外,如果编写属于自己的注解呢? 这篇教程就来教大家来编写属于自己的注解。 注解定义 注解扫描 imi 是常驻内存运行的,所以冷启动时采用了全量扫描的方式,来实现注解缓存。使用的时候,就和读取配置一样简单高效。 一般需要在配置文件里的beanScan中,配置注解类所在命名空间。在 imi 框架中,涉及

  • 问题内容: 我想知道是否已经有一个库可以以编程方式编写Java类或方法? 我正在寻找能够将新的源代码写入现有文件或扩展已经存在的文件的库。 问题答案: 查看Eclipse JDT。 Eclipse Java开发工具(JDT)提供用于访问和操作Java源代码的API。它允许访问工作空间中的现有项目,创建新项目以及修改和读取现有项目。 更具体地说,您可以使用Java Model API创建新的Java

  • 下面是读取多个项目的自定义平面文件项目读取器的代码 } 下面是自定义项目编写器的代码 } 我是Spring批处理的新手。这段代码正确吗?我可能缺少任何用例?目前我的批处理作业按顺序执行,但将来可能会使用多线程和分区。 需要这样做是因为我需要在处理器中进行数据库查找。为多个项目进行查找比为单个项目进行查找要好。

  • 我有一个模型,其中有一个@列(nullable=false)注释HiberNate和所有字段有nullable=false,我想以编程方式添加一些新的注释,如@NotNull和@ApiModelProperty(必需=true)-用于招摇过市。 所以,我希望能够从我的应用程序的模型中解析所有字段,获得现有的注释,并在此基础上添加新的注释。这能做到吗? 更新:问题是每次添加一个新字段,如果它不能为空

  • 我想编写自定义Lombok注释处理程序。我知道http://notatube.blogspot.de/2010/12/project-lombok-creating-custom.html.但是当前的lombok jar文件并不包含很多内容。类文件,但文件名为。症状自评量表。取而代之的是龙目山。 我发现,的。SCL. lombok文件是. class文件,Lombok的构建脚本在生成jar文件时重