如何在 MultiResourceItemReader Spring 批处理中解决 ClassCastException

How to resolve ClassCastException in MultiResourceItemReader Spring Batch

我正在使用 MultiResourceItemReader 从 S3 存储桶中读取多个文件,在执行 myReader() 方法之前我得到 ClassCastException MultiResourceItemReader 不对确定这里出了什么问题。

请在下面找到我的代码:

                @Bean
                public MultiResourceItemReader<String> multiResourceReader()
                {
                    String bucket = "mybucket;
                    String key = "/myfiles";
                
                    List<InputStream> resourceList = s3Client.getFiles(bucket, key);
                    List<InputStreamResource> inputStreamResourceList = new ArrayList<>();
                    for (InputStream s: resourceList) {
                        inputStreamResourceList.add(new InputStreamResource(s));
                    }
            
            Resource[] resources = inputStreamResourceList.toArray(new InputStreamResource[inputStreamResourceList.size()]);
        //InputStreamResource[] resources = inputStreamResourceList.toArray(new InputStreamResource[inputStreamResourceList.size()]);
            
            // I'm getting all the stream content - I verified my stream is not null
                    for (int i = 0; i < resources.length; i++) {
                        try {
                            InputStream s  = resources[i].getInputStream();
                            String result = IOUtils.toString(s, StandardCharsets.UTF_8);
                            System.out.println(result);
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
            
                    MultiResourceItemReader<String> resourceItemReader = new MultiResourceItemReader<>();
                    resourceItemReader.setResources(resources);
                    resourceItemReader.setDelegate(myReader());
                    
    resourceItemReader.setDelegate((ResourceAwareItemReaderItemStream<? extends String>) new CustomComparator()); 
                    return resourceItemReader;
                }
        
        
          

异常:

Caused by: java.lang.ClassCastException: class CustomComparator cannot be cast to class org.springframework.batch.item.file.ResourceAwareItemReaderItemStream (CustomComparator and org.springframework.batch.item.file.ResourceAwareItemReaderItemStream are in unnamed module of loader org.springframework.boot.loader.LaunchedURLClassLoader @cc285f4)
        at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:244)
        at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:331)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.base/java.lang.reflect.Method.invoke(Method.java:566)
        at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:154)
        ... 65 common frames omitted

有人可以帮我解决这个问题吗?提前感谢您的帮助。谢谢。

您看到 NullPointerException 的原因是 MultiResourceItemReader 在加载资源后使用默认比较器对资源进行排序。

默认比较行为调用 InputStreamResource 的 getFilename() 方法。

参考 - https://github.com/spring-projects/spring-batch/blob/115c3022147692155d45e23cdd5cef84895bf9f5/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/MultiResourceItemReader.java#L82

但是 InputStreamResource 只是从其父 AbstractResource 继承了 getFileName() 方法,它只是 returns null。 https://github.com/spring-projects/spring-framework/blob/316e84f04f3dbec3ea5ab8563cc920fb21f49749/spring-core/src/main/java/org/springframework/core/io/AbstractResource.java#L220

解决方案是为 MultiResourceItemReader 提供自定义比较器。下面是一个简单的例子,假设您不想在处理之前以特定方式对资源进行排序:

public class CustomComparator implements Comparator<InputStream>{

        @Override
        public int compare(InputStream is1, InputStream is2) {
       //comparing based on last modified time
            return Long.compare(is1.hashCode(),is2.hashCode());
   }
}

MultiResourceItemReader<String> resourceItemReader = new MultiResourceItemReader<>();
resourceItemReader.setResources(resources);
resourceItemReader.setDelegate(myReader());
//UPDATED with correction - set custom Comparator
resourceItemReader.setComparator(new CustomComparator());

请参阅此答案以了解 Spring Batch MultiResourceItemReader 如何使用 Comparator。