committed by
GitHub
parent
d4bc60929b
commit
9449edf93b
@@ -217,6 +217,12 @@ The configuration property must be present in order to update the value after a
|
||||
a value in your application you might want to switch your logic to rely on its absence instead. Another option would be to rely
|
||||
on the value changing rather than not being present in the application's configuration.
|
||||
|
||||
[refresh-scope-on-restart]
|
||||
=== Refresh Scope on Restart
|
||||
|
||||
In order to allow seamlessly refreshing beans on restart, which is especially useful for applications running with JVM Checkpoint Restore (for example with https://github.com/CRaC[Project CRaC]), we now instantiate a `RefreshScopeLifecycle` bean that will trigger Context Refresh on restart, resulting in rebinding configuration properties and refreshing any `@RefreshScope`-annotated beans. This behaviour can be disabled by setting the value of `spring.cloud.refresh.on-restart.enabled` to `false`.
|
||||
|
||||
|
||||
[[encryption-and-decryption]]
|
||||
== Encryption and Decryption
|
||||
|
||||
|
||||
@@ -75,6 +75,7 @@
|
||||
|spring.cloud.refresh.enabled | `+++true+++` | Enables autoconfiguration for the refresh scope and associated features.
|
||||
|spring.cloud.refresh.extra-refreshable | `+++true+++` | Additional class names for beans to post process into refresh scope.
|
||||
|spring.cloud.refresh.never-refreshable | `+++true+++` | Comma separated list of class names for beans to never be refreshed or rebound.
|
||||
|spring.cloud.refresh.on-restart.enabled | `+++true+++` | Enable refreshing context on start.
|
||||
|spring.cloud.service-registry.auto-registration.enabled | `+++true+++` | Whether service auto-registration is enabled. Defaults to true.
|
||||
|spring.cloud.service-registry.auto-registration.fail-fast | `+++false+++` | Whether startup fails if there is no AutoServiceRegistration. Defaults to false.
|
||||
|spring.cloud.service-registry.auto-registration.register-management | `+++true+++` | Whether to register the management as a service. Defaults to true.
|
||||
|
||||
@@ -43,6 +43,7 @@ import org.springframework.boot.context.properties.bind.Binder;
|
||||
import org.springframework.cloud.context.refresh.ConfigDataContextRefresher;
|
||||
import org.springframework.cloud.context.refresh.ContextRefresher;
|
||||
import org.springframework.cloud.context.refresh.LegacyContextRefresher;
|
||||
import org.springframework.cloud.context.refresh.RefreshScopeLifecycle;
|
||||
import org.springframework.cloud.context.scope.refresh.RefreshScope;
|
||||
import org.springframework.cloud.endpoint.event.RefreshEventListener;
|
||||
import org.springframework.cloud.logging.LoggingRebinder;
|
||||
@@ -66,6 +67,7 @@ import org.springframework.util.StringUtils;
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @author Venil Noronha
|
||||
* @author Olga Maciaszek-Sharma
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnClass(RefreshScope.class)
|
||||
@@ -117,6 +119,12 @@ public class RefreshAutoConfiguration {
|
||||
return new ConfigDataContextRefresher(context, scope, properties);
|
||||
}
|
||||
|
||||
@ConditionalOnProperty(value = "spring.cloud.refresh.on-restart.enabled", matchIfMissing = true)
|
||||
@Bean
|
||||
RefreshScopeLifecycle refreshScopeLifecycle(ContextRefresher contextRefresher) {
|
||||
return new RefreshScopeLifecycle(contextRefresher);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public RefreshEventListener refreshEventListener(ContextRefresher contextRefresher) {
|
||||
return new RefreshEventListener(contextRefresher);
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright 2012-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.context.refresh;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.context.Lifecycle;
|
||||
|
||||
/**
|
||||
* A {@link Lifecycle} implementation that triggers {@link ContextRefresher#refresh()} to
|
||||
* be called on restart.
|
||||
*
|
||||
* @author Olga Maciaszek-Sharma
|
||||
* @since 4.1.0
|
||||
*/
|
||||
public class RefreshScopeLifecycle implements Lifecycle {
|
||||
|
||||
private static final Log LOG = LogFactory.getLog(RefreshScopeLifecycle.class);
|
||||
|
||||
private final ContextRefresher contextRefresher;
|
||||
|
||||
private final Object lifecycleMonitor = new Object();
|
||||
|
||||
private volatile boolean running = true;
|
||||
|
||||
public RefreshScopeLifecycle(ContextRefresher contextRefresher) {
|
||||
this.contextRefresher = contextRefresher;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
synchronized (lifecycleMonitor) {
|
||||
if (!isRunning()) {
|
||||
if (LOG.isInfoEnabled()) {
|
||||
LOG.info("Refreshing context on restart.");
|
||||
}
|
||||
Set<String> keys = contextRefresher.refresh();
|
||||
if(LOG.isInfoEnabled()){
|
||||
LOG.info("Refreshed keys: " + keys);
|
||||
}
|
||||
}
|
||||
running = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
synchronized (lifecycleMonitor) {
|
||||
if (isRunning()) {
|
||||
running = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRunning() {
|
||||
return running;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -53,6 +53,12 @@
|
||||
"type": "java.lang.Boolean",
|
||||
"description": "Enable the DecryptEnvironmentPostProcessor.",
|
||||
"defaultValue": true
|
||||
},
|
||||
{
|
||||
"name": "spring.cloud.refresh.on-restart.enabled",
|
||||
"type": "java.lang.Boolean",
|
||||
"description": "Enable refreshing context on start.",
|
||||
"defaultValue": true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -22,11 +22,13 @@ import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
|
||||
import org.springframework.boot.WebApplicationType;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
|
||||
import org.springframework.boot.builder.SpringApplicationBuilder;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||
import org.springframework.boot.test.system.CapturedOutput;
|
||||
import org.springframework.boot.test.system.OutputCaptureExtension;
|
||||
import org.springframework.cloud.context.refresh.ContextRefresher;
|
||||
@@ -38,9 +40,10 @@ import static org.assertj.core.api.BDDAssertions.then;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
* @author Olga Maciaszek-Sharma
|
||||
*/
|
||||
@ExtendWith(OutputCaptureExtension.class)
|
||||
public class RefreshAutoConfigurationTests {
|
||||
class RefreshAutoConfigurationTests {
|
||||
|
||||
private static ConfigurableApplicationContext getApplicationContext(WebApplicationType type, Class<?> configuration,
|
||||
String... properties) {
|
||||
@@ -49,7 +52,7 @@ public class RefreshAutoConfigurationTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void noWarnings(CapturedOutput output) {
|
||||
void noWarnings(CapturedOutput output) {
|
||||
try (ConfigurableApplicationContext context = getApplicationContext(WebApplicationType.NONE, Config.class)) {
|
||||
then(context.containsBean("refreshScope")).isTrue();
|
||||
then(output.toString()).doesNotContain("WARN");
|
||||
@@ -57,7 +60,7 @@ public class RefreshAutoConfigurationTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void disabled() {
|
||||
void disabled() {
|
||||
try (ConfigurableApplicationContext context = getApplicationContext(WebApplicationType.SERVLET, Config.class,
|
||||
"spring.cloud.refresh.enabled:false")) {
|
||||
then(context.containsBean("refreshScope")).isFalse();
|
||||
@@ -65,7 +68,7 @@ public class RefreshAutoConfigurationTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void refreshables() {
|
||||
void refreshables() {
|
||||
try (ConfigurableApplicationContext context = getApplicationContext(WebApplicationType.NONE, Config.class,
|
||||
"config.foo=bar", "spring.cloud.refresh.refreshable:" + SealedConfigProps.class.getName())) {
|
||||
context.getBean(SealedConfigProps.class);
|
||||
@@ -84,7 +87,7 @@ public class RefreshAutoConfigurationTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void neverRefreshable() {
|
||||
void neverRefreshable() {
|
||||
try (ConfigurableApplicationContext context = getApplicationContext(WebApplicationType.NONE, Config.class,
|
||||
"countingconfig.foo=bar",
|
||||
"spring.cloud.refresh.never-refreshable:" + CountingConfigProps.class.getName())) {
|
||||
@@ -94,6 +97,19 @@ public class RefreshAutoConfigurationTests {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void refreshScopeLifecylePresentByDefault() {
|
||||
new ApplicationContextRunner().withConfiguration(AutoConfigurations.of(RefreshAutoConfiguration.class))
|
||||
.run(context -> assertThat(context).hasBean("refreshScopeLifecycle"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void refreshScopeLifecyleDisabledWithProp() {
|
||||
new ApplicationContextRunner().withConfiguration(AutoConfigurations.of(RefreshAutoConfiguration.class))
|
||||
.withPropertyValues("spring.cloud.refresh.on-restart.enabled=false")
|
||||
.run(context -> assertThat(context).doesNotHaveBean("refreshScopeLifecycle"));
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@EnableAutoConfiguration(exclude = DataSourceAutoConfiguration.class)
|
||||
@EnableConfigurationProperties({ SealedConfigProps.class, CountingConfigProps.class })
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright 2012-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.context.refresh;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoInteractions;
|
||||
|
||||
/**
|
||||
* Tests for {@link RefreshScopeLifecycle}.
|
||||
*
|
||||
* @author Olga Maciaszek-Sharma
|
||||
*/
|
||||
class RefreshScopeLifecycleTests {
|
||||
|
||||
ContextRefresher contextRefresher = mock();
|
||||
|
||||
private final RefreshScopeLifecycle lifecycle = new RefreshScopeLifecycle(contextRefresher);
|
||||
|
||||
@Test
|
||||
void shouldRefreshContextOnRestart() {
|
||||
lifecycle.stop();
|
||||
lifecycle.start();
|
||||
|
||||
verify(contextRefresher).refresh();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldNotRefreshContextOnStart() {
|
||||
lifecycle.start();
|
||||
|
||||
verifyNoInteractions(contextRefresher);
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user