春季批处理作业抛出堆栈溢出错误:启动时出现空错误



我有一个简单的控制器,它接受文件路径的 JSON 字符串,并对这些文件运行 spring 批处理作业。为了实现 spring 批处理,我遵循一个教程,该教程最终将在 https://github.com/michaelhoffmantech/patient-batch-loader 中生成代码。

@RestController
public class JobController {
private static final Log _logger = LogFactory.getLog(JobController.class);
@Autowired
private JobLauncher jobLauncher;
@Autowired
private Job job;
@RequestMapping(value = "/job/import", method = RequestMethod.POST)
public void importAsset(@RequestBody String uploadedFiles) throws JobParametersInvalidException, JobExecutionAlreadyRunningException, JobRestartException, JobInstanceAlreadyCompleteException {
_logger.debug("importAsset() >>");
try{
_logger.debug("Uploaded files: ");
_logger.debug(uploadedFiles);
_logger.debug("Putting values into the parameter map...");
Map<String, JobParameter> parameterMap = new LinkedHashMap<>();
parameterMap.put(Constants.JOB_PARAM_UPLOADED_FILES, new JobParameter(uploadedFiles));
_logger.debug("Launching job [" + job.getName() + "]...");
jobLauncher.run(job, new JobParameters(parameterMap));
_logger.debug("<< importAsset()");
}
catch (Exception e){
String errorMessage = "An error occured while importing a batch. " + e.getLocalizedMessage();
_logger.error(errorMessage, e);
throw e;
}
}
}

批量配置为:

@Configuration
@EnableBatchProcessing
public class BatchConfig implements BatchConfigurer {
@Autowired
private PlatformTransactionManager platformTransactionManager;
@Autowired
private DataSource dataSource;
private JobRepository jobRepository;
private JobExplorer jobExplorer;
private JobLauncher jobLauncher;

@Override
public JobRepository getJobRepository() throws Exception {
return this.jobRepository;
}
@Override
public PlatformTransactionManager getTransactionManager() throws Exception {
return this.platformTransactionManager;
}
@Override
public JobLauncher getJobLauncher() throws Exception {
return this.jobLauncher;
}
@Override
public JobExplorer getJobExplorer() throws Exception {
return this.jobExplorer;
}
protected JobLauncher createJobLauncher() throws Exception {
SimpleJobLauncher jobLauncher = new SimpleJobLauncher();
jobLauncher.setJobRepository(this.jobRepository);
jobLauncher.afterPropertiesSet();
return jobLauncher;
}
protected JobRepository createJobRepository() throws Exception {
JobRepositoryFactoryBean factory = new JobRepositoryFactoryBean();
factory.setDataSource(this.dataSource);
factory.setTransactionManager(getTransactionManager());
factory.afterPropertiesSet();
return factory.getObject();
}
@PostConstruct
public void afterPropertiesSet() throws Exception{
this.jobRepository = createJobRepository();
JobExplorerFactoryBean jobExplorerFactoryBean = new JobExplorerFactoryBean();
jobExplorerFactoryBean.setDataSource(this.dataSource);
jobExplorerFactoryBean.afterPropertiesSet();
this.jobExplorer = jobExplorerFactoryBean.getObject();
this.jobLauncher = createJobLauncher();
}
}

最后,作业配置为:

