From 22abe35f959790c49ecd19fa02b311b9ba11f533 Mon Sep 17 00:00:00 2001 From: Rui Figueira Date: Tue, 5 Jun 2018 18:11:56 +0100 Subject: [PATCH 1/2] 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 --- .../MessageSourceAutoConfiguration.java | 6 ++- .../context/MessageSourceProperties.java | 14 +++++++ .../MessageSourceAutoConfigurationTests.java | 41 +++++++++++++++++++ .../appendix-application-properties.adoc | 1 + 4 files changed, 61 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 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 From 11ac9bf6b52a9454c1392b9cc7128dec59cb91f6 Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Thu, 4 Oct 2018 14:29:22 -0700 Subject: [PATCH 2/2] Polish "Add ReloadableResourceBundleMessageSource support" See gh-13377 --- .../MessageSourceAutoConfigurationTests.java | 42 ++++++++----------- 1 file changed, 18 insertions(+), 24 deletions(-) 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 94d46e87d1..c31c222ae2 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,7 +21,6 @@ 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; @@ -226,39 +225,34 @@ public class MessageSourceAutoConfigurationTests { @Test public void testDefaultReloadableValueMessageSource() { - this.contextRunner.withPropertyValues("spring.messages.basename:test/messages") - .run((context) -> assertThat(getDeclaredMessageSource(context)) - .isInstanceOf(ResourceBundleMessageSource.class)); + testReloadableMessageSource(ResourceBundleMessageSource.class, + "spring.messages.basename:test/messages"); } @Test public void testNotReloadableMessageSource() { - this.contextRunner - .withPropertyValues("spring.messages.basename:test/messages", - "spring.messages.reloadable:false") - .run((context) -> assertThat(getDeclaredMessageSource(context)) - .isInstanceOf(ResourceBundleMessageSource.class)); + testReloadableMessageSource(ResourceBundleMessageSource.class, + "spring.messages.basename:test/messages", + "spring.messages.reloadable:false"); } @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"); - }); + testReloadableMessageSource(ReloadableResourceBundleMessageSource.class, + "spring.messages.basename:test/messages", + "spring.messages.reloadable:true"); } - private MessageSource getDeclaredMessageSource(AssertableApplicationContext context) - throws BeansException { - MessageSource messageSource = context.getBean(MessageSource.class); - if (messageSource instanceof DelegatingMessageSource) { - messageSource = ((DelegatingMessageSource) messageSource) - .getParentMessageSource(); - } - return messageSource; + private void testReloadableMessageSource(Class expectedInstance, + String... propertyValues) { + this.contextRunner.withPropertyValues(propertyValues).run((context) -> { + MessageSource messageSource = context.getBean(MessageSource.class); + if (messageSource instanceof DelegatingMessageSource) { + messageSource = ((DelegatingMessageSource) messageSource) + .getParentMessageSource(); + } + assertThat(messageSource).isInstanceOf(expectedInstance); + }); } @Configuration