From 302f3c9db780f39e8a0d60ca846a26fe90047cf2 Mon Sep 17 00:00:00 2001 From: Rui Figueira Date: Tue, 5 Jun 2018 18:11:56 +0100 Subject: [PATCH] Support for ReloadableResourceBundleMessageSource This commit adds support for `ReloadableResourceBundleMessageSource`, by adding a `spring.messages.reloadable` configuration property, which can be set to `true` to force the `messageSource` bean to be an instance of that class. --- .../MessageSourceAutoConfiguration.java | 6 ++- .../context/MessageSourceProperties.java | 16 +++++++ .../MessageSourceAutoConfigurationTests.java | 43 +++++++++++++++++++ .../appendix-application-properties.adoc | 1 + 4 files changed, 65 insertions(+), 1 deletion(-) 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 d3a16cdb54ff..af4bd65805cf 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.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; @@ -66,7 +68,9 @@ public MessageSourceProperties messageSourceProperties() { @Bean public MessageSource messageSource() { MessageSourceProperties properties = messageSourceProperties(); - 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 62d366c2cd05..d322cc7e2f7a 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 @@ -22,6 +22,8 @@ import java.time.temporal.ChronoUnit; import org.springframework.boot.convert.DurationUnit; +import org.springframework.context.support.ReloadableResourceBundleMessageSource; +import org.springframework.context.support.ResourceBundleMessageSource; /** * Configuration properties for Message Source. @@ -71,6 +73,12 @@ public class MessageSourceProperties { */ private boolean useCodeAsDefaultMessage = false; + /** + * Whether to use a {@link ReloadableResourceBundleMessageSource} instead of the + * default {@link ResourceBundleMessageSource}. Recommended during development only. + */ + private boolean reloadable = false; + public String getBasename() { return this.basename; } @@ -119,4 +127,12 @@ public void setUseCodeAsDefaultMessage(boolean useCodeAsDefaultMessage) { 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 7ea0371bfef0..857b531861c1 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 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.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,45 @@ public void existingMessageSourceInParentIsIgnored() { .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 b44fee4a1293..7577ce3a4bc3 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 @@ -126,6 +126,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