Skip to content

Commit d46c26d

Browse files
committed
Call Lifecycle.stop() for already started beans on failed refresh
Closes gh-20028
1 parent ecd3f19 commit d46c26d

File tree

2 files changed

+47
-1
lines changed

2 files changed

+47
-1
lines changed

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

+11-1
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,8 @@ private ConfigurableListableBeanFactory getBeanFactory() {
159159
public void start() {
160160
this.stoppedBeans = null;
161161
startBeans(false);
162+
// If any bean failed to explicitly start, the exception propagates here.
163+
// The caller may choose to subsequently call stop() if appropriate.
162164
this.running = true;
163165
}
164166

@@ -183,7 +185,15 @@ public void onRefresh() {
183185
}
184186

185187
this.stoppedBeans = null;
186-
startBeans(true);
188+
try {
189+
startBeans(true);
190+
}
191+
catch (ApplicationContextException ex) {
192+
// Some bean failed to auto-start within context refresh:
193+
// stop already started beans on context refresh failure.
194+
stopBeans();
195+
throw ex;
196+
}
187197
this.running = true;
188198
}
189199

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

+36
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,14 @@
2424
import org.springframework.beans.factory.FactoryBean;
2525
import org.springframework.beans.factory.config.BeanDefinition;
2626
import org.springframework.beans.factory.support.RootBeanDefinition;
27+
import org.springframework.context.ApplicationContextException;
2728
import org.springframework.context.Lifecycle;
2829
import org.springframework.context.LifecycleProcessor;
2930
import org.springframework.context.SmartLifecycle;
3031
import org.springframework.core.testfixture.EnabledForTestGroups;
3132

3233
import static org.assertj.core.api.Assertions.assertThat;
34+
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
3335
import static org.springframework.core.testfixture.TestGroup.LONG_RUNNING;
3436

3537
/**
@@ -107,6 +109,21 @@ void singleSmartLifecycleAutoStartupWithLazyInitFactoryBean() {
107109
context.close();
108110
}
109111

112+
@Test
113+
void singleSmartLifecycleAutoStartupWithFailingLifecycleBean() {
114+
CopyOnWriteArrayList<Lifecycle> startedBeans = new CopyOnWriteArrayList<>();
115+
TestSmartLifecycleBean bean = TestSmartLifecycleBean.forStartupTests(1, startedBeans);
116+
bean.setAutoStartup(true);
117+
StaticApplicationContext context = new StaticApplicationContext();
118+
context.getBeanFactory().registerSingleton("bean", bean);
119+
context.registerSingleton("failingBean", FailingLifecycleBean.class);
120+
assertThat(bean.isRunning()).isFalse();
121+
assertThatExceptionOfType(ApplicationContextException.class)
122+
.isThrownBy(context::refresh).withCauseInstanceOf(IllegalStateException.class);
123+
assertThat(bean.isRunning()).isFalse();
124+
assertThat(startedBeans).hasSize(1);
125+
}
126+
110127
@Test
111128
void singleSmartLifecycleWithoutAutoStartup() {
112129
CopyOnWriteArrayList<Lifecycle> startedBeans = new CopyOnWriteArrayList<>();
@@ -832,4 +849,23 @@ public int getPhase() {
832849
}
833850
}
834851

852+
853+
public static class FailingLifecycleBean implements SmartLifecycle {
854+
855+
@Override
856+
public void start() {
857+
throw new IllegalStateException();
858+
}
859+
860+
@Override
861+
public void stop() {
862+
throw new IllegalStateException();
863+
}
864+
865+
@Override
866+
public boolean isRunning() {
867+
return false;
868+
}
869+
}
870+
835871
}

0 commit comments

Comments
 (0)