@Configuration
public class BatchJobConfig {
private static final Log _logger = LogFactory.getLog(BatchJobConfig.class);
@Autowired
private JobBuilderFactory jobBuilderFactory;
@Autowired
private StepBuilderFactory stepBuilderFactory;
@Bean
JobRegistryBeanPostProcessor jobRegistryBeanPostProcessor(JobRegistry jobRegistry){
JobRegistryBeanPostProcessor postProcessor = new JobRegistryBeanPostProcessor();
postProcessor.setJobRegistry(jobRegistry);
return postProcessor;
}
@Bean
public Step step() throws Exception{
return this.stepBuilderFactory
.get(Constants.STEP_NAME)
.tasklet(new Tasklet() {
@Override
public RepeatStatus execute(StepContribution stepContribution, ChunkContext chunkContext) throws Exception {
_logger.debug("execute() >>");
_logger.debug("<< execute()");
return RepeatStatus.FINISHED;
}
})
.build();
}
@Bean
public Job job(Step step){
return this.jobBuilderFactory
.get(Constants.JOB_NAME)
.validator(validator())
.start(step)
.build();
}
public JobParametersValidator validator(){
return new JobParametersValidator(){
@Override
public void validate(JobParameters parameters) throws JobParametersInvalidException {
_logger.debug("validate() >>");
String filePathsJsonStr = parameters.getString(Constants.JOB_PARAM_UPLOADED_FILES);
if(StringUtils.isBlank(filePathsJsonStr)){
throw new JobParametersInvalidException("'" + Constants.JOB_PARAM_UPLOADED_FILES  + "' parameter is required for job '" + Constants.JOB_NAME + "'.");
}
Gson gson = new Gson();
Type listType = new TypeToken<ArrayList<UploadFile>>(){}.getType();
ArrayList<UploadFile> uploadedFiles = gson.fromJson(filePathsJsonStr, listType);
for(UploadFile uploadFile: uploadedFiles){
File file = new File(uploadFile.getPath());
if(!file.exists()){
throw new JobParametersInvalidException("File '" + uploadFile.getPath() + "' did not exist or was not readable.");
}
}
_logger.debug("<< validate()");
}
};
}
}

当我通过 REST 方法运行批处理作业时。我在日志中得到以下内容:

2018-09-29 20:00:51.680 DEBUG 10104 --- [nio-8102-exec-2] c.g.m.t.t.controllers.JobController      : importAsset() >>
2018-09-29 20:00:51.680 DEBUG 10104 --- [nio-8102-exec-2] c.g.m.t.t.controllers.JobController      : Uploaded files: 
2018-09-29 20:00:51.680 DEBUG 10104 --- [nio-8102-exec-2] c.g.m.t.t.controllers.JobController      : [{"path":"C:\app-import-staging\cat-pet-animal-domestic-104827.jpeg","username":"test"},{"path":"C:\app-import-staging\kittens-cat-cat-puppy-rush-45170.jpeg","username":"test"}]
2018-09-29 20:00:51.680 DEBUG 10104 --- [nio-8102-exec-2] c.g.m.t.t.controllers.JobController      : Putting values into the parameter map...
2018-09-29 20:00:51.681 DEBUG 10104 --- [nio-8102-exec-2] c.g.m.t.t.controllers.JobController      : Launching job [app-import]...
2018-09-29 20:00:51.745 ERROR 10104 --- [nio-8102-exec-2] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Handler dispatch failed; nested exception is java.lang.StackOverflowError] with root cause
java.lang.StackOverflowError: null
at java.util.HashMap.putVal(HashMap.java:657) ~[na:1.8.0_181]
at java.util.HashMap.put(HashMap.java:612) ~[na:1.8.0_181]
at java.util.HashSet.add(HashSet.java:220) ~[na:1.8.0_181]
at java.util.Collections$SynchronizedCollection.add(Collections.java:2035) ~[na:1.8.0_181]
at java.lang.ClassLoader.checkPackageAccess(ClassLoader.java:508) ~[na:1.8.0_181]
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:333) ~[spring-aop-4.3.14.RELEASE.jar:4.3.14.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:190) ~[spring-aop-4.3.14.RELEASE.jar:4.3.14.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157) ~[spring-aop-4.3.14.RELEASE.jar:4.3.14.RELEASE]
at org.springframework.batch.core.configuration.annotation.SimpleBatchConfiguration$PassthruAdvice.invoke(SimpleBatchConfiguration.java:127) ~[spring-batch-core-3.0.8.RELEASE.jar:3.0.8.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.3.14.RELEASE.jar:4.3.14.RELEASE]
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:213) ~[spring-aop-4.3.14.RELEASE.jar:4.3.14.RELEASE]
at com.sun.proxy.$Proxy83.getTransaction(Unknown Source) ~[na:na]
at sun.reflect.GeneratedMethodAccessor93.invoke(Unknown Source) ~[na:na]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_181]
at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_181]
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:333) ~[spring-aop-4.3.14.RELEASE.jar:4.3.14.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:190) ~[spring-aop-4.3.14.RELEASE.jar:4.3.14.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157) ~[spring-aop-4.3.14.RELEASE.jar:4.3.14.RELEASE]
at org.springframework.batch.core.configuration.annotation.SimpleBatchConfiguration$PassthruAdvice.invoke(SimpleBatchConfiguration.java:127) ~[spring-batch-core-3.0.8.RELEASE.jar:3.0.8.RELEASE]

