[spring-projects/spring-boot]application.properties 中的 spring.main.web-application-type 在 @SpringBootTest 中无效

2024-06-26 741 views
4

考虑到 RestTemplateAutoConfiguration 中的设计方式@Conditional(NotReactiveWebApplicationCondition.class),如果我们同时具有 spring-web 和 spring-webflux 依赖项,则条件计算结果为 false,并且 rest 模板未自动配置。这发生在我身上只是因为我正在导入包含 webflux 作为依赖项的 prometheus-rsocket-spring

以前的版本没有发生过这种情况,我刚刚更新到 2.6。我尝试通过 spring.main.web-application-type: servlet 禁用它,但它仍然没有配置它

回答

2

您好,我无法重现此问题。我已将spring-boot-starter-web和添加prometheus-rsocket-spring到我的 Spring Boot 2.6.6 应用程序中,并且RestTemplateBuilder仍处于自动配置状态。

如果您希望我们花一些时间进行调查,请花时间提供一个完整的最小样本(我们可以解压缩或 git 克隆、构建和部署的样本),以重现该问题。

3

非常感谢@mhalbritter,很抱歉占用了你的时间,我根据我的描述从零创建了一个项目,但事实上,我无法重现。但是:

我的应用程序是一个更复杂的大型应用程序,它是基于 SERVLET 的,而不是基于 REACTIVE(例如,使用 resttemplate 而不是 webclient),但它不是一个 Web 应用程序,因为它没有启动 Web 上下文(所以我将其定义为 spring.main.web-application-type:NONE)

我必须从示例中删除 spring-web-starter 并设置 spring.main.web-application-type=NONE/SERVLET,然后我才能重现它。问题不在于条件本身。发生此问题的原因是 spring.main.web-application-type 在 SpringBootTestContextBootstrapper.getWebApplicationType 中未正确解析,因此如果您没有某些 Web 依赖项但有 flux 依赖项,它会回退到 REACTIVE 标识。如果您进行了测试并将 spring-web-starter 替换为裸 spring-boot-starter,然后将 spring.main.web-application-type 设置为 NONE 或 Servlet,您将看到 resttemplatebuilder 如果需要则无法满足上下文要求,因为以下行无法绑定属性值,而是 null(它在 application.properties 中)

绑定器.bind(“spring.main.web-application-type”,Bindable.of(WebApplicationType.class))-> null

您希望我附加一个示例吗?或者,如果您已经这样做了,只需将 spring-web-starter 更改为 spring-boot-starter 并重试?

我正在使用 2.6.4 和 JDK17

9

听起来与#29170 和 #29695 有一些相似之处。

4

@wilkinsona 我正在寻找这个binder.bind("spring.main.web-application-type", Bindable.of(WebApplicationType.class)) -> null的问题,但令我惊讶的是,我什么也没找到。从我的无知来看,但作为如此低级的行,它完全看起来像是Bindable停止解析枚举。我在绑定循环中得到了2个DataObjectBinder,JavaBeanBinder和ValueObjectBinder,当尝试绑定应用程序时,它们都返回null

1

当包含时spring-boot-starter-web,它RestTemplateBuilder可以作为 bean 使用,并且自动配置报告如下所示:

   RestTemplateAutoConfiguration matched:
      - @ConditionalOnClass found required class 'org.springframework.web.client.RestTemplate' (OnClassCondition)
      - NoneNestedConditions 0 matched 1 did not; NestedCondition on RestTemplateAutoConfiguration.NotReactiveWebApplicationCondition.ReactiveWebApplication not a reactive web application (RestTemplateAutoConfiguration.NotReactiveWebApplicationCondition)

当不包括时spring-boot-starter-web,则RestTemplateBuilder不能作为 bean 使用,并且自动配置报告如下所示:

   RestTemplateAutoConfiguration:
      Did not match:
         - NoneNestedConditions 1 matched 0 did not; NestedCondition on RestTemplateAutoConfiguration.NotReactiveWebApplicationCondition.ReactiveWebApplication found ConfigurableReactiveWebEnvironment (RestTemplateAutoConfiguration.NotReactiveWebApplicationCondition)
      Matched:
         - @ConditionalOnClass found required class 'org.springframework.web.client.RestTemplate' (OnClassCondition)

