Spring Batch - почему bean-компонент Step создается / выполняется в веб-контексте, а не в контексте Job? - PullRequest
0 голосов
/ 12 февраля 2019

Внутри моего веб-приложения я запускаю и управляю несколькими десятками подпружиненных процессов для длительных операций.

Мне кажется, что Spring-Batch построил задание в контексте веб-приложения, а не в контексте задания, что приводит к неинформативной ошибке "Нет области действия для имени области действия" step "".

Есть идеи, что мне не хватает?

  • Версия Java: 1.8
  • Версия Spring: 5.1.3.RELEASE
  • Версия с подпружиненной версией: 4.1.1.RELEASE
  • Версия Tomcat: 8.0

Изменения / обновления, сделанные после публикации вопроса:

  1. Реализован реестр заданий для инкапсуляции заданийи обновил запуск задания для использования реестра заданий - без изменений
  2. Оба режима TARGET_CLASS и DEFAULT proxy были опробованы и работают одинаково - без изменений
  3. Added ""к объявлению бина StepScope в ответ Махмуда Бена Хассина - без изменений

Журнал ...

taskExecutor-1 2019-02-12 13:31:32,836 ERROR o.s.b.c.s.AbstractStep - Encountered an error executing step step0002-init-prepareGraphDatastore in job hierarchy-analyser
java.lang.IllegalStateException: No Scope registered for scope name 'step'
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:350) ~[spring-beans-5.1.3.RELEASE.jar:5.1.3.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) ~[spring-beans-5.1.3.RELEASE.jar:5.1.3.RELEASE]
at org.springframework.aop.target.SimpleBeanTargetSource.getTarget(SimpleBeanTargetSource.java:35) ~[spring-aop-5.1.3.RELEASE.jar:5.1.3.RELEASE]
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:672) ~[spring-aop-5.1.3.RELEASE.jar:5.1.3.RELEASE]
at com.xxxx.MyExampleReader$$EnhancerBySpringCGLIB$$b0c58048.beforeStep(<generated>) ~[relationship-analyzer-tool-BASELINE.jar:na]
at org.springframework.batch.core.listener.CompositeStepExecutionListener.beforeStep(CompositeStepExecutionListener.java:77) ~[spring-batch-core-4.1.1.RELEASE.jar:4.1.1.RELEASE]
at org.springframework.batch.core.step.AbstractStep.execute(AbstractStep.java:199) ~[spring-batch-core-4.1.1.RELEASE.jar:4.1.1.RELEASE]
at org.springframework.batch.core.job.SimpleStepHandler.handleStep(SimpleStepHandler.java:148) [spring-batch-core-4.1.1.RELEASE.jar:4.1.1.RELEASE]
at org.springframework.batch.core.job.flow.JobFlowExecutor.executeStep(JobFlowExecutor.java:68) [spring-batch-core-4.1.1.RELEASE.jar:4.1.1.RELEASE]
at org.springframework.batch.core.job.flow.support.state.StepState.handle(StepState.java:67) [spring-batch-core-4.1.1.RELEASE.jar:4.1.1.RELEASE]
at org.springframework.batch.core.job.flow.support.SimpleFlow.resume(SimpleFlow.java:169) [spring-batch-core-4.1.1.RELEASE.jar:4.1.1.RELEASE]
at org.springframework.batch.core.job.flow.support.SimpleFlow.start(SimpleFlow.java:144) [spring-batch-core-4.1.1.RELEASE.jar:4.1.1.RELEASE]
at org.springframework.batch.core.job.flow.FlowJob.doExecute(FlowJob.java:136) [spring-batch-core-4.1.1.RELEASE.jar:4.1.1.RELEASE]
at org.springframework.batch.core.job.AbstractJob.execute(AbstractJob.java:313) [spring-batch-core-4.1.1.RELEASE.jar:4.1.1.RELEASE]
at org.springframework.batch.core.launch.support.SimpleJobLauncher$1.run(SimpleJobLauncher.java:144) [spring-batch-core-4.1.1.RELEASE.jar:4.1.1.RELEASE]
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [na:1.8.0_162]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [na:1.8.0_162]
at java.lang.Thread.run(Thread.java:748) [na:1.8.0_162]

Spring-batch job XML ...

<?xml version="1.0" encoding="UTF-8"?>
<beans default-lazy-init="false"
xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:batch="http://www.springframework.org/schema/batch"
xsi:schemaLocation="
http://www.springframework.org/schema/batch http://www.springframework.org/schema/batch/spring-batch.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.1.xsd">
<description>Hierarchy Analyzer</description>
<context:component-scan
    base-package="com.xxxx.*" />

<bean class="org.springframework.batch.core.scope.JobScope" />
<bean class="org.springframework.batch.core.scope.StepScope" />

