You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Batch自定义ItemReader获取JobParameter失败致项目启动异常

Spring Batch SpEL获取JobParameter失败:EL1008E错误解决

问题说明

尝试通过SpEL表达式#{jobParameters['filePath']}将JobParameter中的参数注入自定义ItemReader,项目启动失败,报错:
EL1008E: Property or field 'jobParameters' cannot be found on object of type 'org.springframework.beans.factory.config.BeanExpressionContext' - maybe not public or not valid?

错误堆栈

Error starting ApplicationContext. To display the condition evaluation report re-run your application with 'debug' enabled.
2025-04-16 16:12:17.568 SMI-NEW LOS ERROR  --- [main]   master-service o.s.boot.SpringApplication.reportFailure  :  Application run failed
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'scopedTarget.debtorReader' defined in file [C:\Users\Indra\IST\SMI\master-service\target\classes\id\co\ist\los\master\service\migration\reader\DebtorReader.class]: Unsatisfied dependency expressed through constructor parameter 0: Expression parsing failed
    at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:801)
    at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:240)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1352)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1189)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:560)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:520)
    at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:973)
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:950)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:616)
    at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146)
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:738)
    at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:440)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:316)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1306)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1295)
    at id.co.ist.los.MasterServiceApplication.main(MasterServiceApplication.java:17)
Caused by: org.springframework.beans.factory.BeanExpressionException: Expression parsing failed
    at org.springframework.context.expression.StandardBeanExpressionResolver.evaluate(StandardBeanExpressionResolver.java:170)
    at org.springframework.beans.factory.support.AbstractBeanFactory.evaluateBeanDefinitionString(AbstractBeanFactory.java:1591)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1361)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1337)
    at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:910)
    at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:788)
    ... 19 common frames omitted
Caused by: org.springframework.expression.spel.SpelEvaluationException: EL1008E: Property or field 'jobParameters' cannot be found on object of type 'org.springframework.beans.factory.config.BeanExpressionContext' - maybe not public or not valid?
    at org.springframework.expression.spel.ast.PropertyOrFieldReference.readProperty(PropertyOrFieldReference.java:228)
    at org.springframework.expression.spel.ast.PropertyOrFieldReference.getValueInternal(PropertyOrFieldReference.java:111)
    at org.springframework.expression.spel.ast.PropertyOrFieldReference.getValueInternal(PropertyOrFieldReference.java:99)
    at org.springframework.expression.spel.ast.CompoundExpression.getValueRef(CompoundExpression.java:61)
    at org.springframework.expression.spel.ast.CompoundExpression.getValueInternal(CompoundExpression.java:97)
    at org.springframework.expression.spel.ast.SpelNodeImpl.getValue(SpelNodeImpl.java:114)
    at org.springframework.expression.spel.standard.SpelExpression.getValue(SpelExpression.java:273)
    at org.springframework.context.expression.StandardBeanExpressionResolver.evaluate(StandardBeanExpressionResolver.java:167)
    ... 24 common frames omitted

相关代码

Post API代码

@PostMapping(value = "/upload-and-trigger", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public ResponseEntity<String> uploadAndTriggerJob(
        @RequestParam("file") MultipartFile file) {
    try {
        // 保存文件到本地
        String filePath = saveFileToLocal(file);

        // 构建Job参数
        JobParametersBuilder jobParameters = new JobParametersBuilder();
        jobParameters.addString("filePath", filePath);
        jobParameters.addLong("time", System.currentTimeMillis());

        // 启动Job
        JobExecution jobExecution = jobLauncher.run(job, jobParameters.toJobParameters());
        return ResponseEntity.ok("Job triggered successfully with status: " + jobExecution.getStatus());
    } catch (Exception e) {
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                .body("Failed to trigger job: " + e.getMessage());
    }
}

Batch配置代码

@Configuration
@EnableBatchProcessing
public class MasterDebiturBatchConfig {

    @Value("${edc.etl.transaction-sales.chunksize:1000}")
    private Integer defaultChunkSize;

    @Autowired
    private JobRepository jobRepository;
    @Autowired
    private PlatformTransactionManager transactionManager;

    private String JOB_NAME = "etl-migration-job";
    private String EXECUTION_STEP_0 = "execution-debitur-write";

    @Bean
    public Job myJob(Step myStep) {
        return new JobBuilder(JOB_NAME, jobRepository)
                .incrementer(new RunIdIncrementer())
                .start(myStep)
                .build();
    }

    @Bean
    public Step myStep(DebtorReader debtorReader,
                       LoggingDebtorProcessor loggingDebtorProcessor,
                       DatabaseDebtorWriter myItemWriter) {
        return new StepBuilder(EXECUTION_STEP_0, jobRepository)
                .<ReadDebtorDto, MDebtor>chunk(defaultChunkSize, transactionManager)
                .reader(debtorReader)
                .processor(loggingDebtorProcessor)
                .writer(myItemWriter)
                .build();
    }
}

自定义ItemReader代码

@Component
@StepScope
@Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)
@Slf4j
public class DebtorReader implements ItemReader<ReadDebtorDto> {

