首页 > 解决方案 > Spring 不实例化具有 Map 或 List 作为属性的 bean

问题描述

我正在使用 Spring Boot 和 Spring Batch 实现一个基本的批处理应用程序。

对于在输入目录中找到的每个文件,都会创建一个步骤,这要归功于MultiResourcePartitioner

<bean id="partitioner"
      class="org.springframework.batch.core.partition.support.MultiResourcePartitioner">
    <description>
        Create a step for each files found in the input directory
    </description>
    <property name="resources" value="#{resourceProvider.read()}"/>
</bean>

每个步骤由 ItemReader、ItemProcessor 和 ItemWriter 组成。我的问题是 ItemReader,事实上每个 bean 都使用 List/Map 作为 xml 配置中的属性。

我的 ItemReader 如下:

<bean id="itemReader" class="org.springframework.batch.item.file.FlatFileItemReader" scope="step">
    <description>
        * resource is set via step scoped late binding (see multiresourcereader)
        * strict mode - we expect input files
    </description>
    <property name="resource" value="#{stepExecutionContext['fileName']}"/>
    <property name="strict" value="true"/>
    <property name="lineMapper" ref="lineMapper"/>
</bean>

<bean id="lineMapper" class="input.CustomPatternMatchingLineMapper">
    <property name="fieldSetMappers">
        <map>
            <entry key="001*" value-ref="payloadFieldSetMapper"/>
            <entry key="000*" value-ref="headerFieldSetMapper"/>
            <entry key="999*" value-ref="footerFieldSetMapper"/>
        </map>
    </property>
    <property name="tokenizers">
        <map>
            <entry key="001*" value-ref="payloadTokenizer"/>
            <entry key="000*" value-ref="headerTokenizer"/>
            <entry key="999*" value-ref="footerTokenizer"/>
        </map>
    </property>
</bean>

<bean id="headerTokenizer" class="input.tokenizer.HeaderTokenizer"/>
<bean id="payloadTokenizer" class="input.tokenizer.PayloadTokenizer"/>
<bean id="footerTokenizer" class="input.tokenizer.FooterTokenizer"/>

<bean id="headerFieldSetMapper" class="input.fieldsetmapper.HeaderFieldSetMapper"/>
<bean id="payloadFieldSetMapper" class="input.fieldsetmapper.PayloadFieldSetMapper"/>
<bean id="footerFieldSetMapper" class="input.fieldsetmapper.FooterFieldSetMapper"/>

这是我在 bean 实例化期间在日志中得到的内容

2020-04-21 10:22:32.491 DEBUG 6808 --- [           main] o.s.beans.TypeConverterDelegate          : Original ConversionService attempt failed - ignored since PropertyEditor based conversion eventually succeeded

