[spring-projects/spring-boot]在切片测试中禁用@ConfigurationProperties扫描

2024-04-11 335 views
6

我发现版本 2.2.0.M2 出现了回归。

我有一个使用 @DataJpaTest 注释的 dao 测试,如下所示:

@DataJpaTest
@RunWith(SpringRunner.class)
@Sql(executionPhase = BEFORE_TEST_METHOD, scripts = "classpath:beforeTestDaoITest.sql")
@Sql(executionPhase = AFTER_TEST_METHOD, scripts = "classpath:afterTestDaoITest.sql")
public class TestDaoITest {
  @Autowired
  private TestDao underTest;

  @Test
  public void whenFindOne_givenExistingId_thenId() {
    Optional<TestEntity> result = this.underTest.findById(1L);

    assertEquals(result.get().id, (Long) 1L);
  }
}

在项目中,我有一个使用@ConfigurationProperties的配置类,它看起来像这样:

@Configuration
@ConfigurationProperties(prefix = "monitoring")
public class Config {
  private Long id;

  @Bean
  public HealthIndicator healthIndicator(HealthAggregator healthAggregator) {
    HealthIndicatorRegistry healthIndicatorRegistry = new DefaultHealthIndicatorRegistry();
    return new CompositeHealthIndicator(healthAggregator, healthIndicatorRegistry);
  }

  public Long getId() {
    return this.id;
  }

  public void setId(Long id) {
    this.id = id;
  }
}

使用版本 2.1.4.RELEASE,一切正常。因为它是 DataJpaTest,所以不会扫描配置并且测试通过。

在版本 2.2.0.M2 中,扫描配置类并在测试中发生错误,因为不存在 HealthAggregator bean。我认为原因来自@ConfigurationProperties,当我删除@ConfigurationProperties注释时,不再扫描配置类

我这里有一个重现问题的工作示例:https://github.com/ebussieres/spring-boot-2.2-issue

回答

7

感谢您提供示例并尝试 2.2 里程碑。这个问题是新的配置属性扫描支持的一个有趣的副作用。

建议使用注释的类@ConfigurationProperties是 POJO,并且不提供任何其他功能。它们也不应该是一个@Configuration类或一个@RestController例子。您的代码不遵循此建议,这就是发生故障的原因。

我认为不可能使配置属性扫描了解测试切片,因为没有明显的方法可以知道@ConfigurationProperties应用程序的特定切片中需要哪些类。让我们看看还有什么我们可以做的吗?

9

我现在会检查以更改我们的配置类,我不知道该建议。感谢您的快速答复。

2

@snicoll 指出,在 2.1 中,不会创建@ConfigurationPropertiesScan任何beans,直到通过主类@ConfigurationProperties启用它们为止。@EnableConfigurationProperties如果我们在运行切片测试时不进行任何配置属性扫描,我们将恢复与 2.1 中相同的行为。

5

@mbhave 指出我们忽略了@EnableConfigurationProperties2.1 中主类中的场景,然后在 2.2 中被删除以支持扫描。在这种情况下,配置属性扫描的当前行为与 2.1 匹配,禁用它会产生问题。

2

在这种情况下,配置属性扫描的当前行为与 2.1 匹配,禁用它会产生问题。

我可能会遗漏一些东西,但我不认为这是一个问题。为此,用户必须更改代码中的某些内容(依赖类路径扫描与常规注册)。如果他们这样做了,在我看来,@ConfigurationProperties豆子消失是公平的游戏。如果他们不改变任何东西,测试将像以前一样工作。

我知道这个类比不是 100% 正确,但如果你将@Bean主类上的方法移动到@Component目标对象上,它也会消失。如果您需要获取这些@ConfigurationPropertiesbean,则需要一种方法来指示它们,目前,我认为没有一个好的用例可以在切片测试中扫描它们。

3

如果他们这样做,在我看来,@ConfigurationProperties bean 消失是公平的游戏

我担心的是,它可能会导致测试切片更难使用,从而削弱了转向配置属性扫描的好处。删除@EnableConfiguraionProperties主代码中的一个注释,然后只需将其添加到测试代码中,似乎有点鱼龙混杂。从好的方面来说,它确实提供了更多的控制。

8

使测试切片更难使用。

