无法使用带有 spring 批处理 + spring 启动的 XStreamMarshaller 解析 xml

Unable to parse xml using XStreamMarshaller with spring batch + spring boot

我正在尝试使用 XStreamMarshaller 在 spring 批处理和 spring 引导中将 xml 解析为 java 个对象。一切正常,我能够解析文件并获得所需的 java 对象,但是一旦我引入 taskexecutor 多线程来提高我的性能,它就开始给我错误,我无法修复。我搜索了问题,但没有找到任何适当的原因或响应。请帮助我解决问题或将我重定向到一些合适的 link,这可能会解决我的问题。 提前致谢...

BatchxmlApplication.java

@SpringBootApplication
@EnableBatchProcessing
public class BatchxmlApplication {

    public static void main(String[] args) {
        SpringApplication.run(BatchxmlApplication.class, args);
    }

}

XmlConfiguration.java

@Configuration
public class XmlConfiguration 
{

    @Autowired
    JobBuilderFactory jobBuilderFactory;

    @Autowired
    StepBuilderFactory stepBuilderFactory;

    @StepScope
    @Bean(name="xmlReader")
    public StaxEventItemReader<StudentDTO> reader() 
    {
        StaxEventItemReader<StudentDTO> xmlFileReader = new StaxEventItemReader<>();
        xmlFileReader.setResource(new ClassPathResource("students.xml"));
        xmlFileReader.setFragmentRootElementName("student");

        Map<String, Class<?>> aliases = new HashMap<>();
        aliases.put("student", StudentDTO.class);

        XStreamMarshaller xStreamMarshaller = new XStreamMarshaller();
        xStreamMarshaller.setAliases(aliases);

        xmlFileReader.setUnmarshaller(xStreamMarshaller);

        return xmlFileReader;
    }

    @Bean(name="xmlProcessor")
    public ItemProcessor<StudentDTO, StudentDTO> processor() 
    {
        return new Processor();
    }

    @Bean(name="xmlWriter")
    public ItemWriter<StudentDTO> writer() 
    {
        return new Writer();     
    }

    @Bean(name="xmljobListener")
    public JobExecutionListenerSupport jobListener() 
    {
        return new JobListener();
    }

    @JobScope
    @Bean(name="xmltaskExecutor")   
    public ThreadPoolTaskExecutor taskExecutor() 
    {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(50);
        executor.setMaxPoolSize(100);
        return executor;
    }

    @Bean(name="xmlStep")
    public Step xmlFileToDatabaseStep() 
    {
        return stepBuilderFactory.get("xmlStep")
                .<StudentDTO, StudentDTO>chunk(1)
                .reader(this.reader())
                .processor(this.processor())
                .writer(this.writer())
                .taskExecutor(this.taskExecutor())
                .build();
    }

    @Bean(name="xmlJob")
    public Job xmlFileToDatabaseJob(@Autowired @Qualifier("xmlStep") Step step) 
    {
        return jobBuilderFactory
                .get("xmlJob"+new Date())
                .incrementer(new RunIdIncrementer())
                .listener(this.jobListener())
                .flow(step)
                .end()
                .build();
    }

}

Processor.java

public class Processor implements ItemProcessor<StudentDTO, StudentDTO>
{
    @Override
    public StudentDTO process(StudentDTO item) throws Exception 
    {
        StudentDTO st = item;
        return st;
    }

}

Writer.java

public class Writer implements ItemWriter<StudentDTO>
{
    @Override
    public void write(List<? extends StudentDTO> items) throws Exception 
    {
        items.stream().forEach(i->System.err.println(i));
    }
}

StudentDTO.java

@XmlRootElement(name="student")
public class StudentDTO 
{
    private String emailAddress;
    private String name;
    private String purchasedPackage;
    ... getter,setter and constructor
}

XMLBatchController.java

@CrossOrigin("*")
@RestController
public class XMLBatchController 
{
    @Autowired
    @Qualifier("xmlJob")
    Job job;

    @Autowired
    private JobLauncher jobLauncher;