org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.util.LinkedHashMap<?, ?>] to type [@org.springframework.lang.Nullable java.util.Map<java.lang.Class<?>, java.lang.Class<java.beans.PropertyEditor>>] for value '{org.springframework.batch.item.file.transform.Range[]=org.springframework.batch.item.file.transform.RangeArrayPropertyEditor}'; nested exception is org.springframework.core.convert.ConverterNotFoundException: No converter found capable of converting from type [java.lang.String] to type [@org.springframework.lang.Nullable java.lang.Class<?>]
    at org.springframework.core.convert.support.ConversionUtils.invokeConverter(ConversionUtils.java:47) ~[spring-core-5.2.5.RELEASE.jar:5.2.5.RELEASE]
    at org.springframework.core.convert.support.GenericConversionService.convert(GenericConversionService.java:191) ~[spring-core-5.2.5.RELEASE.jar:5.2.5.RELEASE]
    at org.springframework.beans.TypeConverterDelegate.convertIfNecessary(TypeConverterDelegate.java:129) ~[spring-beans-5.2.5.RELEASE.jar:5.2.5.RELEASE]
    at org.springframework.beans.AbstractNestablePropertyAccessor.convertIfNecessary(AbstractNestablePropertyAccessor.java:585) ~[spring-beans-5.2.5.RELEASE.jar:5.2.5.RELEASE]
    at org.springframework.beans.AbstractNestablePropertyAccessor.convertForProperty(AbstractNestablePropertyAccessor.java:604) ~[spring-beans-5.2.5.RELEASE.jar:5.2.5.RELEASE]
    at org.springframework.beans.BeanWrapperImpl.convertForProperty(BeanWrapperImpl.java:219) ~[spring-beans-5.2.5.RELEASE.jar:5.2.5.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.convertForProperty(AbstractAutowireCapableBeanFactory.java:1748) ~[spring-beans-5.2.5.RELEASE.jar:5.2.5.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:1704) ~[spring-beans-5.2.5.RELEASE.jar:5.2.5.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1444) ~[spring-beans-5.2.5.RELEASE.jar:5.2.5.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:594) ~[spring-beans-5.2.5.RELEASE.jar:5.2.5.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:517) ~[spring-beans-5.2.5.RELEASE.jar:5.2.5.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:323) ~[spring-beans-5.2.5.RELEASE.jar:5.2.5.RELEASE]
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222) ~[spring-beans-5.2.5.RELEASE.jar:5.2.5.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:321) ~[spring-beans-5.2.5.RELEASE.jar:5.2.5.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:207) ~[spring-beans-5.2.5.RELEASE.jar:5.2.5.RELEASE]
    at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:171) ~[spring-context-5.2.5.RELEASE.jar:5.2.5.RELEASE]
    at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:706) ~[spring-context-5.2.5.RELEASE.jar:5.2.5.RELEASE]
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:532) ~[spring-context-5.2.5.RELEASE.jar:5.2.5.RELEASE]
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:747) ~[spring-boot-2.2.6.RELEASE.jar:2.2.6.RELEASE]
    at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:397) ~[spring-boot-2.2.6.RELEASE.jar:2.2.6.RELEASE]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:315) ~[spring-boot-2.2.6.RELEASE.jar:2.2.6.RELEASE]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1226) ~[spring-boot-2.2.6.RELEASE.jar:2.2.6.RELEASE]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1215) ~[spring-boot-2.2.6.RELEASE.jar:2.2.6.RELEASE]
    at Lanceur.main(Lanceur.java:40) ~[classes/:na]
Caused by: org.springframework.core.convert.ConverterNotFoundException: No converter found capable of converting from type [java.lang.String] to type [@org.springframework.lang.Nullable java.lang.Class<?>]
    at org.springframework.core.convert.support.GenericConversionService.handleConverterNotFound(GenericConversionService.java:321) ~[spring-core-5.2.5.RELEASE.jar:5.2.5.RELEASE]
    at org.springframework.core.convert.support.GenericConversionService.convert(GenericConversionService.java:194) ~[spring-core-5.2.5.RELEASE.jar:5.2.5.RELEASE]
    at org.springframework.core.convert.support.MapToMapConverter.convertKey(MapToMapConverter.java:122) ~[spring-core-5.2.5.RELEASE.jar:5.2.5.RELEASE]
    at org.springframework.core.convert.support.MapToMapConverter.convert(MapToMapConverter.java:84) ~[spring-core-5.2.5.RELEASE.jar:5.2.5.RELEASE]
    at org.springframework.core.convert.support.ConversionUtils.invokeConverter(ConversionUtils.java:41) ~[spring-core-5.2.5.RELEASE.jar:5.2.5.RELEASE]
    ... 23 common frames omitted

这是该过程无限期挂起的地方:

2020-04-21 10:22:36.261 DEBUG 6808 --- [cTaskExecutor-4] o.s.batch.core.scope.StepScope           : Creating object in scope=step, name=scopedTarget.itemReader

供您参考,如果我使用另一个 LineMapper,我不会遇到这个问题。

另一个信息,因为我使用的是 Spring Boot,所以我必须覆盖 StepScope 定义才能使用该scope="step"属性

<bean id="stepScope" class="org.springframework.batch.core.scope.StepScope">
    <property name="autoProxy" value="true"/>
</bean>
spring.main.allow-bean-definition-overriding=true

编辑
这是我必须映射的行的示例,它的关联 POJO 和它实现的接口:

001234577589NNNNNN                   FFF                        M
@Data
public class Payload implements Element {

    private String debLine;

    private String id;

    private String name;

    private String firstName;

    private String gender;

    private int numLine;
}
public interface Element {

    String getDebLine();

    int getNumLine();

    void setNumLine(int numLine);

}

标签: javaspringspring-bootspring-batch

解决方案


推荐阅读