Add ReloadableResourceBundleMessageSource support

Add a `spring.messages.reloadable` configuration property which can be
used to auto-configure a `ReloadableResourceBundleMessageSource` rather
than a `ResourceBundleMessageSource`.

Closes gh-13377
This commit is contained in:
Rui Figueira 2018-06-05 18:11:56 +01:00 committed by Phillip Webb
parent 8b59503291
commit 22abe35f95
4 changed files with 61 additions and 1 deletions

View File

@ -33,6 +33,8 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ConditionContext; import org.springframework.context.annotation.ConditionContext;
import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.AbstractResourceBasedMessageSource;
import org.springframework.context.support.ReloadableResourceBundleMessageSource;
import org.springframework.context.support.ResourceBundleMessageSource; import org.springframework.context.support.ResourceBundleMessageSource;
import org.springframework.core.Ordered; import org.springframework.core.Ordered;
import org.springframework.core.io.Resource; import org.springframework.core.io.Resource;
@ -65,7 +67,9 @@ public class MessageSourceAutoConfiguration {
@Bean @Bean
public MessageSource messageSource(MessageSourceProperties properties) { public MessageSource messageSource(MessageSourceProperties properties) {
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource(); AbstractResourceBasedMessageSource messageSource = (properties.isReloadable()
? new ReloadableResourceBundleMessageSource()
: new ResourceBundleMessageSource());
if (StringUtils.hasText(properties.getBasename())) { if (StringUtils.hasText(properties.getBasename())) {
messageSource.setBasenames(StringUtils.commaDelimitedListToStringArray( messageSource.setBasenames(StringUtils.commaDelimitedListToStringArray(
StringUtils.trimAllWhitespace(properties.getBasename()))); StringUtils.trimAllWhitespace(properties.getBasename())));

View File

@ -71,6 +71,12 @@ public class MessageSourceProperties {
*/ */
private boolean useCodeAsDefaultMessage = false; private boolean useCodeAsDefaultMessage = false;
/**
* Whether to use a "ReloadableResourceBundleMessageSource" rather than the default
* "ResourceBundleMessageSource". Recommended during development only.
*/
private boolean reloadable = false;
public String getBasename() { public String getBasename() {
return this.basename; return this.basename;
} }
@ -119,4 +125,12 @@ public class MessageSourceProperties {
this.useCodeAsDefaultMessage = useCodeAsDefaultMessage; this.useCodeAsDefaultMessage = useCodeAsDefaultMessage;
} }
public boolean isReloadable() {
return this.reloadable;
}
public void setReloadable(boolean reloadable) {
this.reloadable = reloadable;
}
} }

View File

@ -21,6 +21,7 @@ import java.util.Locale;
import org.junit.Ignore; import org.junit.Ignore;
import org.junit.Test; import org.junit.Test;
import org.springframework.beans.BeansException;
import org.springframework.beans.DirectFieldAccessor; import org.springframework.beans.DirectFieldAccessor;
import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.test.context.assertj.AssertableApplicationContext; import org.springframework.boot.test.context.assertj.AssertableApplicationContext;
@ -32,6 +33,9 @@ import org.springframework.context.NoSuchMessageException;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource; import org.springframework.context.annotation.PropertySource;
import org.springframework.context.support.DelegatingMessageSource;
import org.springframework.context.support.ReloadableResourceBundleMessageSource;
import org.springframework.context.support.ResourceBundleMessageSource;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
@ -220,6 +224,43 @@ public class MessageSourceAutoConfigurationTests {
.isEqualTo("bar"))); .isEqualTo("bar")));
} }
@Test
public void testDefaultReloadableValueMessageSource() {
this.contextRunner.withPropertyValues("spring.messages.basename:test/messages")
.run((context) -> assertThat(getDeclaredMessageSource(context))
.isInstanceOf(ResourceBundleMessageSource.class));
}
@Test
public void testNotReloadableMessageSource() {
this.contextRunner
.withPropertyValues("spring.messages.basename:test/messages",
"spring.messages.reloadable:false")
.run((context) -> assertThat(getDeclaredMessageSource(context))
.isInstanceOf(ResourceBundleMessageSource.class));
}
@Test
public void testReloadableMessageSource() {
this.contextRunner.withPropertyValues("spring.messages.basename:test/messages",
"spring.messages.reloadable:true").run((context) -> {
assertThat(getDeclaredMessageSource(context))
.isInstanceOf(ReloadableResourceBundleMessageSource.class);
assertThat(context.getMessage("foo", null, "Foo message", Locale.UK))
.isEqualTo("bar");
});
}
private MessageSource getDeclaredMessageSource(AssertableApplicationContext context)
throws BeansException {
MessageSource messageSource = context.getBean(MessageSource.class);
if (messageSource instanceof DelegatingMessageSource) {
messageSource = ((DelegatingMessageSource) messageSource)
.getParentMessageSource();
}
return messageSource;
}
@Configuration @Configuration
@PropertySource("classpath:/switch-messages.properties") @PropertySource("classpath:/switch-messages.properties")
protected static class Config { protected static class Config {

View File

@ -133,6 +133,7 @@ content into your application. Rather, pick only the properties that you need.
spring.messages.cache-duration= # Loaded resource bundle files cache duration. When not set, bundles are cached forever. If a duration suffix is not specified, seconds will be used. spring.messages.cache-duration= # Loaded resource bundle files cache duration. When not set, bundles are cached forever. If a duration suffix is not specified, seconds will be used.
spring.messages.encoding=UTF-8 # Message bundles encoding. spring.messages.encoding=UTF-8 # Message bundles encoding.
spring.messages.fallback-to-system-locale=true # Whether to fall back to the system Locale if no files for a specific Locale have been found. spring.messages.fallback-to-system-locale=true # Whether to fall back to the system Locale if no files for a specific Locale have been found.
spring.messages.reloadable=false # Whether to use a ReloadableResourceBundleMessageSource instead of the default ResourceBundleMessageSource. Recommended during development only.
spring.messages.use-code-as-default-message=false # Whether to use the message code as the default message instead of throwing a "NoSuchMessageException". Recommended during development only. spring.messages.use-code-as-default-message=false # Whether to use the message code as the default message instead of throwing a "NoSuchMessageException". Recommended during development only.
# OUTPUT # OUTPUT