我正在使用 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
配置绑定(bind)到这样的 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
最终对我来说没有成功。它没有正确绑定(bind)根属性(我认为它甚至没有加载 application.yml
),并且它以某种方式忽略了我的 Spring Security 设置并提出了自己的默认设置。
对我有用的是删除 RbsApplication
中的 extends
并简单地自己提供一个 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 和安全性按照定义工作,并且将根属性绑定(bind)到 RbsConfiguration
也可以在 .war
部署中工作。
我希望这可以帮助任何遇到类似问题的人(提供 .war 混合并尝试正确绑定(bind)根属性)。
如果有人找到更简单的方法来完成此任务,我将不胜感激!
关于java - SpringBootServletInitializer 和 @ConfigurationProperties 不适用于 .war 部署上的根属性,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48830050/