[spring-projects/spring-boot]自 2.x 起,“classpath*:”对于配置属性上的 Resource[] 属性不起作用

2024-04-11 598 views
3

自 Spring Boot 2.x 以来,我在配置属性功能上遇到了意外行为。在 1.5.x 上,它工作得很好。

状况
  • Resource[]定义自定义配置属性类的属性
  • 使用前缀指定属性值classpath*:,例如classpath*:files/*.txt
预期结果
  • 绑定Resource与模式字符串匹配的多个实例
实际结果
  • 绑定一个包含指定模式字符串的单个Resource,例如classpath*:files/*.txt
重现测试

演示.zip

package com.example.demoresources;

import org.assertj.core.api.Assertions;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.core.io.Resource;
import org.springframework.test.context.junit4.SpringRunner;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

@RunWith(SpringRunner.class)
@SpringBootTest(properties = "my.files=classpath*:files/*.txt")
@EnableConfigurationProperties({DemoResourcesApplicationTests.MyProperties.class})
public class DemoResourcesApplicationTests {

  @Autowired
  MyProperties myProperties;

  @BeforeClass
  public static void setup() throws IOException {
    Path dir = Paths.get("target", "test-classes", "files");
    Files.createDirectories(dir);
    createFileIfNotExist(dir.resolve("a.txt"));
    createFileIfNotExist(dir.resolve("b.txt"));
  }

  private static void createFileIfNotExist(Path path) throws IOException {
    if (!path.toFile().exists()) {
      Files.createFile(path);
    }
  }

  @Test
  public void contextLoads() {
    Resource[] files = myProperties.getFiles();
    List<String> fileNames = Arrays.stream(files).map(Resource::getFilename).collect(Collectors.toList());
    Assertions.assertThat(fileNames)
        .hasSize(2)
        .contains("a.txt", "b.txt"); // Success with Spring Boot 1.5.x but fail with Spring Boot 2.x ...
  }

  @ConfigurationProperties(prefix = "my")
  public static class MyProperties {
    private Resource[] files = {};

    public void setFiles(Resource[] files) {
      this.files = files;
    }

    public Resource[] getFiles() {
      return files;
    }

  }

}

回答

7

感谢您提供样品。与 #12166 中描述的问题有一些相似之处。

在 1.5 中,转换由 Framework 的ResourceArrayPropertyEditor.在 2.x 中,转换由 Boot 的DelimitedStringToArrayConverter.ResourceArrayPropertyEditor已注册到 Boot TypeConverterConversionService,但TypeConverterConverter仅注册到单个可转换对,该可转换对与to转换的可转换对java.lang.String -> java.lang.Object不匹配。java.lang.String -> [Lorg.springframework.core.io.Resource;StringResource[]

ArrayBinderTests这是重现该问题的补充:

@Test
public void bindToResourceArrayShouldUsePropertyEditorAndPatternResolution() {
    MockConfigurationPropertySource source = new MockConfigurationPropertySource();
    source.put("foo", "classpath*:/**/*.class");
    this.sources.add(source);
    assertThat(this.binder.bind("foo", Bindable.of(Resource[].class)).get().length)
            .isGreaterThan(1);
}

绑定到集合也存在同样的问题。

1

我不完全确定我们应该尝试以与ResourceEditor.我对在绑定过程中执行这样的逻辑感到有点不安。我想知道在大多数情况下,将模式绑定为 aString然后使用它来对抗 aResourcePatternResolver可能是更好的方法吗?

0

我们将根据具体情况进行处理。我们现在仅添加资源数组和资源集合属性编辑器。

1

Spring 框架对于资源数组和集合的@Value行为是不一致的。在我们知道 Framework 可能做什么之前,我们应该推迟对 Boot 的更改。

3

谢谢,@filiphr。我们已经知道它Resource[]适用于@Value. Madhura 提出的框架问题注意到了这一点,并旨在解决它不能与Collection<Resource>.我们希望在决定如何在 Boot 中继续之前以某种方式解决这种不一致问题。

4

感谢您清理它@wilkinsona。我只是想从其他问题中获取更多信息(尽管我毫不怀疑您已经掌握了它?)。

1

框架问题已进入 5.x 待办事项中,因此我们不太可能在 2.1.x 甚至 2.2.x 中解决此问题。

3

你好,团队,关于这个问题有什么消息吗?

6

看起来Framework 6.1将会发生变化。我们应该看看我们可以在 Boot 3.2 中做什么(如果有的话)。

3

框架现在一致地将模式转换为多个资源,无论是 aResource[]还是Collection<Resource>.为了保持一致性,我们应该在 Boot 中做同样的事情来进行绑定。我们需要找出利用绑定器使用的转换服务来实现这一点的最佳方法。