簡體   English   中英

SpringBootServletInitializer 和 @ConfigurationProperties 不適用於 .war 部署的根屬性

[英]SpringBootServletInitializer and @ConfigurationProperties not working with root properties on .war deployments

我正在使用 Spring Boot 2.0.0.RC1 來構建我的 REST 服務。 為了提供 jar 執行和 .war 部署,我像這樣擴展SpringBootServletInitializer

@Configuration
@SpringBootApplication
@EnableWebFlux
@EnableConfigurationProperties({ RbsConfiguration.class, 
JwtConfiguration.class })
public class RbsApplication extends SpringBootServletInitializer 
implements WebFluxConfigurer {

    public static void main(String[] args) throws Exception {
        SpringApplication.run(RbsApplication.class, args);
    }

    ...
}

我還使用@ConfigurationProperties將我的application.yml配置綁定到這樣的 bean:

@ConfigurationProperties
@Validated
public class RbsConfiguration {

    private Map<String, String> users;

    @NotEmpty
    public Map<String, String> getUsers() {
        return users;
    }

    public void setUsers(Map<String, String> users) {
        this.users = users;
    }
}

使用這個application.yml

users:
  user1:
    password: secret

當我使用java -jar啟動應用程序時,一切都按預期工作,我可以通過RbsConfiguration訪問用戶。 但是,如果我將其作為 .war 部署到 Tomcat,則會出現以下異常:

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

Description:

Failed to bind properties under '' to foo.bar.RbsConfiguration:

    Reason: PropertyName must not be empty

Action:

Update your application's configuration

...

Caused by: org.springframework.boot.context.properties.bind.BindException: Failed to bind properties under '' to foo.bar.RbsConfiguration
    at org.springframework.boot.context.properties.bind.Binder.handleBindError(Binder.java:227)
    at org.springframework.boot.context.properties.bind.Binder.bind(Binder.java:203)
    at org.springframework.boot.context.properties.bind.Binder.bind(Binder.java:187)
    at org.springframework.boot.context.properties.bind.Binder.bind(Binder.java:169)
    at org.springframework.boot.context.properties.ConfigurationPropertiesBinder.bind(ConfigurationPropertiesBinder.java:79)
    at org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor.postProcessBeforeInitialization(ConfigurationPropertiesBindingPostProcessor.java:167)
    ... 100 more
Caused by: java.lang.IllegalArgumentException: PropertyName must not be empty
    at org.springframework.util.Assert.hasLength(Assert.java:233)
    at org.springframework.boot.origin.PropertySourceOrigin.<init>(PropertySourceOrigin.java:41)
    at org.springframework.boot.origin.PropertySourceOrigin.get(PropertySourceOrigin.java:79)
    at org.springframework.boot.context.properties.source.SpringConfigurationPropertySource.find(SpringConfigurationPropertySource.java:121)
    at org.springframework.boot.context.properties.source.SpringConfigurationPropertySource.find(SpringConfigurationPropertySource.java:104)
    at org.springframework.boot.context.properties.source.SpringConfigurationPropertySource.getConfigurationProperty(SpringConfigurationPropertySource.java:86)
    at org.springframework.boot.context.properties.bind.Binder.lambda$findProperty$3(Binder.java:294)
    at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193)
    at java.util.Spliterators$IteratorSpliterator.tryAdvance(Spliterators.java:1812)
    at java.util.stream.ReferencePipeline.forEachWithCancel(ReferencePipeline.java:126)
    at java.util.stream.AbstractPipeline.copyIntoWithCancel(AbstractPipeline.java:498)
    at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:485)
    at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471)
    at java.util.stream.FindOps$FindOp.evaluateSequential(FindOps.java:152)
    at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
    at java.util.stream.ReferencePipeline.findFirst(ReferencePipeline.java:464)
    at org.springframework.boot.context.properties.bind.Binder.findProperty(Binder.java:295)
    at org.springframework.boot.context.properties.bind.Binder.bindObject(Binder.java:239)
    at org.springframework.boot.context.properties.bind.Binder.bind(Binder.java:198)
    ... 104 more

所以我想知道這里有什么區別。 似乎當作為 .war 啟動時,它需要一個前綴,當直接通過 Spring Boot 啟動時,可以不加前綴。 除了RbsConfiguration我還有其他配置類(例如JwtConfiguration ),它們使用前綴並且似乎與 .war 部署一起工作正常。

任何提示為什么我會看到這種行為?

擴展SpringBootServletInitializer最終對我不起作用。 它沒有正確綁定根屬性(我認為它甚至沒有加載application.yml )並且它以某種方式忽略了我的 Spring Security 設置並提出了自己的默認設置。

對我RbsApplication是刪除RbsApplicationextends ,並簡單地自己提供一個 Webflux 初始值設定項,手動設置 SpringBoot 應用程序 - 深受SpringBootServletInitializer啟發。

public class WebfluxInitializer extends AbstractReactiveWebInitializer {

    private ServletContext servletContext;

    @Override
    public void onStartup(ServletContext servletContext) throws ServletException {
        this.servletContext = servletContext;
        super.onStartup(servletContext);
    }

    @Override
    protected ApplicationContext createApplicationContext() {

        SpringApplicationBuilder builder = new SpringApplicationBuilder();

        StandardServletEnvironment environment = new StandardServletEnvironment();
        environment.initPropertySources(servletContext, null);

        builder.environment(environment);
        builder.sources(getConfigClasses());
        builder.web(WebApplicationType.NONE);
        builder.main(RbsApplication.class);

        return builder.build().run();
    }

    @Override
    protected Class<?>[] getConfigClasses() {
        return new Class[] { RbsApplication.class };
    }
}

有了這個 Webflux 和安全工作定義和綁定根屬性到RbsConfiguration也在.war部署中工作。

我希望這可以幫助任何有類似問題的人(提供 .war 混合文件並嘗試正確綁定根屬性)。

如果有人找到了更簡單的方法來完成這項工作,我將不勝感激!

您需要在@ConfigurationProperties定義中提供前綴。 否則,您希望事情如何運作? 一切都應該映射到您的配置類,包括spring.*命名空間?

我猜您可能會看到這些部署之間的差異可能與啟動應用程序時環境中存在的值有關。

使用 WAR 模式時,您可以從 application.yaml 加載屬性:

@Bean
public PropertySourcesPlaceholderConfigurer properties() {
    PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer = new PropertySourcesPlaceholderConfigurer();
    propertySourcesPlaceholderConfigurer.setIgnoreResourceNotFound(true);
    YamlPropertiesFactoryBean yaml = new YamlPropertiesFactoryBean();
    List<Resource> resources = new ArrayList<>();
    resources.add(new ClassPathResource("application.yaml"));
    yaml.setResources(resources.toArray(new Resource[0]));
    propertySourcesPlaceholderConfigurer.setProperties(yaml.getObject());
    return propertySourcesPlaceholderConfigurer;
}

SpringBoot 對於 WebFlux 堆棧開箱即用不支持 WAR 模式。 使用上述方法,您可以獲得並能夠使用 @EnableConfigurationProperties 注釋。 但它仍然是一個黑客。
關鍵是這個 bean 創建得太晚了,因此許多有據可查的特性 spring boot 特性不起作用。 例如屬性加密(jasypt)或條件bean加載等。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM