diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/context/MessageSourceAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/context/MessageSourceAutoConfiguration.java index ec87833c6f..b853baf73a 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/context/MessageSourceAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/context/MessageSourceAutoConfiguration.java @@ -33,6 +33,8 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ConditionContext; import org.springframework.context.annotation.Conditional; 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.core.Ordered; import org.springframework.core.io.Resource; @@ -65,7 +67,9 @@ public class MessageSourceAutoConfiguration { @Bean public MessageSource messageSource(MessageSourceProperties properties) { - ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource(); + AbstractResourceBasedMessageSource messageSource = (properties.isReloadable() + ? new ReloadableResourceBundleMessageSource() + : new ResourceBundleMessageSource()); if (StringUtils.hasText(properties.getBasename())) { messageSource.setBasenames(StringUtils.commaDelimitedListToStringArray( StringUtils.trimAllWhitespace(properties.getBasename()))); diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/context/MessageSourceProperties.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/context/MessageSourceProperties.java index 62d366c2cd..b8985ea8ec 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/context/MessageSourceProperties.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/context/MessageSourceProperties.java @@ -71,6 +71,12 @@ public class MessageSourceProperties { */ 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() { return this.basename; } @@ -119,4 +125,12 @@ public class MessageSourceProperties { this.useCodeAsDefaultMessage = useCodeAsDefaultMessage; } + public boolean isReloadable() { + return this.reloadable; + } + + public void setReloadable(boolean reloadable) { + this.reloadable = reloadable; + } + } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/context/MessageSourceAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/context/MessageSourceAutoConfigurationTests.java index 7ea0371bfe..94d46e87d1 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/context/MessageSourceAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/context/MessageSourceAutoConfigurationTests.java @@ -21,6 +21,7 @@ import java.util.Locale; import org.junit.Ignore; import org.junit.Test; +import org.springframework.beans.BeansException; import org.springframework.beans.DirectFieldAccessor; import org.springframework.boot.autoconfigure.AutoConfigurations; 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.Configuration; 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; @@ -220,6 +224,43 @@ public class MessageSourceAutoConfigurationTests { .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 @PropertySource("classpath:/switch-messages.properties") protected static class Config { diff --git a/spring-boot-project/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc b/spring-boot-project/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc index ede9ebeb73..4546300149 100644 --- a/spring-boot-project/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc +++ b/spring-boot-project/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc @@ -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.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.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. # OUTPUT