我注意到的是,该部分:

at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.3.14.RELEASE.jar:4.3.14.RELEASE]
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:213) ~[spring-aop-4.3.14.RELEASE.jar:4.3.14.RELEASE]
at com.sun.proxy.$Proxy83.getTransaction(Unknown Source) ~[na:na]
at sun.reflect.GeneratedMethodAccessor93.invoke(Unknown Source) ~[na:na]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_181]
at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_181]
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:333) ~[spring-aop-4.3.14.RELEASE.jar:4.3.14.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:190) ~[spring-aop-4.3.14.RELEASE.jar:4.3.14.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157) ~[spring-aop-4.3.14.RELEASE.jar:4.3.14.RELEASE]
at org.springframework.batch.core.configuration.annotation.SimpleBatchConfiguration$PassthruAdvice.invoke(SimpleBatchConfiguration.java:127) ~[spring-batch-core-3.0.8.RELEASE.jar:3.0.8.RELEASE]

一直持续下去,直到它抛出一个 StackOverflowError。

任何关于更改以解决此问题的建议或帮助将不胜感激。

BatchConfig中的更改.java

@Override
public PlatformTransactionManager getTransactionManager() throws Exception {
return this.platformTransactionManager;
}

@Override
public PlatformTransactionManager getTransactionManager() throws Exception {
return new DataSourceTransactionManager(dataSource);
}

我最终通过删除BatchJobConfig并修改BatchConfig类来简化工作,如下所示:

@Configuration
@EnableBatchProcessing
public class BatchConfig {
private static final Log _logger = LogFactory.getLog(BatchConfig.class);
@Bean
public Job job(JobBuilderFactory jobBuilderFactory, StepBuilderFactory stepBuilderFactory){
Step step = stepBuilderFactory.get(Constants.STEP_NAME)
.tasklet(new Tasklet() {
@Override
public RepeatStatus execute(StepContribution stepContribution, ChunkContext chunkContext) throws Exception {
_logger.debug("execute() >>");
_logger.debug("<< execute()");
return RepeatStatus.FINISHED;
}
})
.build();
return jobBuilderFactory.get(Constants.JOB_NAME)
.incrementer(new RunIdIncrementer())
.validator(validator())
.start(step)
.build();
}
public JobParametersValidator validator(){
return new JobParametersValidator(){
@Override
public void validate(JobParameters parameters) throws JobParametersInvalidException {
_logger.debug("validate() >>");
String filePathsJsonStr = parameters.getString(Constants.JOB_PARAM_UPLOADED_FILES);
if(StringUtils.isBlank(filePathsJsonStr)){
throw new JobParametersInvalidException("'" + Constants.JOB_PARAM_UPLOADED_FILES  + "' parameter is required for job '" + Constants.JOB_NAME + "'.");
}
Gson gson = new Gson();
Type listType = new TypeToken<ArrayList<UploadFile>>(){}.getType();
ArrayList<UploadFile> uploadedFiles = gson.fromJson(filePathsJsonStr, listType);
for(UploadFile uploadFile: uploadedFiles){
File file = new File(uploadFile.getPath());
if(!file.exists()){
throw new JobParametersInvalidException("File '" + uploadFile.getPath() + "' did not exist or was not readable.");
}
}
_logger.debug("<< validate()");
}
};
}
}

在此之后,作业按预期执行。

最新更新