    @GetMapping(value="/run")
    public String run() throws JobExecutionAlreadyRunningException, JobRestartException, JobInstanceAlreadyCompleteException, JobParametersInvalidException 
    {
        long st = System.currentTimeMillis();
        JobParametersBuilder builder = new JobParametersBuilder();
        builder.addDate("date", new Date());
        jobLauncher.run(job, builder.toJobParameters());
        return "The  processing took = "+(System.currentTimeMillis()-st)+" ms<p>Timestamp = "+new Date();
    }
}

JobListener.java

public class JobListener extends JobExecutionListenerSupport 
{

    @Autowired
    @Qualifier("xmltaskExecutor")
    ThreadPoolTaskExecutor taskExecutor; 

    @Override
    public void afterJob(JobExecution jobExecution) 
    {
        if(jobExecution.getStatus() == BatchStatus.COMPLETED) 
        {
            taskExecutor.shutdown();
            System.err.println("*****************");
            System.err.println("\tJob Completed");
            System.err.println("*****************");
        }
        else
        {
            System.err.println("*****************");
            System.err.println("\tJob Failed");
            System.err.println("*****************");            
        }
    }

    @Override
    public void beforeJob(JobExecution jobExecution) 
    {

    }

}

students.xml

<students>
    <student>
        <name>Tony Tester</name>
        <emailAddress>tony.tester@gmail.com</emailAddress>
        <purchasedPackage>master</purchasedPackage>
    </student>
    <student>
        <name>Nick Newbie</name>
        <emailAddress>nick.newbie@gmail.com</emailAddress>
        <purchasedPackage>starter</purchasedPackage>
    </student>
    <student>
        <name>Ian Intermediate</name>
        <emailAddress>ian.intermediate@gmail.com</emailAddress>
        <purchasedPackage>intermediate</purchasedPackage>
    </student>
</students>

错误日志

2019-06-20 12:26:05.039  INFO 12108 --- [nio-9090-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/batch]  : Initializing Spring DispatcherServlet 'dispatcherServlet'
2019-06-20 12:26:05.039  INFO 12108 --- [nio-9090-exec-1] o.s.web.servlet.DispatcherServlet        : Initializing Servlet 'dispatcherServlet'
2019-06-20 12:26:05.048  INFO 12108 --- [nio-9090-exec-1] o.s.web.servlet.DispatcherServlet        : Completed initialization in 9 ms
2019-06-20 12:26:05.350  INFO 12108 --- [nio-9090-exec-1] o.s.b.c.l.support.SimpleJobLauncher      : Job: [FlowJob: [name=xmlJobThu Jun 20 12:25:57 IST 2019]] launched with the following parameters: [{date=1561013765206}]
2019-06-20 12:26:05.411  INFO 12108 --- [nio-9090-exec-1] o.s.batch.core.job.SimpleStepHandler     : Executing step: [xmlStep]
2019-06-20 12:26:05.497  INFO 12108 --- [nio-9090-exec-1] o.s.s.concurrent.ThreadPoolTaskExecutor  : Initializing ExecutorService 'scopedTarget.xmltaskExecutor'
2019-06-20 12:26:05.642 ERROR 12108 --- [nio-9090-exec-1] o.s.batch.core.step.AbstractStep         : Encountered an error executing step xmlStep in job xmlJobThu Jun 20 12:25:57 IST 2019