这与必须模拟您想要测试的组件确切需要的 bean 有什么不同?切片测试以减少的类路径运行,所以我真的不明白为什么我们应该考虑@ConfigurationProperties类型。

2

它的不同纯粹是因为这是你以前不必做的事情。出于这个原因,我认为这有点削弱了转向配置属性扫描的好处。

0

我们将研究元注释,@ConfigurationProperties以便@Component通过通常的组件扫描来获取它们(我们需要弄清楚如何自定义 bean 名称)。同时,我们认为@ConfigurationProperties在切片测试中禁用 bean 扫描是有意义的,就像它们是@Components 一样。

9

我认为由于此增强,构建目前失败。

***************************
APPLICATION FAILED TO START
***************************

Description:

Parameter 0 of constructor in sample.test.service.RemoteVehicleDetailsService required a bean of type 'sample.test.service.ServiceProperties' that could not be found.

Action:

Consider defining a bean of type 'sample.test.service.ServiceProperties' in your configuration.

2019-05-10 22:32:52.629 ERROR 5583 --- [           main] o.s.test.context.TestContextManager      : Caught exception while allowing TestExecutionListener [org.springframework.boot.test.autoconfigure.SpringBootDependencyInjectionTestExecutionListener@5acf93bb] to prepare test instance [sample.test.service.RemoteVehicleDetailsServiceTests@12ffd1de]

