首页 > 解决方案 > 使用 Spring Batch 创建一个 @JobScope @Service 来保存作业的共享内存?

问题描述

本质上,我要做的是创建一个@Service 或组件,将一些数据从数据库表中加载到内存中,该数据库表在整个作业执行过程中都会被引用

package com.squareup.se.bridge.batchworker.components.context;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.base.Strings;
import com.squareup.se.bridge.batchworker.repositories.BridgeBatchJobParametersRepository;
import com.squareup.se.bridge.batchworker.util.JobParameterKeys;
import com.squareup.se.bridge.core.api.services.batchworker.FatalSyncException;
import com.squareup.se.bridge.core.integration.util.logger.JobExecutionLoggerFactory;
import java.io.IOException;
import javax.validation.constraints.NotNull;
import org.slf4j.Logger;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.JobExecutionListener;
import org.springframework.batch.core.configuration.annotation.JobScope;
import org.springframework.stereotype.Component;

@JobScope @Component public class BridgeBatchIntegrationJobContextProvider
    implements JobExecutionListener {
  private Logger logger;
  private ObjectMapper mapper;
  private BridgeBatchJobParametersRepository bridgeBatchJobParametersRepository;
  private BridgeIntegrationJobContext context;

  public BridgeBatchIntegrationJobContextProvider(ObjectMapper mapper,
      BridgeBatchJobParametersRepository bridgeBatchJobParametersRepository) {
    this.mapper = mapper;
    this.bridgeBatchJobParametersRepository = bridgeBatchJobParametersRepository;
  }

  @Override public void beforeJob(JobExecution jobExecution) {
    var jobId = jobExecution.getJobParameters().getString(JobParameterKeys.SYNC_ID);
    this.logger = JobExecutionLoggerFactory.getLogger(
        BridgeBatchIntegrationJobContextProvider.class, jobId);
    this.context = deserializeJobParameters(jobId);
  }

  @NotNull public BridgeIntegrationJobContext get() {
    if (context == null) {
      throw new IllegalStateException("Expected context to exist before calling this method");
    }
    return context;
  }

  @Override public void afterJob(JobExecution jobExecution) { }

  @NotNull private String getParameters(String jobId) {
    var jobParams = bridgeBatchJobParametersRepository.find(jobId);
    if (jobParams == null || jobParams.size() == 0) {
      throw new FatalSyncException(String.format("No job parameters for job `%s` exists", jobId));
    }
    if (jobParams.size() > 1) {
      throw new FatalSyncException(String.format("Multiple parameter entries exist for job `%s`",
          jobId));
    } else if (Strings.isNullOrEmpty(jobParams.get(0).getIntegrationContext())) {
      throw new FatalSyncException(String.format("Job parameters for job `%s` is empty", jobId));
    }
    return jobParams.get(0).getIntegrationContext();
  }

  @NotNull private BridgeIntegrationJobContext deserializeJobParameters(String jobId) {
    try {
      return mapper.readValue(getParameters(jobId),
          BridgeIntegrationJobContext.class);
    } catch (IOException e) {
      //TODO page on this
      logger.info(e.getMessage(), e);
      throw new FatalSyncException(e);
    }
  }
}

我已经配置了这样的工作:

return jobBuilderFactory.get(CUSTOMERS_BATCH_JOB_NAME)
        .incrementer(new RunIdIncrementer())
        .start(loadFromOriginStep)
        .next(retryFailuresFromOriginStep)
        .listener(bridgeBatchIntegrationJobContextProvider)
        .listener(jobListener)
        .build();

构造函数依赖于其他 bean,包括 jackson 对象映射器和 JPA 存储库。我遇到了一些问题:

  1. Spring没有实例化构造函数,因此我要绑定的实例变量不存在

如果我从组件中删除 @JobScope,Spring 会构造组件实例。

标签: springspring-batch

解决方案


我看不到@JobContext您的代码在哪里使用,根据您的要求,您不需要它。

如果您想使用侦听器在作业执行上下文中加载一些数据,您可以beforeJob使用jobExecution.getExecutionContext().put("key", "value");.

也就是说,不建议在执行上下文中加载大量数据,因为它在步骤之间保持不变。

因此,除非您在执行上下文中加载少量数据,否则您需要找到另一种方法(例如使用单独的缓存,请参阅Spring Batch With Annotation and Caching)。


推荐阅读