org.springframework.oxm.UnmarshallingFailureException: XStream unmarshalling exception; nested exception is com.thoughtworks.xstream.converters.ConversionException: 
---- Debugging information ----
cause-exception     : java.util.NoSuchElementException
cause-message       : null
class               : com.example.demo.dto.StudentDTO
required-type       : com.example.demo.dto.StudentDTO
converter-type      : com.thoughtworks.xstream.converters.reflection.ReflectionConverter
path                : /student
line number         : 2
version             : 5.1.8.RELEASE
-------------------------------
    at org.springframework.oxm.xstream.XStreamMarshaller.convertXStreamException(XStreamMarshaller.java:851) ~[spring-oxm-5.1.8.RELEASE.jar:5.1.8.RELEASE]
    at org.springframework.oxm.xstream.XStreamMarshaller.doUnmarshal(XStreamMarshaller.java:829) ~[spring-oxm-5.1.8.RELEASE.jar:5.1.8.RELEASE]
    at org.springframework.oxm.xstream.XStreamMarshaller.unmarshalXmlStreamReader(XStreamMarshaller.java:786) ~[spring-oxm-5.1.8.RELEASE.jar:5.1.8.RELEASE]
    at org.springframework.oxm.xstream.XStreamMarshaller.unmarshalXmlEventReader(XStreamMarshaller.java:777) ~[spring-oxm-5.1.8.RELEASE.jar:5.1.8.RELEASE]
    at org.springframework.oxm.support.AbstractMarshaller.unmarshalStaxSource(AbstractMarshaller.java:411) ~[spring-oxm-5.1.8.RELEASE.jar:5.1.8.RELEASE]
    at org.springframework.oxm.support.AbstractMarshaller.unmarshal(AbstractMarshaller.java:354) ~[spring-oxm-5.1.8.RELEASE.jar:5.1.8.RELEASE]
    at org.springframework.batch.item.xml.StaxEventItemReader.doRead(StaxEventItemReader.java:255) ~[spring-batch-infrastructure-4.1.2.RELEASE.jar:4.1.2.RELEASE]
    at org.springframework.batch.item.support.AbstractItemCountingItemStreamItemReader.read(AbstractItemCountingItemStreamItemReader.java:92) ~[spring-batch-infrastructure-4.1.2.RELEASE.jar:4.1.2.RELEASE]
    at org.springframework.batch.item.support.AbstractItemCountingItemStreamItemReader$$FastClassBySpringCGLIB$$ebb633d0.invoke(<generated>) ~[spring-batch-infrastructure-4.1.2.RELEASE.jar:4.1.2.RELEASE]
    at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218) ~[spring-core-5.1.8.RELEASE.jar:5.1.8.RELEASE]
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:749) ~[spring-aop-5.1.8.RELEASE.jar:5.1.8.RELEASE]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) ~[spring-aop-5.1.8.RELEASE.jar:5.1.8.RELEASE]
    at org.springframework.aop.support.DelegatingIntroductionInterceptor.doProceed(DelegatingIntroductionInterceptor.java:136) ~[spring-aop-5.1.8.RELEASE.jar:5.1.8.RELEASE]
    at org.springframework.aop.support.DelegatingIntroductionInterceptor.invoke(DelegatingIntroductionInterceptor.java:124) ~[spring-aop-5.1.8.RELEASE.jar:5.1.8.RELEASE]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.1.8.RELEASE.jar:5.1.8.RELEASE]
    at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:688) ~[spring-aop-5.1.8.RELEASE.jar:5.1.8.RELEASE]
    at org.springframework.batch.item.xml.StaxEventItemReader$$EnhancerBySpringCGLIB$$f905f63e.read(<generated>) ~[spring-batch-infrastructure-4.1.2.RELEASE.jar:4.1.2.RELEASE]
    at org.springframework.batch.core.step.item.SimpleChunkProvider.doRead(SimpleChunkProvider.java:94) ~[spring-batch-core-4.1.2.RELEASE.jar:4.1.2.RELEASE]
    at org.springframework.batch.core.step.item.SimpleChunkProvider.read(SimpleChunkProvider.java:161) ~[spring-batch-core-4.1.2.RELEASE.jar:4.1.2.RELEASE]
    at org.springframework.batch.core.step.item.SimpleChunkProvider.doInIteration(SimpleChunkProvider.java:119) ~[spring-batch-core-4.1.2.RELEASE.jar:4.1.2.RELEASE]
    at org.springframework.batch.repeat.support.RepeatTemplate.getNextResult(RepeatTemplate.java:375) ~[spring-batch-infrastructure-4.1.2.RELEASE.jar:4.1.2.RELEASE]
    at org.springframework.batch.repeat.support.RepeatTemplate.executeInternal(RepeatTemplate.java:215) ~[spring-batch-infrastructure-4.1.2.RELEASE.jar:4.1.2.RELEASE]
    at org.springframework.batch.repeat.support.RepeatTemplate.iterate(RepeatTemplate.java:145) ~[spring-batch-infrastructure-4.1.2.RELEASE.jar:4.1.2.RELEASE]
    at org.springframework.batch.core.step.item.SimpleChunkProvider.provide(SimpleChunkProvider.java:113) ~[spring-batch-core-4.1.2.RELEASE.jar:4.1.2.RELEASE]
    at org.springframework.batch.core.step.item.ChunkOrientedTasklet.execute(ChunkOrientedTasklet.java:69) ~[spring-batch-core-4.1.2.RELEASE.jar:4.1.2.RELEASE]
    at org.springframework.batch.core.step.tasklet.TaskletStep$ChunkTransactionCallback.doInTransaction(TaskletStep.java:407) ~[spring-batch-core-4.1.2.RELEASE.jar:4.1.2.RELEASE]
    at org.springframework.batch.core.step.tasklet.TaskletStep$ChunkTransactionCallback.doInTransaction(TaskletStep.java:331) ~[spring-batch-core-4.1.2.RELEASE.jar:4.1.2.RELEASE]
    at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:140) ~[spring-tx-5.1.8.RELEASE.jar:5.1.8.RELEASE]
    at org.springframework.batch.core.step.tasklet.TaskletStep.doInChunkContext(TaskletStep.java:273) ~[spring-batch-core-4.1.2.RELEASE.jar:4.1.2.RELEASE]
    at org.springframework.batch.core.scope.context.StepContextRepeatCallback.doInIteration(StepContextRepeatCallback.java:82) ~[spring-batch-core-4.1.2.RELEASE.jar:4.1.2.RELEASE]
    at org.springframework.batch.repeat.support.TaskExecutorRepeatTemplate$ExecutingRunnable.run(TaskExecutorRepeatTemplate.java:262) ~[spring-batch-infrastructure-4.1.2.RELEASE.jar:4.1.2.RELEASE]
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) ~[na:1.8.0_144]
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) ~[na:1.8.0_144]
    at java.lang.Thread.run(Thread.java:748) ~[na:1.8.0_144]