java.lang.IllegalStateException: Failed to load ApplicationContext
    at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:132) ~[spring-test-5.2.0.M2.jar:5.2.0.M2]
    at org.springframework.test.context.support.DefaultTestContext.getApplicationContext(DefaultTestContext.java:122) ~[spring-test-5.2.0.M2.jar:5.2.0.M2]
    at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.injectDependencies(DependencyInjectionTestExecutionListener.java:118) ~[spring-test-5.2.0.M2.jar:5.2.0.M2]
    at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.prepareTestInstance(DependencyInjectionTestExecutionListener.java:83) ~[spring-test-5.2.0.M2.jar:5.2.0.M2]
    at org.springframework.boot.test.autoconfigure.SpringBootDependencyInjectionTestExecutionListener.prepareTestInstance(SpringBootDependencyInjectionTestExecutionListener.java:44) ~[spring-boot-test-autoconfigure-2.2.0.BUILD-SNAPSHOT.jar:2.2.0.BUILD-SNAPSHOT]
    at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:244) ~[spring-test-5.2.0.M2.jar:5.2.0.M2]
    at org.springframework.test.context.junit.jupiter.SpringExtension.postProcessTestInstance(SpringExtension.java:98) [spring-test-5.2.0.M2.jar:5.2.0.M2]
    at org.junit.jupiter.engine.descriptor.ClassTestDescriptor.lambda$invokeTestInstancePostProcessors$6(ClassTestDescriptor.java:352) [junit-jupiter-engine-5.4.2.jar:5.4.2]
    at org.junit.jupiter.engine.descriptor.JupiterTestDescriptor.executeAndMaskThrowable(JupiterTestDescriptor.java:204) ~[junit-jupiter-engine-5.4.2.jar:5.4.2]
    at org.junit.jupiter.engine.descriptor.ClassTestDescriptor.lambda$invokeTestInstancePostProcessors$7(ClassTestDescriptor.java:352) [junit-jupiter-engine-5.4.2.jar:5.4.2]
    at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193) ~[na:1.8.0_202]
    at java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:175) ~[na:1.8.0_202]
    at java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1382) ~[na:1.8.0_202]
    at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481) ~[na:1.8.0_202]
    at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471) ~[na:1.8.0_202]
    at java.util.stream.StreamSpliterators$WrappingSpliterator.forEachRemaining(StreamSpliterators.java:312) ~[na:1.8.0_202]
    at java.util.stream.Streams$ConcatSpliterator.forEachRemaining(Streams.java:743) ~[na:1.8.0_202]
    at java.util.stream.Streams$ConcatSpliterator.forEachRemaining(Streams.java:742) ~[na:1.8.0_202]
    at java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:580) ~[na:1.8.0_202]
    at org.junit.jupiter.engine.descriptor.ClassTestDescriptor.invokeTestInstancePostProcessors(ClassTestDescriptor.java:351) [junit-jupiter-engine-5.4.2.jar:5.4.2]
    at org.junit.jupiter.engine.descriptor.ClassTestDescriptor.instantiateAndPostProcessTestInstance(ClassTestDescriptor.java:270) [junit-jupiter-engine-5.4.2.jar:5.4.2]
    at org.junit.jupiter.engine.descriptor.ClassTestDescriptor.lambda$testInstancesProvider$2(ClassTestDescriptor.java:259) [junit-jupiter-engine-5.4.2.jar:5.4.2]
    at org.junit.jupiter.engine.descriptor.ClassTestDescriptor.lambda$testInstancesProvider$3(ClassTestDescriptor.java:263) [junit-jupiter-engine-5.4.2.jar:5.4.2]
    at java.util.Optional.orElseGet(Optional.java:267) ~[na:1.8.0_202]
    at org.junit.jupiter.engine.descriptor.ClassTestDescriptor.lambda$testInstancesProvider$4(ClassTestDescriptor.java:262) [junit-jupiter-engine-5.4.2.jar:5.4.2]
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$prepare$0(TestMethodTestDescriptor.java:98) ~[junit-jupiter-engine-5.4.2.jar:5.4.2]
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) ~[junit-platform-engine-1.4.2.jar:1.4.2]
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.prepare(TestMethodTestDescriptor.java:97) ~[junit-jupiter-engine-5.4.2.jar:5.4.2]
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.prepare(TestMethodTestDescriptor.java:68) ~[junit-jupiter-engine-5.4.2.jar:5.4.2]
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$prepare$1(NodeTestTask.java:107) ~[junit-platform-engine-1.4.2.jar:1.4.2]
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) ~[junit-platform-engine-1.4.2.jar:1.4.2]
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.prepare(NodeTestTask.java:107) ~[junit-platform-engine-1.4.2.jar:1.4.2]
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:75) ~[junit-platform-engine-1.4.2.jar:1.4.2]
    at java.util.ArrayList.forEach(ArrayList.java:1257) ~[na:1.8.0_202]
    at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38) ~[junit-platform-engine-1.4.2.jar:1.4.2]
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:139) ~[junit-platform-engine-1.4.2.jar:1.4.2]
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) ~[junit-platform-engine-1.4.2.jar:1.4.2]
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:125) ~[junit-platform-engine-1.4.2.jar:1.4.2]
    at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:135) ~[junit-platform-engine-1.4.2.jar:1.4.2]
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:123) ~[junit-platform-engine-1.4.2.jar:1.4.2]
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) ~[junit-platform-engine-1.4.2.jar:1.4.2]
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:122) ~[junit-platform-engine-1.4.2.jar:1.4.2]
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:80) ~[junit-platform-engine-1.4.2.jar:1.4.2]
    at java.util.ArrayList.forEach(ArrayList.java:1257) ~[na:1.8.0_202]
    at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38) ~[junit-platform-engine-1.4.2.jar:1.4.2]
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:139) ~[junit-platform-engine-1.4.2.jar:1.4.2]
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) ~[junit-platform-engine-1.4.2.jar:1.4.2]
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:125) ~[junit-platform-engine-1.4.2.jar:1.4.2]
    at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:135) ~[junit-platform-engine-1.4.2.jar:1.4.2]
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:123) ~[junit-platform-engine-1.4.2.jar:1.4.2]
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) ~[junit-platform-engine-1.4.2.jar:1.4.2]
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:122) ~[junit-platform-engine-1.4.2.jar:1.4.2]
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:80) ~[junit-platform-engine-1.4.2.jar:1.4.2]
    at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:32) ~[junit-platform-engine-1.4.2.jar:1.4.2]
    at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57) ~[junit-platform-engine-1.4.2.jar:1.4.2]
    at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:51) ~[junit-platform-engine-1.4.2.jar:1.4.2]
    at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:220) ~[junit-platform-launcher-1.3.1.jar:1.3.1]
    at org.junit.platform.launcher.core.DefaultLauncher.lambda$execute$6(DefaultLauncher.java:188) ~[junit-platform-launcher-1.3.1.jar:1.3.1]
    at org.junit.platform.launcher.core.DefaultLauncher.withInterceptedStreams(DefaultLauncher.java:202) ~[junit-platform-launcher-1.3.1.jar:1.3.1]
    at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:181) ~[junit-platform-launcher-1.3.1.jar:1.3.1]
    at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:128) ~[junit-platform-launcher-1.3.1.jar:1.3.1]
    at org.apache.maven.surefire.junitplatform.JUnitPlatformProvider.invokeAllTests(JUnitPlatformProvider.java:150) ~[surefire-junit-platform-2.22.2.jar:2.22.2]
    at org.apache.maven.surefire.junitplatform.JUnitPlatformProvider.invoke(JUnitPlatformProvider.java:124) ~[surefire-junit-platform-2.22.2.jar:2.22.2]
    at org.apache.maven.surefire.booter.ForkedBooter.invokeProviderInSameClassLoader(ForkedBooter.java:384) ~[surefire-booter-2.22.2.jar:2.22.2]
    at org.apache.maven.surefire.booter.ForkedBooter.runSuitesInProcess(ForkedBooter.java:345) ~[surefire-booter-2.22.2.jar:2.22.2]
    at org.apache.maven.surefire.booter.ForkedBooter.execute(ForkedBooter.java:126) ~[surefire-booter-2.22.2.jar:2.22.2]
    at org.apache.maven.surefire.booter.ForkedBooter.main(ForkedBooter.java:418) ~[surefire-booter-2.22.2.jar:2.22.2]
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'remoteVehicleDetailsService' defined in file [/tmp/build/fd4135ff/git-repo/spring-boot-samples/spring-boot-sample-test/target/classes/sample/test/service/RemoteVehicleDetailsService.class]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'sample.test.service.ServiceProperties' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
    at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:780) ~[spring-beans-5.2.0.M2.jar:5.2.0.M2]
    at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:219) ~[spring-beans-5.2.0.M2.jar:5.2.0.M2]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1345) ~[spring-beans-5.2.0.M2.jar:5.2.0.M2]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1191) ~[spring-beans-5.2.0.M2.jar:5.2.0.M2]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:555) ~[spring-beans-5.2.0.M2.jar:5.2.0.M2]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:515) ~[spring-beans-5.2.0.M2.jar:5.2.0.M2]
    at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:320) ~[spring-beans-5.2.0.M2.jar:5.2.0.M2]
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222) ~[spring-beans-5.2.0.M2.jar:5.2.0.M2]
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:318) ~[spring-beans-5.2.0.M2.jar:5.2.0.M2]
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) ~[spring-beans-5.2.0.M2.jar:5.2.0.M2]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:868) ~[spring-beans-5.2.0.M2.jar:5.2.0.M2]
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:877) ~[spring-context-5.2.0.M2.jar:5.2.0.M2]
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:549) ~[spring-context-5.2.0.M2.jar:5.2.0.M2]
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:782) ~[spring-boot-2.2.0.BUILD-SNAPSHOT.jar:2.2.0.BUILD-SNAPSHOT]
    at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:404) ~[spring-boot-2.2.0.BUILD-SNAPSHOT.jar:2.2.0.BUILD-SNAPSHOT]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:319) ~[spring-boot-2.2.0.BUILD-SNAPSHOT.jar:2.2.0.BUILD-SNAPSHOT]
    at org.springframework.boot.test.context.SpringBootContextLoader.loadContext(SpringBootContextLoader.java:132) ~[spring-boot-test-2.2.0.BUILD-SNAPSHOT.jar:2.2.0.BUILD-SNAPSHOT]
    at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContextInternal(DefaultCacheAwareContextLoaderDelegate.java:99) ~[spring-test-5.2.0.M2.jar:5.2.0.M2]
    at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:124) ~[spring-test-5.2.0.M2.jar:5.2.0.M2]
    ... 66 common frames omitted
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'sample.test.service.ServiceProperties' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoMatchingBeanFound(DefaultListableBeanFactory.java:1681) ~[spring-beans-5.2.0.M2.jar:5.2.0.M2]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1239) ~[spring-beans-5.2.0.M2.jar:5.2.0.M2]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1193) ~[spring-beans-5.2.0.M2.jar:5.2.0.M2]
    at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:868) ~[spring-beans-5.2.0.M2.jar:5.2.0.M2]
    at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:771) ~[spring-beans-5.2.0.M2.jar:5.2.0.M2]
    ... 84 common frames omitted

