java - 创建名为 'batchPropertyPostProcessor' 的 bean 时,批处理作业初始化因错误而失败

标签 java spring-batch jsr352

我正在尝试使用 JSR-352 API 和 Spring Batch 3.0.4 作为实现来实现示例批处理应用程序。

在创建名为“batchPropertyPostProcessor”的 bean 时,批处理作业执行在初始化阶段因错误而失败:

Exception in thread "main" javax.batch.operations.JobStartException: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'batchPropertyPostProcessor': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire method: public void org.springframework.batch.core.jsr.launch.support.BatchPropertyBeanPostProcessor.setBatchPropertyContext(org.springframework.batch.core.jsr.configuration.support.BatchPropertyContext); nested exception is org.springframework.beans.factory.CannotLoadBeanClassException: Cannot find class [dataListingStepListener] for bean with name 'scopedTarget.dataListingStepListener' defined in null; nested exception is java.lang.ClassNotFoundException: dataListingStepListener
    at org.springframework.batch.core.jsr.launch.JsrJobOperator.start(JsrJobOperator.java:637)
    at x98.BatchRunner.main(BatchRunner.java:18)
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'batchPropertyPostProcessor': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire method: public void org.springframework.batch.core.jsr.launch.support.BatchPropertyBeanPostProcessor.setBatchPropertyContext(org.springframework.batch.core.jsr.configuration.support.BatchPropertyContext); nested exception is org.springframework.beans.factory.CannotLoadBeanClassException: Cannot find class [dataListingStepListener] for bean with name 'scopedTarget.dataListingStepListener' defined in null; nested exception is java.lang.ClassNotFoundException: dataListingStepListener
    at org.springframework.batch.core.jsr.configuration.support.SpringAutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(SpringAutowiredAnnotationBeanPostProcessor.java:262)
    at org.springframework.batch.core.jsr.configuration.support.JsrAutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(JsrAutowiredAnnotationBeanPostProcessor.java:30)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1185)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:537)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:475)
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:302)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:228)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:298)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:198)
    at org.springframework.context.support.PostProcessorRegistrationDelegate.registerBeanPostProcessors(PostProcessorRegistrationDelegate.java:232)
    at org.springframework.context.support.AbstractApplicationContext.registerBeanPostProcessors(AbstractApplicationContext.java:618)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:467)
    at org.springframework.batch.core.jsr.launch.JsrJobOperator.start(JsrJobOperator.java:635)
    ... 1 more
Caused by: org.springframework.beans.factory.BeanCreationException: Could not autowire method: public void org.springframework.batch.core.jsr.launch.support.BatchPropertyBeanPostProcessor.setBatchPropertyContext(org.springframework.batch.core.jsr.configuration.support.BatchPropertyContext); nested exception is org.springframework.beans.factory.CannotLoadBeanClassException: Cannot find class [dataListingStepListener] for bean with name 'scopedTarget.dataListingStepListener' defined in null; nested exception is java.lang.ClassNotFoundException: dataListingStepListener
    at org.springframework.batch.core.jsr.configuration.support.SpringAutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.inject(SpringAutowiredAnnotationBeanPostProcessor.java:575)
    at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:87)
    at org.springframework.batch.core.jsr.configuration.support.SpringAutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(SpringAutowiredAnnotationBeanPostProcessor.java:259)
    ... 13 more
Caused by: org.springframework.beans.factory.CannotLoadBeanClassException: Cannot find class [dataListingStepListener] for bean with name 'scopedTarget.dataListingStepListener' defined in null; nested exception is java.lang.ClassNotFoundException: dataListingStepListener
    at org.springframework.beans.factory.support.AbstractBeanFactory.resolveBeanClass(AbstractBeanFactory.java:1325)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.predictBeanType(AbstractAutowireCapableBeanFactory.java:594)
    at org.springframework.beans.factory.support.AbstractBeanFactory.isTypeMatch(AbstractBeanFactory.java:526)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doGetBeanNamesForType(DefaultListableBeanFactory.java:387)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanNamesForType(DefaultListableBeanFactory.java:354)
    at org.springframework.beans.factory.BeanFactoryUtils.beanNamesForTypeIncludingAncestors(BeanFactoryUtils.java:187)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.findAutowireCandidates(DefaultListableBeanFactory.java:1002)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:960)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:858)
    at org.springframework.batch.core.jsr.configuration.support.SpringAutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.inject(SpringAutowiredAnnotationBeanPostProcessor.java:532)
    ... 15 more
Caused by: java.lang.ClassNotFoundException: dataListingStepListener
    at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
    at org.springframework.util.ClassUtils.forName(ClassUtils.java:247)
    at org.springframework.beans.factory.support.AbstractBeanDefinition.resolveBeanClass(AbstractBeanDefinition.java:395)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doResolveBeanClass(AbstractBeanFactory.java:1346)
    at org.springframework.beans.factory.support.AbstractBeanFactory.resolveBeanClass(AbstractBeanFactory.java:1317)
    ... 24 more

我的批处理运行程序:

public class BatchRunner {

    public static void main(String[] args) {

        System.setProperty("JSR-352-BASE-CONTEXT", "x98_batch_local.xml");

        Properties jobParameters = new Properties();
        jobParameters.put("message", "Hello!");

        JobOperator jobOperator = BatchRuntime.getJobOperator();
        jobOperator.start("x98SampleJob", jobParameters);

    }
}

我的示例作业定义:

