我有独立的spring批处理工作。在JUNIT中运行时效果很好
@RunWith(SpringJUnit4ClassRunner.class) //@SpringApplicationConfiguration(classes = KPBootApplication.class) @ContextConfiguration(locations={"classpath:kp-sb.xml"}) public class KPBootApplicationTests { private final static Logger LOG=LoggerFactory.getLogger(KPBootApplicationTests.class); @Autowired ApplicationContext context; @Autowired private JobLauncher jobLauncher; @Autowired private Job job; @Test public void testJob() { final JobParameters jobParameters = new JobParametersBuilder() .toJobParameters(); JobExecution execution; try { execution = jobLauncher.run(job, jobParameters); final ExitStatus status = execution.getExitStatus(); if (ExitStatus.COMPLETED.getExitCode().equals(status.getExitCode())) { LOG.info("Job completed Yeaaaaaaaa!!!!!"); } else { final List<Throwable> exceptions = execution .getAllFailureExceptions(); for (final Throwable throwable : exceptions) { LOG.error(throwable.getMessage(), throwable); } } } catch (JobExecutionAlreadyRunningException e) { LOG.error(e.getMessage(), e); } catch (JobRestartException e) { LOG.error(e.getMessage(), e); } catch (JobInstanceAlreadyCompleteException e) { LOG.error(e.getMessage(), e); } catch (JobParametersInvalidException e) { LOG.error(e.getMessage(), e); } } }
和配置文件
<!-- Below code till Job Repo is commented out during spring-boot --> <context:property-placeholder properties-ref="kpProps" /> <util:properties id="kpProps"> <prop key="app.file.path"> D:/temp/kp1/all </prop> <prop key="app.max_thread_num"> 10 </prop> </util:properties> <!-- <bean id="jobLauncher" class="org.springframework.batch.core.launch.support.SimpleJobLauncher"> <property name="jobRepository" ref="jobRepository" /> </bean> <bean id="jobRepository" class="org.springframework.batch.core.repository.support.MapJobRepositoryFactoryBean"> <property name="transactionManager" ref="batchTransactionManager" /> </bean> --> <bean id="batchTransactionManager" class="org.springframework.batch.support.transaction.ResourcelessTransactionManager"> <property name="rollbackOnCommitFailure" value="false" /> </bean> <bean id="multiResourcePartitionerReq" class="org.springframework.batch.core.partition.support.MultiResourcePartitioner"> <property name="resources" value="file:${app.file.path}/kp_http_request*" /> </bean> <bean id="multiResourcePartitionerRes" class="org.springframework.batch.core.partition.support.MultiResourcePartitioner"> <property name="resources" value="file:${app.file.path}/kp_http_response*" /> </bean> <bean id="kpPool" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor" destroy-method="destroy"> <property name="maxPoolSize" value="${app.max_thread_num}" /> </bean> <bean id="idIncrementer1" class="org.springframework.batch.core.launch.support.RunIdIncrementer" /> <batch:job id="kpGenJob" incrementer="idIncrementer1" job-repository="jobRepository"> <batch:split id="splitStep" next="step4"> <batch:flow> <batch:step id="step2"> <batch:partition partitioner="multiResourcePartitionerReq" step="step2.slave"> <batch:handler task-executor="kpPool" /> </batch:partition> </batch:step> </batch:flow> <batch:flow> <batch:step id="step3"> <batch:partition partitioner="multiResourcePartitionerRes" step="step3.slave"> <batch:handler task-executor="kpPool" /> </batch:partition> </batch:step> </batch:flow> </batch:split> <batch:step id="step4"> <batch:tasklet transaction-manager="batchTransactionManager"> <ref bean="kptasklet" /> </batch:tasklet> </batch:step> </batch:job> <batch:step id="step2.slave"> <batch:tasklet transaction-manager="batchTransactionManager"> <batch:chunk reader="reqItemReader" writer="cvsFileItemWriter" commit-interval="10000" /> </batch:tasklet> </batch:step> <batch:step id="step3.slave"> <batch:tasklet transaction-manager="batchTransactionManager"> <batch:chunk reader="resItemReader" writer="cvsFileItemWriter" commit-interval="10000" /> </batch:tasklet> </batch:step> <bean id="reqItemReader" class="org.springframework.batch.item.file.FlatFileItemReader" scope="step"> <property name="resource" value="#{stepExecutionContext['fileName']}" /> <property name="lineMapper"> <bean class="org.springframework.batch.item.file.mapping.DefaultLineMapper"> <!-- split it --> <property name="lineTokenizer"> <bean class="org.springframework.batch.item.file.transform.DelimitedLineTokenizer"> <property name="includedFields" value="5,6,8,10,11"></property> <property name="names" value="f1,f2,f3,f4,f5" /> <property name="strict" value="false" /> </bean> </property> <property name="fieldSetMapper"> <bean class="org.springframework.batch.item.file.mapping.BeanWrapperFieldSetMapper"> <property name="prototypeBeanName" value="tblHttpData" /> </bean> </property> </bean> </property> <property name="linesToSkip" value="1"></property> </bean> <bean id="tblHttpData" class="com.kp.batch.batch.job.domain.TblHttpData" scope="prototype" /> <bean id="resItemReader" class="org.springframework.batch.item.file.FlatFileItemReader" scope="step"> <property name="resource" value="#{stepExecutionContext['fileName']}" /> <property name="lineMapper"> <bean class="org.springframework.batch.item.file.mapping.DefaultLineMapper"> <!-- split it --> <property name="lineTokenizer"> <bean class="org.springframework.batch.item.file.transform.DelimitedLineTokenizer"> <property name="includedFields" value="3,4"></property> <property name="names" value="f1,f2" /> <property name="strict" value="false" /> </bean> </property> <property name="fieldSetMapper"> <bean class="org.springframework.batch.item.file.mapping.BeanWrapperFieldSetMapper"> <property name="prototypeBeanName" value="tblHttpData" /> <property name="strict" value="true" /> </bean> </property> </bean> </property> <property name="linesToSkip" value="1"></property> </bean> <bean id="kptasklet" class="com.kp.batch.batch.job.step.KPTasklet" /> <bean id="cvsFileItemWriter" class="org.springframework.batch.item.file.FlatFileItemWriter" scope="step"> <!-- write to this csv file --> <property name="resource" value="#{stepExecutionContext['fileName']}.tmp" /> <property name="shouldDeleteIfExists" value="true" /> <property name="lineAggregator"> <bean class="org.springframework.batch.item.file.transform.DelimitedLineAggregator"> <property name="delimiter" value="," /> <property name="fieldExtractor"> <bean class="org.springframework.batch.item.file.transform.BeanWrapperFieldExtractor"> <property name="names" value="server,refferer,application,baseApplication,httpSize" /> </bean> </property> </bean> </property> </bean>
The Main Class
@SpringBootApplication @EnableBatchProcessing @ImportResource(value={"classpath:spring-context.xml"}) public class KPBootApplication { private final static Logger LOG = LoggerFactory .getLogger(KPBootApplication.class); public static void main(String[] args) { ApplicationContext ctx = SpringApplication.run( KPBootApplication.class, args); LOG.info("Application KPBOOT Started"); SayHello hello = (SayHello) ctx.getBean("sayHello"); if (hello != null) { LOG.debug("hello is not null"); LOG.info("Got message {}", hello.getMessage()); } else { LOG.debug("hello is null"); } LOG.info("Done"); } }
当我注释掉contextconfiguration注释并通过启用@SpringApplicationConfiguration来启用spring-boot时(注意:我已经使用注释@EnableBatchProcessing启用了spring batch),并注释掉了由spring-boot自动为spring batch创建的bean,例如JobRepo。错误,指出步骤范围没有上下文持有者
java.lang.IllegalStateException: Failed to load ApplicationContext at org.springframework.test.context.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:94) at org.springframework.test.context.DefaultTestContext.getApplicationContext(DefaultTestContext.java:72) at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.injectDependencies(DependencyInjectionTestExecutionListener.java:117) at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.prepareTestInstance(DependencyInjectionTestExecutionListener.java:83) at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:212) at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.createTest(SpringJUnit4ClassRunner.java:200) at org.springframework.test.context.junit4.SpringJUnit4ClassRunner$1.runReflectiveCall(SpringJUnit4ClassRunner.java:252) at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.methodBlock(SpringJUnit4ClassRunner.java:254) at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:217) at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:83) at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290) at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71) at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288) at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58) at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268) at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61) at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:68) at org.junit.runners.ParentRunner.run(ParentRunner.java:363) at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:163) at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50) at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197) Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'step2': Cannot resolve reference to bean 'step2.slave' while setting bean property 'step'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'step2.slave': Cannot resolve reference to bean 'reqItemReader' while setting bean property 'itemReader'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'reqItemReader': Scope 'step' is not active for the current thread; consider defining a scoped proxy for this bean if you intend to refer to it from a singleton; nested exception is java.lang.IllegalStateException: No context holder available for step scope at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:359) at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:108) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:1469) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1214) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:537) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:476) at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:303) at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230) at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:299) at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:194) at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:743) at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:757) at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:480) at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:691) at org.springframework.boot.SpringApplication.run(SpringApplication.java:321) at org.springframework.boot.test.SpringApplicationContextLoader.loadContext(SpringApplicationContextLoader.java:98) at org.springframework.test.context.DefaultCacheAwareContextLoaderDelegate.loadContextInternal(DefaultCacheAwareContextLoaderDelegate.java:68) at org.springframework.test.context.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:86) ... 25 common frames omitted Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'step2.slave': Cannot resolve reference to bean 'reqItemReader' while setting bean property 'itemReader'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'reqItemReader': Scope 'step' is not active for the current thread; consider defining a scoped proxy for this bean if you intend to refer to it from a singleton; nested exception is java.lang.IllegalStateException: No context holder available for step scope at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:359) at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:108) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:1469) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1214) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:537) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:476) at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:303) at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230) at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:299) at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:194) at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:351) ... 42 common frames omitted Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'reqItemReader': Scope 'step' is not active for the current thread; consider defining a scoped proxy for this bean if you intend to refer to it from a singleton; nested exception is java.lang.IllegalStateException: No context holder available for step scope at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:352) at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:194) at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:351) ... 52 common frames omitted Caused by: java.lang.IllegalStateException: No context holder available for step scope at org.springframework.batch.core.scope.StepScope.getContext(StepScope.java:160) at org.springframework.batch.core.scope.StepScope.get(StepScope.java:99) at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:337) ... 54 common frames omitted
不知道为什么Spring Boot会引发错误 注:Spring Boot版本1.2.1
这可能是一个错误(我们仍在调查中),但是我们确实有解决方法。这样做的原因是,当使用@EnableBatchProcessing的StepScope是自动配置假定java的配置,因此不代理步骤范围的Bean,使他们得太早创建。解决方法是StepScope使用以下配置在XML配置中手动配置:
@EnableBatchProcessing
StepScope
<bean id="stepScope" class="org.springframework.batch.core.scope.StepScope"> <property name="autoProxy" value="true"/> </bean>