在我们的示例中,我们将配置属性类声明为具有@RestClientTest.请参阅https://github.com/spring-projects/spring-boot/blob/3b4ff7d74641bd453554df22bc4eaa55da4522d4/spring-boot-samples/spring-boot-sample-test/src/test/java/sample/test/service/RemoteVehicleDetailsS ​​erviceTests.java #L43

将其更改为以下内容可以修复测试:

@RestClientTest(RemoteVehicleDetailsService.class)
@EnableConfigurationProperties(ServiceProperties.class)
class RemoteVehicleDetailsServiceTests {

想知道这是否是预期的结果,或者我们是否想重新讨论这个问题。

3

谢谢@bclozel。我认为我没有考虑过这种情况,即@ConfigurationProperties之前标记为 的内容@Component通过显式地将它们添加为包含在TypeExcludeFilter.

此提交完全禁用@ConfigurationPropertiesScan。相反,我们可能需要做一些类似于@ComponentScan过滤器的事情。我将恢复提交,这样它就不会阻止构建。

1

相反,我们可能需要做一些类似于@ComponentScan过滤器的事情

我不确定我们是否需要那个。从我的角度来看,@Import(MyProperties.class)应该“只是工作”。这与组件扫描无关,是吗?上面的示例与显式列出类的方向相同,并且在显式定义此类时应启用配置道具(env 绑定)的处理。

5

我们可能应该在某个时候将其移至其自己的问题中,但我们已经讨论了删除自定义组件扫描代码以依赖框架的标准行为并进行一些额外调整的可能性。我已经打开https://github.com/spring-projects/spring-framework/issues/22995来查看框架团队对用例的看法,我们将在此进行相应的跟进。

7

与@jhoeller 讨论,基本上有两种选择(具有不同的变体):

