GOOGLE ADS

sábado, 30 de abril de 2022

Spring Batch: defina el alcance de paso del lector/escritor mediante programación sin anotación

Definiría múltiples lectores/escritores Spring Batch programáticamente, especialmente para StepScope.

Es común definir un lector con la anotación @StepScope, etc., pero este no es mi escenario.

COMO ES:

@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;
}

Pero tengo mucho trabajo (con su lector y escritor) para definir (~20) y podría llegar más... y, en general, no modificaría el código para cada nuevo trabajo/lector/escritor que tengo que proporcionar.

Entonces, la idea, TO-BE (pseudo-código):

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);
}

A continuación, métodos comunes para crear lectores y escritores, comunes a los escenarios TAL CUAL y FUTURA:

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;
}

Desafortunadamente, no puedo encontrar una manera de especificar el StepScope para aplicar al lector/escritor GenericBeanDefinition. Además, encontré la clase org.springframework.batch.core.scope.StepScope, que parece ser responsable de aplicar ese alcance al bean anotado @StepScope, y declarado en una clase anotada @Configuration que también tiene la anotación @EnableBatchProcessing en la parte superior de la clase mismo, entre @Configuration. Desafortunadamente, tampoco pude encontrar una manera de usar ese StepScope. Y necesito StepScope para lector/escritor porque necesitan algunos parámetros del contexto laboral, como:

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

Al no proporcionar el alcance del paso para el lector/escritor, por supuesto, esas inyecciones no funcionarán y tendré un valor nulo, ya que los datos que pasan entre los pasos no funcionarán (usando StepExecutionContext).

Entonces, ¿alguna sugerencia? También usando otra forma, por supuesto... un enfoque diferente al mío.

Gracias


Solución del problema

Puedes GenericBeanDefinition#setScope(String)usar Así que en tu caso podría ser algo como:

readerBeanDefinition.setScope("step");

Tenga en cuenta que el "paso" del alcance debe registrarse en el contexto de la aplicación, ya que no está registrado de forma predeterminada (es un alcance personalizado de Spring Batch).

No hay comentarios.:

Publicar un comentario

Flutter: error de rango al acceder a la respuesta JSON

Estoy accediendo a una respuesta JSON con la siguiente estructura. { "fullName": "FirstName LastName", "listings...