Spring Batch-为什么在Web上下文而不是Job上下文中创建/执行Job Step bean?

时间:2019-02-12 19:55:24

标签: java spring spring-batch

在我的Web应用程序中,我启动并管理了几十个用于长期运行的spring-batch进程。

在我看来,spring-batch是在Web应用程序上下文中而不是在作业上下文中构建作业,从而导致非提示性错误“没有为作用域名称“步骤”注册任何作用域”。

有什么想法我想念的吗?

  • Java版本:1.8
  • 春季版本:5.1.3.RELEASE
  • spring-batch版本:4.1.1。发布
  • Tomcat版本:8.0

发布问题以来进行的更改/更新:

  1. 实施了作业注册表以封装作业,并更新了作业启动以使用作业注册表-无需更改
  2. 已尝试TARGET_CLASS和DEFAULT代理模式,并且功能相同-不变
  3. 根据Mahmoud Ben Hassine的回答,在StepScope bean声明中添加了“ <属性名称=“ proxyTargetClass”值=“ true” />“-不变

登录...

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]

春季批量作业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()内部,这是spring上下文的内容:

{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,并在父类路径中找到一个,恰好是通过网络配置扫描的一个。 Bean被声明为“ Step”。 webconfig当然没有步骤范围。

错误消息既正确(英语:Yo dude,此bean被声明为step作用域,但上下文中没有一个),并且具有误导性(我在工作中看到有step作用域,它正在执行中)步骤范围,其他Bean在步骤范围内操作,WTH ??? !!!!!)。

我希望看到更多从Spring返回的智能错误消息。一次追寻完全准确但完全隐藏问题根源的错误消息很容易一次浪费几天的时间。

1 个答案:

答案 0 :(得分:2)

您在您的阅读器范围内使用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)时没有被代理的bean有一个未解决的问题,但是我不确定您是否在这里遇到了这个问题。

以下是我会尝试的几件事:

  • 在删除<context:component-scan base-package="com.xxxx.*" />MyExampleReader后,不要使用scope="step"并使用带有@Component的XML声明@Scope(value = "step", proxyMode = ScopedProxyMode.TARGET_CLASS)
  • 我不理解“启动工作的代码”部分,与在Web应用程序中启动工作的典型方式相比,这看起来并不熟悉。如果您的Spring Batch应用程序上下文是Web应用程序上下文的子上下文,则在批处理上下文中定义的所有bean在您的控制器中都是可见的,您可以注入JobLauncherJob来启动。您可以在此处找到示例:https://docs.spring.io/spring-batch/4.1.x/reference/html/job.html#runningJobsFromWebContainer

可以在这里找到类似的问题:Spring batch scope issue while using spring boot

希望这会有所帮助。