  1. 打开核心容器,以便我们可以将我们的自定义@ConfigurationPropertiesScan@ComponentScan
  2. 修复我们的@ConfigurationPropertiesScan问题,使其了解切片测试并执行正确的操作

我们花了一些时间讨论第一个选项,因为它对我来说看起来是最好的结果,避免了额外的包扫描并确保事情在一个地方协调一致。我们考虑的一种选择是include我们的@ComponentScan过滤器中包含一个过滤器,以确保@ConfigurationProperties包含类型,然后在上下文上提供一个额外的挂钩点来注册特殊的回调。

该回调的目的是能够调整/更改BeanDefinition与扫描匹配的每个组件。这将是我们为基于构造函数的绑定注入特殊处理的机会。查看当前的层次结构,我们需要在 级别添加一些内容,AnnotationConfigRegistry因为我们需要为能够识别扫描的上下文执行此操作。目前,组件扫描处理是相当独立的,因此为该用例开放它将是一个挑战。

另一种选择是修复我们必须包含的内容,TypeExcludeFilter以便将其应用到那里。现在我将重点讨论这个想法,看看是否可以解决这里暴露的问题。这将导致过滤器的两次调用,但我们的自定义组件扫描目前也执行此操作。如果这个解决方案有效,我们可以看看是否有办法缓解这种情况。

6

自此问题创建以来,有一些事情发生了变化。首先,当我们尝试使用以下命令构建此项目时,现在有以下报告2.2.0.M4

***************************
APPLICATION FAILED TO START
***************************

Description:

com.example.demo.Config is annotated with @ConfigurationProperties and @Configuration. This may cause the @ConfigurationProperties bean to be registered twice.

Action:

Remove @Configuration from com.example.demo.Config or consider disabling automatic @ConfigurationProperties scanning.

我认为这将缓解这里暴露的问题。然而,@ConfigurationProperties切片测试中扫描的合法性问题仍然存在。

TypeExcludeFilter当我们扫描他们时进行注册会导致他们被排除在外。如果我们决定切片应该包含它们,那么更新过滤器就很容易做到这一点。该项目构建干净。RemoteVehicleDetailsServiceTests即使对于基于构造的 binidng,这同样适用于(显式导入)。最后一点在这一点上有点神秘,所以我需要进一步调查。