    private static final String WILL_BE_INJECTED = null;
    private final String filePath;

    public DebtorReader(@Value("#{jobParameters['filePath']}") String filePath) {
        this.filePath = filePath;
    }

    @Override
    public ReadDebtorDto read() throws Exception {
        PoiItemReader<ReadDebtorDto> reader = new PoiItemReader<>();
        reader.setResource(new ClassPathResource(filePath));
        reader.setRowMapper(new DebtorRowMapper());
        reader.setStrict(false);

        var readDebtorDto = new ReadDebtorDto();
        readDebtorDto.setName(reader.read().getName());
        readDebtorDto.setNpwp(reader.read().getNpwp());
        readDebtorDto.setDebtorType(reader.read().getDebtorType());
        readDebtorDto.setDebtorRating(reader.read().getDebtorRating());
        readDebtorDto.setCustomerId(reader.read().getCustomerId());

        return readDebtorDto;
    }
}

解决方案

1. 调整参数注入方式

构造器注入会在Spring容器启动时尝试解析SpEL表达式,但此时JobParameter还未传入(Job通过API触发时才生成参数),导致表达式解析失败。改为字段注入+无参构造器,延迟表达式解析到Step执行阶段:

@Component
@StepScope
@Slf4j
public class DebtorReader implements ItemReader<ReadDebtorDto> {

    @Value("#{jobParameters['filePath']}")
    private String filePath;

    // 无参构造器
    public DebtorReader() {
    }

    // ... 后续read方法
}

2. 修正PoiItemReader初始化逻辑

原代码在read()方法中每次创建新的PoiItemReader,会导致重复加载文件、每次读取都从头开始,严重影响性能和正确性。改为初始化时配置:

@Component
@StepScope
@Slf4j
public class DebtorReader implements ItemReader<ReadDebtorDto> {

    @Value("#{jobParameters['filePath']}")
    private String filePath;
    private PoiItemReader<ReadDebtorDto> poiReader;

    @PostConstruct
    public void init() throws Exception {
        poiReader = new PoiItemReader<>();
        // 注意:如果是本地磁盘路径,用FileSystemResource而非ClassPathResource
        poiReader.setResource(new FileSystemResource(filePath));
        poiReader.setRowMapper(new DebtorRowMapper());
        poiReader.setStrict(false);
        poiReader.afterPropertiesSet(); // 初始化reader
    }

    @Override
    public ReadDebtorDto read() throws Exception {
        // 直接复用初始化好的poiReader读取数据
        return poiReader.read();
    }
}

3. 修正资源类型

若filePath是本地磁盘路径(如saveFileToLocal保存到磁盘的路径),必须使用FileSystemResource,ClassPathResource仅用于读取类路径(如resources目录下)的文件。

关键原因解析

@StepScope标记的Bean会在Step执行时才实例化,构造器注入会导致Spring在容器启动阶段就尝试解析SpEL表达式,但此时jobParameters仅存在于Job执行上下文,容器启动阶段无法访问,因此抛出找不到属性的错误。改用字段注入后,表达式解析会延迟到Bean实例化(即Step执行时),此时jobParameters已存在,可正常获取参数。

内容的提问来源于stack exchange,提问作者iyp.prog

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 09:50:55