Caused by: com.thoughtworks.xstream.converters.ConversionException: 
---- Debugging information ----
cause-exception     : java.util.NoSuchElementException
cause-message       : null
class               : com.example.demo.dto.StudentDTO
required-type       : com.example.demo.dto.StudentDTO
converter-type      : com.thoughtworks.xstream.converters.reflection.ReflectionConverter
path                : /student
line number         : 2
version             : 5.1.8.RELEASE
-------------------------------
    at com.thoughtworks.xstream.core.TreeUnmarshaller.convert(TreeUnmarshaller.java:79) ~[xstream-1.4.9.jar:1.4.9]
    at com.thoughtworks.xstream.core.AbstractReferenceUnmarshaller.convert(AbstractReferenceUnmarshaller.java:70) ~[xstream-1.4.9.jar:1.4.9]
    at com.thoughtworks.xstream.core.TreeUnmarshaller.convertAnother(TreeUnmarshaller.java:66) ~[xstream-1.4.9.jar:1.4.9]
    at com.thoughtworks.xstream.core.TreeUnmarshaller.convertAnother(TreeUnmarshaller.java:50) ~[xstream-1.4.9.jar:1.4.9]
    at com.thoughtworks.xstream.core.TreeUnmarshaller.start(TreeUnmarshaller.java:134) ~[xstream-1.4.9.jar:1.4.9]
    at com.thoughtworks.xstream.core.AbstractTreeMarshallingStrategy.unmarshal(AbstractTreeMarshallingStrategy.java:32) ~[xstream-1.4.9.jar:1.4.9]
    at com.thoughtworks.xstream.XStream.unmarshal(XStream.java:1230) ~[xstream-1.4.9.jar:1.4.9]
    at org.springframework.oxm.xstream.XStreamMarshaller.doUnmarshal(XStreamMarshaller.java:826) ~[spring-oxm-5.1.8.RELEASE.jar:5.1.8.RELEASE]
    ... 32 common frames omitted
