Skip to content

Commit 523552a

Browse files
committed
Introduce ConfigurableApplicationContext.restart() method
Closes gh-35171
1 parent b48da4c commit 523552a

File tree

5 files changed

+99
-15
lines changed

5 files changed

+99
-15
lines changed

spring-context/src/main/java/org/springframework/context/ConfigurableApplicationContext.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,17 @@ public interface ConfigurableApplicationContext extends ApplicationContext, Life
220220
*/
221221
void refresh() throws BeansException, IllegalStateException;
222222

223+
/**
224+
* Stop all beans in this application context if necessary, and subsequently
225+
* restart all auto-startup beans, effectively restoring the lifecycle state
226+
* after {@link #refresh()} (typically after a preceding {@link #stop()} call
227+
* when a full {@link #start()} of even lazy-starting beans is to be avoided).
228+
* @since 7.0
229+
* @see #stop()
230+
* @see SmartLifecycle#isAutoStartup()
231+
*/
232+
void restart();
233+
223234
/**
224235
* Register a shutdown hook with the JVM runtime, closing this context
225236
* on JVM shutdown unless it has already been closed at that time.

spring-context/src/main/java/org/springframework/context/LifecycleProcessor.java

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,31 @@
2626
public interface LifecycleProcessor extends Lifecycle {
2727

2828
/**
29-
* Notification of context refresh, for example, for auto-starting components.
29+
* Notification of context refresh for auto-starting components.
30+
* @see ConfigurableApplicationContext#refresh()
3031
*/
31-
void onRefresh();
32+
default void onRefresh() {
33+
start();
34+
}
3235

3336
/**
34-
* Notification of context close phase, for example, for auto-stopping components.
37+
* Notification of context restart for auto-stopping and subsequently
38+
* auto-starting components.
39+
* @since 7.0
40+
* @see ConfigurableApplicationContext#restart()
3541
*/
36-
void onClose();
42+
default void onRestart() {
43+
stop();
44+
start();
45+
}
46+
47+
/**
48+
* Notification of context close phase for auto-stopping components
49+
* before destruction.
50+
* @see ConfigurableApplicationContext#close()
51+
*/
52+
default void onClose() {
53+
stop();
54+
}
3755

3856
}

spring-context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1548,6 +1548,12 @@ public void stop() {
15481548
publishEvent(new ContextStoppedEvent(this));
15491549
}
15501550

1551+
@Override
1552+
public void restart() {
1553+
getLifecycleProcessor().onRestart();
1554+
publishEvent(new ContextStartedEvent(this));
1555+
}
1556+
15511557
@Override
15521558
public boolean isRunning() {
15531559
return (this.lifecycleProcessor != null && this.lifecycleProcessor.isRunning());

spring-context/src/main/java/org/springframework/context/support/DefaultLifecycleProcessor.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,16 @@ public void onRefresh() {
314314
this.running = true;
315315
}
316316

317+
@Override
318+
public void onRestart() {
319+
this.stoppedBeans = null;
320+
if (this.running) {
321+
stopBeans();
322+
}
323+
startBeans(true);
324+
this.running = true;
325+
}
326+
317327
@Override
318328
public void onClose() {
319329
stopBeans();

spring-context/src/test/java/org/springframework/context/support/DefaultLifecycleProcessorTests.java

Lines changed: 50 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,40 @@ void contextRefreshThenStopForRestartWithMixedBeans() {
348348
context.close();
349349
}
350350

351+
@Test
352+
void contextRefreshThenRestartWithMixedBeans() {
353+
StaticApplicationContext context = new StaticApplicationContext();
354+
CopyOnWriteArrayList<Lifecycle> stoppedBeans = new CopyOnWriteArrayList<>();
355+
TestSmartLifecycleBean smartBean1 = TestSmartLifecycleBean.forShutdownTests(5, 0, stoppedBeans);
356+
TestSmartLifecycleBean smartBean2 = TestSmartLifecycleBean.forShutdownTests(-3, 0, stoppedBeans);
357+
smartBean2.setAutoStartup(false);
358+
context.getBeanFactory().registerSingleton("smartBean1", smartBean1);
359+
context.getBeanFactory().registerSingleton("smartBean2", smartBean2);
360+
361+
assertThat(smartBean1.isRunning()).isFalse();
362+
assertThat(smartBean2.isRunning()).isFalse();
363+
context.refresh();
364+
assertThat(smartBean1.isRunning()).isTrue();
365+
assertThat(smartBean2.isRunning()).isFalse();
366+
context.restart();
367+
assertThat(stoppedBeans).containsExactly(smartBean1);
368+
assertThat(smartBean1.isRunning()).isTrue();
369+
assertThat(smartBean2.isRunning()).isFalse();
370+
smartBean1.stop();
371+
assertThat(stoppedBeans).containsExactly(smartBean1, smartBean1);
372+
assertThat(smartBean1.isRunning()).isFalse();
373+
assertThat(smartBean2.isRunning()).isFalse();
374+
context.restart();
375+
assertThat(stoppedBeans).containsExactly(smartBean1, smartBean1);
376+
assertThat(smartBean1.isRunning()).isTrue();
377+
assertThat(smartBean2.isRunning()).isFalse();
378+
context.start();
379+
assertThat(smartBean1.isRunning()).isTrue();
380+
assertThat(smartBean2.isRunning()).isTrue();
381+
context.close();
382+
assertThat(stoppedBeans).containsExactly(smartBean1, smartBean1, smartBean1, smartBean2);
383+
}
384+
351385
@Test
352386
@EnabledForTestGroups(LONG_RUNNING)
353387
void smartLifecycleGroupShutdown() {
@@ -741,17 +775,22 @@ public void stop(final Runnable callback) {
741775
// invocation order in the 'stoppedBeans' list
742776
stop();
743777
final int delay = this.shutdownDelay;
744-
new Thread(() -> {
745-
try {
746-
Thread.sleep(delay);
747-
}
748-
catch (InterruptedException e) {
749-
// ignore
750-
}
751-
finally {
752-
callback.run();
753-
}
754-
}).start();
778+
if (delay > 0) {
779+
new Thread(() -> {
780+
try {
781+
Thread.sleep(delay);
782+
}
783+
catch (InterruptedException e) {
784+
// ignore
785+
}
786+
finally {
787+
callback.run();
788+
}
789+
}).start();
790+
}
791+
else {
792+
callback.run();
793+
}
755794
}
756795
}
757796

0 commit comments

Comments
 (0)