<?xml version="1.0" encoding="UTF-8"?>
<job id="x98SampleJob" xmlns="http://xmlns.jcp.org/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/jobXML_1_0.xsd"
    version="1.0">

    <step id="simple" next="dataListing">
        <batchlet ref="simpleBatchlet">
            <properties>
                <property name="message" value="#{jobParameters['message']}" />
            </properties>
        </batchlet>
    </step>

    <step id="dataListing">
        <listeners>
            <listener ref="dataListingStepListener"/>
        </listeners>
        <chunk item-count="3">
            <reader ref="dataListingItemReader" />
            <processor ref="dataListingItemProcessor"/>
            <writer ref="dataListingItemWriter"/>
        </chunk>
    </step>
</job>  

我的读取器、写入器、处理器和监听器的 Spring bean 配置

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jee="http://www.springframework.org/schema/jee"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.1.xsd">

    <import resource="classpath*:x98_services.xml"/>

    <bean name="simpleBatchlet" class="x98.batch.SimpleBatchlet"/>

    <bean name="dataListingItemProcessor" class="x98.batch.DataListingItemProcessor"/>

    <bean name="dataListingItemReader" class="x98.batch.DataListingItemReader">
        <property name="tx98DatasService" ref="tx98DatasService"/>
        <property name="tx98StructureService" ref="tx98StructureService"/>
    </bean>

    <bean name="dataListingItemWriter" class="x98.batch.DataListingItemWriter"/>

    <bean name="dataListingStepListener" class="x98.batch.DataListingStepListener">
        <property name="tx98StatusService" ref="tx98StatusService"/>
    </bean>
</beans>

Reader、Writer 和 Processor 由 Spring 实例化并在作业运行中正确使用。当我将监听器添加到作业定义时,它停止工作。

我调试了代码,发现 dataListingStepListener bean 已在 Spring 上下文中初始化。我不明白为什么对监听器( <listener ref="dataListingStepListener"/> )的引用不被识别为 Spring bean 并且 Spring 正在尝试加载名为“dataListingStepListener”的类。

我的配置可以吗?我做错了什么吗?

最佳答案

查看要点后,有一些注意事项:

作业特定的 Bean 必须位于子上下文中
Spring Batch 的 JSR-352 上下文管理通过父/子关系进行工作。父上下文由 baseContext.xml 定义,或者可以像您通过系统属性 JSR-352-BASE-CONTEXT 所做的那样进行覆盖。 。该上下文仅用于基础设施 bean,并且不会由 JSR-352 后处理器处理以进行属性注入(inject)等操作。你说“当我将监听器添加到作业定义中时,它就停止工作了。”如果你仔细观察,当工作运行时,你的属性(property)并没有被注入(inject) SimpleBatchlet (消息为 null )。

这样做的原因是,如果您想运行多个作业,则不需要创建多个 JobRepository实例、多个连接池等,因此我们只引导父上下文一次。由于我们只引导一次,因此当时我们没有要注入(inject)的作业属性之类的值(并且它们可能会因作业而异)。

因此,所有特定于作业的 bean 都应该位于子上下文中。

为 JSR-352 定义子上下文
JSR-352 提供了三种不同的方法来处理 bean 创建:

  1. 内联 -ref 中提供完全限定的类名作业 XML 中工件定义的属性,它将使用无参数构造函数创建。
  2. batch.xml -batch.xml 文件提供了一种通过提供类名别名来简化上述概念的方法。
  3. 特定于实现的 DI(本例中为 Spring DI)-Spring Batch 的 JSR-352 实现提供了使用 Spring 的 DI 工具来创建 Bean 的能力。

这样,我们就可以将如何创建 Bean 实例的控制权交给您。默认情况下,在 Spring 中,bean 是在启动时创建的单例。但是,Spring Batch 提供了范围( stepjob )以允许后期绑定(bind)属性。如果你想要属性注入(inject),bean必须定义为step范围( SimpleBatchlet 不是,这就是为什么消息是 null 第一次运行)。

那么,在定义作业上下文时,如何实现 Spring DI 和基于标准的 JSR-352 之间的关注点分离呢?简单的。将作业 XML 导入到 Spring XML 中,并将 Spring XML 名称传递给 JobOperator即将推出。在你的情况下,而不是:

public static void main(String[] args) {

    System.setProperty("JSR-352-BASE-CONTEXT", "x98_batch_local.xml");

    Properties jobParameters = new Properties();
    jobParameters.put("message", "Hello!");

    JobOperator jobOperator = BatchRuntime.getJobOperator();
    jobOperator.start("x98SampleJob", jobParameters);

}

将以下内容添加到 x98_batch_local.xml 中:

<import resource="classpath*:x98SampleJob.xml"/>

然后通过以下方式执行您的工作:

public static void main(String[] args) {

    Properties jobParameters = new Properties();
    jobParameters.put("message", "Hello!");

    JobOperator jobOperator = BatchRuntime.getJobOperator();
    jobOperator.start("x98_batch_local.xml", jobParameters);

}

上述调整并设置您的 SimpleBatchlet设置为步骤范围将使您的作业按预期运行。

注意:由于这个问题,我创建了问题 BATCH-2388更好地记录上面讨论的上下文考虑因素。

关于java - 创建名为 'batchPropertyPostProcessor' 的 bean 时,批处理作业初始化因错误而失败,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30483437/

相关文章:

java - Spring 启动批处理: How to run job with job parameters

java - 在 liberty 中动态配置 dev 和 prod 数据源以根据环境加载

java - 如何在 Cordova 项目中禁用三星智能手机(Android)的自动完成?

java - 如何从其他类调用变量和方法?

java - 在 JScrollPane 中滚动 JComponent

java - Spring 批量多线程

java - 避免在 Maven 依赖项中复制 OSGi 导入?

java - Spring batch FileItemWriter 没有在正确的路径创建文件

java-ee-7 - JEE 7 JSR 352 将数据从批处理传递到 block 步骤