2

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

    }
}

私のsampleJob定義:

<?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>

リーダー、ライター、およびプロセッサは、Spring によってインスタンス化され、ジョブ実行で適切に使用されます。リスナーをジョブ定義に追加すると、動作しなくなりました。

コードをデバッグしたところ、dataListingStepListener Bean が Spring コンテキストで初期化されていることがわかりました。<listener ref="dataListingStepListener"/>リスナーへの参照 ( ) が Spring Bean として認識されず、Spring が代わりに「dataListingStepListener」という名前のクラスをロードしようとしている理由がわかりません。

私の設定は大丈夫ですか?私は何か間違ったことをしていますか?

4

1 に答える 1

1

Gist を確認した後、いくつかの注意事項があります。

ジョブ固有の Bean は子コンテキストにある必要があります
Spring Batch の JSR-352 コンテキスト管理は、親子関係を介して機能します。親コンテキストは、baseContext.xml によって定義されるか、システム プロパティを介して行ったようにオーバーライドできますJSR-352-BASE-CONTEXT。このコンテキストは、インフラストラクチャ Bean にのみ使用されることを意図しており、JSR-352 ポスト プロセッサによってプロパティ インジェクションなどのために処理されることはありません。「リスナーをジョブ定義に追加したら動かなくなった」とおっしゃいました。よく見ると、ジョブの実行中にプロパティは挿入されませんでしたSimpleBatchlet(メッセージは でしたnull)。

この理由は、複数のジョブを実行したい場合、複数のJobRepositoryインスタンスや複数の接続プールなどを作成する必要がないためです。そのため、親コンテキストを 1 回だけブートストラップします。ブートストラップは 1 回だけなので、その時点で注入するジョブ プロパティなどの値はありません (ジョブごとに変わる可能性があります)。

したがって、すべてのジョブ固有の Bean は子コンテキストにある必要があります。

JSR-352 の子コンテキストの定義
JSR-352 には、Bean の作成を処理する 3 つの異なる方法があります。

  1. インライン -ジョブ XML のアーティファクト定義の属性に完全修飾クラス名をref指定すると、引数なしのコンストラクターを使用して作成されます。
  2. batch.xml - batch.xml ファイルは、クラス名のエイリアスを提供することにより、上記の概念を単純化する方法を提供します。
  3. 実装固有の DI (この場合は Spring DI) - Spring Batch の JSR-352 の実装は、Spring の DI 機能を使用して Bean を作成する機能を提供します。

これで、Bean インスタンスがどのように作成されるかを制御できるようになります。デフォルトでは、Spring では、Bean は起動時に作成されるシングルトンです。ただし、Spring Batch はスコープ (stepまたはjob) を提供して、プロパティの遅延バインディングを可能にします。プロパティ注入が必要な場合は、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を作成して、上記のコンテキストに関する考慮事項をより適切に文書化しました。

于 2015-05-29T16:59:00.450 に答える