RestTemplate在两种情况下都在类路径上,因为spring-boot-starter-webflux(由 拉入prometheus-rsocket-spring)对 有依赖性org.springframework:spring-web

starter-web在类路径上时,org.springframework.boot.autoconfigure.condition.OnWebApplicationCondition#isReactiveWebApplication返回,如果不在类路径上,match = false则返回。truestarter-web

也许应该OnWebApplicationCondition尊重的设置spring.main.web-application-type

8

嗯,已经可以了。当不包括时spring-boot-starter-web,我可以设置spring.main.web-application-type=NONE并获取RestTemplateBuilderbean。如果我将其设置为REACTIVE,则不会获取 bean。

@nightswimmings 您应该能够通过设置来解决您的问题spring.main.web-application-type=NONE,即使spring-boot-starter-webflux在类路径上,这也会自动为您配置RestTemplateBuilder(至少在非测试代码中)。

0

感谢您确认预期行为,@mhalbritter。我感觉这与 Java17 有关。您在测试中使用它吗?

8

好的,它与 JDK 无关(在 8、11、15、17 中测试过)。如果我将属性设置为 @SpringBootTest(property),而不是通过 .properties 或 .yml 将其设置为 src/main/resources 或 src/test/resources,我可以让它工作。我确保它不是 IDE 的东西,属性文件最终会出现在类/测试类中,并且 mvn verify 直接通过 CLI 失败。在 @SpringBootTest 或主代码类中注入时都会失败

8

感谢复现者。确实有一个 bug。

spring:
  main:
    web-application-type: none

通过 运行测试时,会忽略此 YAML 内容。YAML 是在还是@SpringBootTest中都无所谓。这就是为什么该方法运行没有问题,但测试却没有问题的原因。src/main/resourcessrc/test/resourcesmain

org.springframework.boot.autoconfigure.condition.OnWebApplicationCondition#isReactiveWebApplicationtrue如果这是在 YAML 中,则在测试中返回,因为context.getResourceLoader()它是org.springframework.boot.web.reactive.context.GenericReactiveWebApplicationContext

但是当使用时@SpringBootTest(properties = "spring.main.web-application-type=NONE"),在测试中org.springframework.boot.autoconfigure.condition.OnWebApplicationCondition#isReactiveWebApplication返回,就像。然后bean 可用。falsecontext.getResourceLoader()org.springframework.context.annotation.AnnotationConfigApplicationContextRestTemplateBuilder

这可能与https://github.com/spring-projects/spring-boot/issues/29170有关,但我不确定是否是同一个错误。@wilkinsona 你怎么看?

5

看起来 #29169 还没有修复 :/ 重现器使用的是最新版的 Spring Boot 2.6.6。我也可以在 2.5.12 中重现它。

5

嘿@nightswimmings,谢谢你的报告。我已编辑了你的问题标题并将其标记为错误。

9

看起来https://github.com/spring-projects/spring-boot/issues/29169还没有修复

看起来情况比这更微妙一些。29169的复现器在 2.5.8 及更早版本中失败,但在 2.5.9 及更高版本中成功。看起来我们成功修复了 #29169 中报告的特定问题,但还有一个更广泛的问题。我想这并不奇怪,因为我们还需要考虑https://github.com/spring-projects/spring-boot/issues/29170https://github.com/spring-projects/spring-boot/issues/29695 。

9

这与 #29170 的问题相同。问题在于,我们使用始终根据当时推断出的上下文SpringApplication类型ApplicationContextFactory返回特定类型的上下文来配置。如果随后通过配置属性绑定更改了 Web 应用程序类型,工厂将生成错误类型的上下文。我们在这里看到问题,上下文最终成为反应性 Web 上下文,从而阻止创建,而RestTemplateBuilder在 #29170 中,上下文最终成为 servlet Web 上下文,而它本应是反应性 Web 上下文。