在Spring Batch 3.0中,我试图在分区和多线程步骤(使用任务:executor bean配置)中使用Bean的新作业范围功能,并且在这两种情况下我都得到异常
Caused by: java.lang.IllegalStateException: No context holder available for job scope
at org.springframework.batch.core.scope.JobScope.getContext(JobScope.java:153)
at org.springframework.batch.core.scope.JobScope.get(JobScope.java:92)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:338)
但如果我使豆步范围可以正常工作。
我注意到JobSynchronizationManager上的注释
N.B。每个{@link Job}实施都有责任确保这一点 {@link JobContext}可用于可能涉及的每个线程 作业执行,包括池中的工作线程。
所以我想知道我是否需要做一些事情来设置它,或者它是否在作业范围实现中它没有正确设置工作线程的错误?
StepSynchronizationManager有一个类似的注释 - 但在这种情况下,显然在步骤中正确设置了线程。
重现问题的示例代码:
TestItemReader
package test;
import java.util.ArrayList;
import java.util.List;
import org.springframework.batch.item.ItemReader;
import org.springframework.batch.item.NonTransientResourceException;
import org.springframework.batch.item.ParseException;
import org.springframework.batch.item.UnexpectedInputException;
import org.springframework.beans.factory.InitializingBean;
public class TestItemReader implements ItemReader<Integer>, InitializingBean {
private List<Integer> items;
@Override
public synchronized Integer read() throws Exception, UnexpectedInputException,
ParseException, NonTransientResourceException {
if (items.size() > 0) {
return items.remove(0);
}
return null;
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("Initialising reader");
items = new ArrayList<Integer>();
for (int i=0;i<100;i++) items.add(i);
}
}
TestItemWriter
package test;
import java.util.List;
import org.springframework.batch.item.ItemWriter;
public class TestItemWriter implements ItemWriter<Integer> {
@Override
public void write(List<? extends Integer> items) throws Exception {
for (int i : items) {
System.out.println(Thread.currentThread().getName() + " Writing " + i);
}
}
}
测试在职context.xml中
<?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:batch="http://www.springframework.org/schema/batch"
xmlns:jdbc="http://www.springframework.org/schema/jdbc"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:task="http://www.springframework.org/schema/task"
xsi:schemaLocation="http://www.springframework.org/schema/batch http://www.springframework.org/schema/batch/spring-batch.xsd
http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd">
<job id="job" restartable="true" xmlns="http://www.springframework.org/schema/batch">
<step id="index">
<tasklet task-executor="executor">
<chunk reader="itemReader" writer="itemWriter" commit-interval="5"/>
</tasklet>
</step>
</job>
<bean id="itemReader" class="test.TestItemReader" scope="job"/>
<bean id="itemWriter" class="test.TestItemWriter"/>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<bean class="org.springframework.batch.test.JobLauncherTestUtils">
<property name="job" ref="job"/>
</bean>
<bean id="jobLauncher" class="org.springframework.batch.core.launch.support.SimpleJobLauncher">
<property name="jobRepository" ref="jobRepository" />
</bean>
<batch:job-repository id="jobRepository"/>
<jdbc:embedded-database id="dataSource" type="HSQL">
<jdbc:script location="classpath:/org/springframework/batch/core/schema-hsqldb.sql"/>
</jdbc:embedded-database>
<task:executor id="executor" queue-capacity="0" pool-size="5"/>
</beans>
JobTest
package test;
import java.util.Collection;
import org.apache.log4j.BasicConfigurator;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.junit.BeforeClass;
import org.junit.Test;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.StepExecution;
import org.springframework.batch.test.JobLauncherTestUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.junit4.AbstractJUnit4SpringContextTests;
import org.springframework.test.context.ContextConfiguration;
@ContextConfiguration(locations={"test-job-context.xml"})
public class JobTest extends AbstractJUnit4SpringContextTests {
@Autowired
private JobLauncherTestUtils jobLauncherTestUtils;
@BeforeClass
public static void beforeClassSetup() {
BasicConfigurator.configure();
Logger.getRootLogger().setLevel(Level.WARN);
Logger.getLogger("org.springframework.batch.core.scope.JobScope").setLevel(Level.DEBUG);
Logger.getLogger("org.springframework.batch.core.scope.StepScope").setLevel(Level.DEBUG);
}
@Test
public void testJobLaunch() throws Exception {
JobExecution execution = jobLauncherTestUtils.launchJob();
System.out.println("After execution " + execution);
Collection<StepExecution> stepExecutions = execution.getStepExecutions();
for (StepExecution stepExecution : stepExecutions) {
System.out.println("StepExecution " + stepExecution);
}
}
}
运行上面的JUnit测试将重现该问题。如果您将阅读器的范围更改为步骤,或者删除范围,则测试将正常完成。
答案 0 :(得分:2)
这是因为当前执行(在这种情况下为JobExecution
)存储在ThreadLocal
中(请参阅org.springframework.batch.core.scope.context.SynchronizationManagerSupport
)。话虽如此,为此添加多线程支持似乎并不合理。随意为它创建一个Jira问题(如果你倾向于拉动请求)。