From 7afddc8eb83403e20bdbedb0c98a0151264e6357 Mon Sep 17 00:00:00 2001 From: Ryan Baxter Date: Tue, 25 Oct 2022 09:05:18 -0400 Subject: [PATCH 1/3] Destroy and initialize beans within the correct context. Fixes #1158 (#1160) Co-authored-by: Ryan Baxter <524254+ryanjbaxter@users.noreply.github.com> --- .../ConfigurationPropertiesRebinder.java | 49 +++++++++++-------- ...ionPropertiesRebinderIntegrationTests.java | 4 +- 2 files changed, 30 insertions(+), 23 deletions(-) diff --git a/spring-cloud-context/src/main/java/org/springframework/cloud/context/properties/ConfigurationPropertiesRebinder.java b/spring-cloud-context/src/main/java/org/springframework/cloud/context/properties/ConfigurationPropertiesRebinder.java index bd15bd69..a29d5893 100644 --- a/spring-cloud-context/src/main/java/org/springframework/cloud/context/properties/ConfigurationPropertiesRebinder.java +++ b/spring-cloud-context/src/main/java/org/springframework/cloud/context/properties/ConfigurationPropertiesRebinder.java @@ -89,30 +89,37 @@ public class ConfigurationPropertiesRebinder if (!this.beans.getBeanNames().contains(name)) { return false; } - if (this.applicationContext != null) { - try { - Object bean = this.applicationContext.getBean(name); - if (AopUtils.isAopProxy(bean)) { - bean = ProxyUtils.getTargetObject(bean); - } - if (bean != null) { - // TODO: determine a more general approach to fix this. - // see https://github.com/spring-cloud/spring-cloud-commons/issues/571 - if (getNeverRefreshable().contains(bean.getClass().getName())) { - return false; // ignore + ApplicationContext appContext = this.applicationContext; + while (appContext != null) { + if (appContext.containsLocalBean(name)) { + try { + Object bean = appContext.getBean(name); + if (AopUtils.isAopProxy(bean)) { + bean = ProxyUtils.getTargetObject(bean); } - this.applicationContext.getAutowireCapableBeanFactory().destroyBean(bean); - this.applicationContext.getAutowireCapableBeanFactory().initializeBean(bean, name); - return true; + if (bean != null) { + // TODO: determine a more general approach to fix this. + // see + // https://github.com/spring-cloud/spring-cloud-commons/issues/571 + if (getNeverRefreshable().contains(bean.getClass().getName())) { + return false; // ignore + } + appContext.getAutowireCapableBeanFactory().destroyBean(bean); + appContext.getAutowireCapableBeanFactory().initializeBean(bean, name); + return true; + } + } + catch (RuntimeException e) { + this.errors.put(name, e); + throw e; + } + catch (Exception e) { + this.errors.put(name, e); + throw new IllegalStateException("Cannot rebind to " + name, e); } } - catch (RuntimeException e) { - this.errors.put(name, e); - throw e; - } - catch (Exception e) { - this.errors.put(name, e); - throw new IllegalStateException("Cannot rebind to " + name, e); + else { + appContext = appContext.getParent(); } } return false; diff --git a/spring-cloud-context/src/test/java/org/springframework/cloud/context/properties/ConfigurationPropertiesRebinderIntegrationTests.java b/spring-cloud-context/src/test/java/org/springframework/cloud/context/properties/ConfigurationPropertiesRebinderIntegrationTests.java index eea089a3..8a9a2f09 100644 --- a/spring-cloud-context/src/test/java/org/springframework/cloud/context/properties/ConfigurationPropertiesRebinderIntegrationTests.java +++ b/spring-cloud-context/src/test/java/org/springframework/cloud/context/properties/ConfigurationPropertiesRebinderIntegrationTests.java @@ -72,12 +72,12 @@ public class ConfigurationPropertiesRebinderIntegrationTests { @Test @DirtiesContext public void testRefreshInParent() throws Exception { - then(this.config.getName()).isEqualTo("main"); + then(this.config.getName()).isEqualTo("parent"); // Change the dynamic property source... TestPropertyValues.of("config.name=foo").applyTo(this.environment); // ...and then refresh, so the bean is re-initialized: this.rebinder.rebind(); - then(this.config.getName()).isEqualTo("foo"); + then(this.config.getName()).isEqualTo("parent"); } @Test From d1dba70bbfb96e213d26576c3b0981fe7141df8f Mon Sep 17 00:00:00 2001 From: spencergibb Date: Thu, 27 Oct 2022 11:41:31 -0400 Subject: [PATCH 2/3] Adds rebind/refresh an individual bean by type Fixes gh-1162 --- .../ConfigurationPropertiesRebinder.java | 74 ++++++++++++------- .../context/scope/refresh/RefreshScope.java | 14 ++++ 2 files changed, 63 insertions(+), 25 deletions(-) diff --git a/spring-cloud-context/src/main/java/org/springframework/cloud/context/properties/ConfigurationPropertiesRebinder.java b/spring-cloud-context/src/main/java/org/springframework/cloud/context/properties/ConfigurationPropertiesRebinder.java index a29d5893..34341125 100644 --- a/spring-cloud-context/src/main/java/org/springframework/cloud/context/properties/ConfigurationPropertiesRebinder.java +++ b/spring-cloud-context/src/main/java/org/springframework/cloud/context/properties/ConfigurationPropertiesRebinder.java @@ -21,8 +21,10 @@ import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; +import org.springframework.aop.scope.ScopedProxyUtils; import org.springframework.aop.support.AopUtils; import org.springframework.beans.BeansException; +import org.springframework.beans.factory.BeanFactoryUtils; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.cloud.context.config.annotation.RefreshScope; import org.springframework.cloud.context.environment.EnvironmentChangeEvent; @@ -92,31 +94,7 @@ public class ConfigurationPropertiesRebinder ApplicationContext appContext = this.applicationContext; while (appContext != null) { if (appContext.containsLocalBean(name)) { - try { - Object bean = appContext.getBean(name); - if (AopUtils.isAopProxy(bean)) { - bean = ProxyUtils.getTargetObject(bean); - } - if (bean != null) { - // TODO: determine a more general approach to fix this. - // see - // https://github.com/spring-cloud/spring-cloud-commons/issues/571 - if (getNeverRefreshable().contains(bean.getClass().getName())) { - return false; // ignore - } - appContext.getAutowireCapableBeanFactory().destroyBean(bean); - appContext.getAutowireCapableBeanFactory().initializeBean(bean, name); - return true; - } - } - catch (RuntimeException e) { - this.errors.put(name, e); - throw e; - } - catch (Exception e) { - this.errors.put(name, e); - throw new IllegalStateException("Cannot rebind to " + name, e); - } + return rebind(name, appContext); } else { appContext = appContext.getParent(); @@ -125,6 +103,52 @@ public class ConfigurationPropertiesRebinder return false; } + /** + * WARNING: This method rebinds beans from any context in the hierarchy using the main application context. + * @param type bean type to rebind. + * @return true, if successful. + */ + public boolean rebind(Class type) { + String[] beanNamesForType = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.applicationContext, type); + if (beanNamesForType.length > 0) { + String name = beanNamesForType[0]; + if (ScopedProxyUtils.isScopedTarget(name)) { + name = ScopedProxyUtils.getOriginalBeanName(name); + } + return rebind(name, this.applicationContext); + } + return false; + } + + private boolean rebind(String name, ApplicationContext appContext) { + try { + Object bean = appContext.getBean(name); + if (AopUtils.isAopProxy(bean)) { + bean = ProxyUtils.getTargetObject(bean); + } + if (bean != null) { + // TODO: determine a more general approach to fix this. + // see + // https://github.com/spring-cloud/spring-cloud-commons/issues/571 + if (getNeverRefreshable().contains(bean.getClass().getName())) { + return false; // ignore + } + appContext.getAutowireCapableBeanFactory().destroyBean(bean); + appContext.getAutowireCapableBeanFactory().initializeBean(bean, name); + return true; + } + } + catch (RuntimeException e) { + this.errors.put(name, e); + throw e; + } + catch (Exception e) { + this.errors.put(name, e); + throw new IllegalStateException("Cannot rebind to " + name, e); + } + return false; + } + @ManagedAttribute public Set getNeverRefreshable() { String neverRefresh = this.applicationContext.getEnvironment() diff --git a/spring-cloud-context/src/main/java/org/springframework/cloud/context/scope/refresh/RefreshScope.java b/spring-cloud-context/src/main/java/org/springframework/cloud/context/scope/refresh/RefreshScope.java index 44018606..7a51eca1 100644 --- a/spring-cloud-context/src/main/java/org/springframework/cloud/context/scope/refresh/RefreshScope.java +++ b/spring-cloud-context/src/main/java/org/springframework/cloud/context/scope/refresh/RefreshScope.java @@ -20,6 +20,7 @@ import java.io.Serializable; import org.springframework.aop.scope.ScopedProxyUtils; import org.springframework.beans.BeansException; +import org.springframework.beans.factory.BeanFactoryUtils; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.cloud.context.scope.GenericScope; @@ -130,6 +131,19 @@ public class RefreshScope extends GenericScope } } + /** + * WARNING: This method refreshes beans from any context in the hierarchy using the main application context. + * @param type bean type to rebind. + * @return true, if successful. + */ + public boolean refresh(Class type) { + String[] beanNamesForType = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.context, type); + if (beanNamesForType.length > 0) { + return refresh(beanNamesForType[0]); + } + return false; + } + @ManagedOperation(description = "Dispose of the current instance of bean name " + "provided and force a refresh on next method execution.") public boolean refresh(String name) { From 3a582dc603750d58fe4bb2af226d000ea7e58655 Mon Sep 17 00:00:00 2001 From: spencergibb Date: Thu, 27 Oct 2022 11:51:04 -0400 Subject: [PATCH 3/3] Adds rebind/refresh an individual bean by type Fixes gh-1162 --- .../context/properties/ConfigurationPropertiesRebinder.java | 3 ++- .../cloud/context/scope/refresh/RefreshScope.java | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/spring-cloud-context/src/main/java/org/springframework/cloud/context/properties/ConfigurationPropertiesRebinder.java b/spring-cloud-context/src/main/java/org/springframework/cloud/context/properties/ConfigurationPropertiesRebinder.java index 34341125..9db8ac3b 100644 --- a/spring-cloud-context/src/main/java/org/springframework/cloud/context/properties/ConfigurationPropertiesRebinder.java +++ b/spring-cloud-context/src/main/java/org/springframework/cloud/context/properties/ConfigurationPropertiesRebinder.java @@ -104,7 +104,8 @@ public class ConfigurationPropertiesRebinder } /** - * WARNING: This method rebinds beans from any context in the hierarchy using the main application context. + * WARNING: This method rebinds beans from any context in the hierarchy using the main + * application context. * @param type bean type to rebind. * @return true, if successful. */ diff --git a/spring-cloud-context/src/main/java/org/springframework/cloud/context/scope/refresh/RefreshScope.java b/spring-cloud-context/src/main/java/org/springframework/cloud/context/scope/refresh/RefreshScope.java index 7a51eca1..b08dfa0f 100644 --- a/spring-cloud-context/src/main/java/org/springframework/cloud/context/scope/refresh/RefreshScope.java +++ b/spring-cloud-context/src/main/java/org/springframework/cloud/context/scope/refresh/RefreshScope.java @@ -132,7 +132,8 @@ public class RefreshScope extends GenericScope } /** - * WARNING: This method refreshes beans from any context in the hierarchy using the main application context. + * WARNING: This method refreshes beans from any context in the hierarchy using the + * main application context. * @param type bean type to rebind. * @return true, if successful. */