繁体   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