Caused by: java.util.NoSuchElementException: null
    at org.springframework.batch.item.xml.stax.DefaultFragmentEventReader.nextEvent(DefaultFragmentEventReader.java:112) ~[spring-batch-infrastructure-4.1.2.RELEASE.jar:4.1.2.RELEASE]
    at org.springframework.util.xml.XMLEventStreamReader.next(XMLEventStreamReader.java:277) ~[spring-core-5.1.8.RELEASE.jar:5.1.8.RELEASE]
    at com.thoughtworks.xstream.io.xml.StaxReader.pullNextEvent(StaxReader.java:58) ~[xstream-1.4.9.jar:1.4.9]
    at com.thoughtworks.xstream.io.xml.AbstractPullReader.readRealEvent(AbstractPullReader.java:148) ~[xstream-1.4.9.jar:1.4.9]
    at com.thoughtworks.xstream.io.xml.AbstractPullReader.readEvent(AbstractPullReader.java:135) ~[xstream-1.4.9.jar:1.4.9]
    at com.thoughtworks.xstream.io.xml.AbstractPullReader.hasMoreChildren(AbstractPullReader.java:87) ~[xstream-1.4.9.jar:1.4.9]
    at com.thoughtworks.xstream.io.ReaderWrapper.hasMoreChildren(ReaderWrapper.java:32) ~[xstream-1.4.9.jar:1.4.9]
    at com.thoughtworks.xstream.converters.reflection.AbstractReflectionConverter.doUnmarshal(AbstractReflectionConverter.java:333) ~[xstream-1.4.9.jar:1.4.9]
    at com.thoughtworks.xstream.converters.reflection.AbstractReflectionConverter.unmarshal(AbstractReflectionConverter.java:281) ~[xstream-1.4.9.jar:1.4.9]
    at com.thoughtworks.xstream.core.TreeUnmarshaller.convert(TreeUnmarshaller.java:72) ~[xstream-1.4.9.jar:1.4.9]
    ... 39 common frames omitted

*****************
    Job Failed
*****************
2019-06-20 12:26:05.693  INFO 12108 --- [nio-9090-exec-1] o.s.s.concurrent.ThreadPoolTaskExecutor  : Shutting down ExecutorService 'scopedTarget.xmltaskExecutor'
2019-06-20 12:26:05.694  INFO 12108 --- [nio-9090-exec-1] o.s.b.c.l.support.SimpleJobLauncher      : Job: [FlowJob: [name=xmlJobThu Jun 20 12:25:57 IST 2019]] completed with the following parameters: [{date=1561013765206}] and the following status: [FAILED]
2019-06-20 12:35:00.318  INFO 12108 --- [n(15)-127.0.0.1] inMXBeanRegistrar$SpringApplicationAdmin : Application shutdown requested.
2019-06-20 12:35:00.327  INFO 12108 --- [n(15)-127.0.0.1] j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default'
2019-06-20 12:35:00.329  INFO 12108 --- [n(15)-127.0.0.1] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Shutdown initiated...
2019-06-20 12:35:00.362  INFO 12108 --- [n(15)-127.0.0.1] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Shutdown completed.

如其 Javadoc 中所述,StaxEventItemReader 不是线程安全的。所以在 multi-threaded 步骤中使用它是不正确的。

您需要将其包装在 SynchronizedItemStreamReader 中。在你的情况下,它会是这样的:

@StepScope
@Bean(name="xmlReader")
public SynchronizedItemStreamReader<StudentDTO> reader() 
{
    StaxEventItemReader<StudentDTO> xmlFileReader = new StaxEventItemReader<>();
    xmlFileReader.setResource(new ClassPathResource("students.xml"));
    xmlFileReader.setFragmentRootElementName("student");

    Map<String, Class<?>> aliases = new HashMap<>();
    aliases.put("student", StudentDTO.class);

    XStreamMarshaller xStreamMarshaller = new XStreamMarshaller();
    xStreamMarshaller.setAliases(aliases);

    xmlFileReader.setUnmarshaller(xStreamMarshaller);

    SynchronizedItemStreamReader< StudentDTO> synchronizedItemStreamReader = new SynchronizedItemStreamReader<>();
    synchronizedItemStreamReader.setDelegate(xmlFileReader);
    return synchronizedItemStreamReader;
}