<batch:job id="hierarchy-analyser">
    <batch:listeners>
        <batch:listener
            ref="someJobListeners" />
    </batch:listeners>

    <batch:step id="step0002-init-long-running-process"
        allow-start-if-complete="true">

        <batch:tasklet
            transaction-manager="jtaTransactionManager" start-limit="100">
            <batch:chunk reader="myExampleReader"
                writer="myExampleWriter" commit-interval="1" />
        </batch:tasklet>
        <batch:fail on="FAILED" />
        <batch:next on="*"
            to="step0002-1-more-stuff" />
        <batch:listeners>
            <batch:listener ref="myExampleReader" />
            <batch:listener ref="myExampleWriter" />
        </batch:listeners>
    </batch:step>

</batch:job>


</beans>

MyExampleReader ...

@Component
@Scope(value = "step", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class MyExampleReader
    implements ItemReader<TableMetadata>, 
    StepExecutionListener { ... }

Код, который запускает задание ...

    private ResultWrapper<Job> setupJobById(Resource r) throws Exception {
    ResultWrapper<Job> result = new ResultWrapper<Job>();
    try {
        Resource[] res = new Resource[] { r };

        ClasspathXmlApplicationContextsFactoryBean b = new ClasspathXmlApplicationContextsFactoryBean();
        b.setApplicationContext(applicationContext);
        b.setResources(res);

        ApplicationContextJobFactory factory = new ApplicationContextJobFactory(
                r.getFilename().substring(0, r.getFilename().lastIndexOf('.')), b.getObject()[0]);

        result.succeed(factory.createJob());
    } catch (Exception ex) {
        logger.error(ex.getMessage(), ex);
        result.fail(null, ex.getMessage(), ex);
    }

    return result;
}

Внутри AbstractBeanFactory.doGetbean (), это содержимое контекста пружины:

{request=org.springframework.web.context.request.RequestScope@3e707e1c, session=org.springframework.web.context.request.SessionScope@375463f}

Обновление: пояснения к ответу

Был ряд проблем с кодом, которые способствовали этому.

  1. Класс, который я искал, отсутствовал ни в одном из путей сканирования контекста в задании, но он был в пути глобального сканирования контекста. Санитарная обработка кода для открытых форумов скрывала это от любых респондентов.

  2. Исходный код не следовал согласованной практике для прокси-режимов во всем приложении. Не было никаких причин не следовать согласованным рекомендациям во всем.

  3. Неправильное использование внутреннего реестра реестра привелок общей "странности".

Что касается вопроса «почему» в исходном вопросе, ответ заключается в том, что Spring оценивает область действия во время сканирования контекста для каждого контекста.Если bean-компонент не загружен с контекстом задания (скажем, из-за того, что в файле job.xml отсутствует один из требуемых путей к классам), то при отложенной загрузке Spring пытается загрузить bean-компонент и находит его в родительском classpath, который оказываетсятот, отсканированный веб-конфигурации.Боб объявлен "Шагом".У webconfig, конечно, нет области действия шага.

Сообщение об ошибке является правильным (английский: чувак, этот компонент объявлен как область действия шага, но его нет в контексте), и вводит в заблуждение (я вижув задании, что есть область действия шага, она выполняется в области действия шага, другие компоненты работают в области действия шага, WTH ??? !!!!!).

Я хотел бы видеть более интеллектуальногосообщения об ошибках возвращены из Spring.Легко терять дни, гоняясь за сообщениями об ошибках, которые абсолютно точны, но скрывают истинный источник проблемы.

1 Ответ

0 голосов
/ 13 февраля 2019

Вы используете proxyMode = ScopedProxyMode.TARGET_CLASS в области действия вашего считывателя, поэтому вам нужно объявить область действия с помощью:

<beans:bean class="org.springframework.batch.core.scope.StepScope">
   <beans:property name="proxyTargetClass" value="true" />
</beans:bean>

РЕДАКТИРОВАТЬ: я знаю, что существует открытая проблема о бобах, которые неполучить прокси при смешении конфигурации Java и конфигурации XML (см. BATCH-2351 ), но я не уверен, что вы решаете эту проблему здесь.

Вот пара вещей, которые я хотел быпопробуйте:

  • Не используйте <context:component-scan base-package="com.xxxx.*" /> и объявите MyExampleReader, используя XML с scope="step" после удаления @Component и @Scope(value = "step", proxyMode = ScopedProxyMode.TARGET_CLASS)
  • Я не понимаючасть «Код, запускающий работу», и это выглядит незнакомым по сравнению с обычным способом запуска работ в веб-приложении.Если ваш контекст приложения Spring Batch является дочерним контекстом контекста вашего веб-приложения, то все компоненты, определенные в контексте пакета, будут видны на вашем контроллере, и вы можете ввести JobLauncher и Job для запуска.Вы можете найти пример здесь: https://docs.spring.io/spring-batch/4.1.x/reference/html/job.html#runningJobsFromWebContainer

Аналогичный вопрос можно найти здесь: Проблема с областью пакета Spring при использовании пружинной загрузки

Надеюсь, это поможет.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...