Browse Source

Stop already started Lifecycle beans on cancelled refresh

Closes gh-35964
pull/35975/head
Juergen Hoeller 2 weeks ago
parent
commit
c74af40288
  1. 12
      spring-context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java
  2. 64
      spring-context/src/test/java/org/springframework/context/support/ApplicationContextLifecycleTests.java
  3. 26
      spring-context/src/test/java/org/springframework/context/support/SmartLifecycleTestBean.java
  4. 19
      spring-context/src/test/resources/org/springframework/context/support/smartLifecycleTests.xml

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

@ -623,12 +623,22 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader @@ -623,12 +623,22 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
finishRefresh();
}
catch (RuntimeException | Error ex ) {
catch (RuntimeException | Error ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// Stop already started Lifecycle beans to avoid dangling resources.
if (this.lifecycleProcessor != null && this.lifecycleProcessor.isRunning()) {
try {
this.lifecycleProcessor.stop();
}
catch (Throwable ex2) {
logger.warn("Exception thrown from LifecycleProcessor on cancelled refresh", ex2);
}
}
// Destroy already created singletons to avoid dangling resources.
destroyBeans();

64
spring-context/src/test/java/org/springframework/context/support/ApplicationContextLifecycleTests.java

@ -18,17 +18,25 @@ package org.springframework.context.support; @@ -18,17 +18,25 @@ package org.springframework.context.support;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.core.io.ClassPathResource;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
/**
* @author Mark Fisher
* @author Chris Beams
* @author Juergen Hoeller
*/
class ApplicationContextLifecycleTests {
@Test
void beansStart() {
AbstractApplicationContext context = new ClassPathXmlApplicationContext("lifecycleTests.xml", getClass());
context.start();
LifecycleTestBean bean1 = (LifecycleTestBean) context.getBean("bean1");
LifecycleTestBean bean2 = (LifecycleTestBean) context.getBean("bean2");
@ -39,12 +47,14 @@ class ApplicationContextLifecycleTests { @@ -39,12 +47,14 @@ class ApplicationContextLifecycleTests {
assertThat(bean2.isRunning()).as(error).isTrue();
assertThat(bean3.isRunning()).as(error).isTrue();
assertThat(bean4.isRunning()).as(error).isTrue();
context.close();
}
@Test
void beansStop() {
AbstractApplicationContext context = new ClassPathXmlApplicationContext("lifecycleTests.xml", getClass());
context.start();
LifecycleTestBean bean1 = (LifecycleTestBean) context.getBean("bean1");
LifecycleTestBean bean2 = (LifecycleTestBean) context.getBean("bean2");
@ -55,18 +65,21 @@ class ApplicationContextLifecycleTests { @@ -55,18 +65,21 @@ class ApplicationContextLifecycleTests {
assertThat(bean2.isRunning()).as(startError).isTrue();
assertThat(bean3.isRunning()).as(startError).isTrue();
assertThat(bean4.isRunning()).as(startError).isTrue();
context.stop();
String stopError = "bean was not stopped";
assertThat(bean1.isRunning()).as(stopError).isFalse();
assertThat(bean2.isRunning()).as(stopError).isFalse();
assertThat(bean3.isRunning()).as(stopError).isFalse();
assertThat(bean4.isRunning()).as(stopError).isFalse();
context.close();
}
@Test
void startOrder() {
AbstractApplicationContext context = new ClassPathXmlApplicationContext("lifecycleTests.xml", getClass());
context.start();
LifecycleTestBean bean1 = (LifecycleTestBean) context.getBean("bean1");
LifecycleTestBean bean2 = (LifecycleTestBean) context.getBean("bean2");
@ -81,18 +94,22 @@ class ApplicationContextLifecycleTests { @@ -81,18 +94,22 @@ class ApplicationContextLifecycleTests {
assertThat(bean2.getStartOrder()).as(orderError).isGreaterThan(bean1.getStartOrder());
assertThat(bean3.getStartOrder()).as(orderError).isGreaterThan(bean2.getStartOrder());
assertThat(bean4.getStartOrder()).as(orderError).isGreaterThan(bean2.getStartOrder());
context.close();
}
@Test
void stopOrder() {
AbstractApplicationContext context = new ClassPathXmlApplicationContext("lifecycleTests.xml", getClass());
context.start();
context.stop();
LifecycleTestBean bean1 = (LifecycleTestBean) context.getBean("bean1");
LifecycleTestBean bean2 = (LifecycleTestBean) context.getBean("bean2");
LifecycleTestBean bean3 = (LifecycleTestBean) context.getBean("bean3");
LifecycleTestBean bean4 = (LifecycleTestBean) context.getBean("bean4");
void autoStartup() {
GenericApplicationContext context = new GenericApplicationContext();
new XmlBeanDefinitionReader(context).loadBeanDefinitions(new ClassPathResource("smartLifecycleTests.xml", getClass()));
context.refresh();
LifecycleTestBean bean1 = (LifecycleTestBean) context.getBeanFactory().getBean("bean1");
LifecycleTestBean bean2 = (LifecycleTestBean) context.getBeanFactory().getBean("bean2");
LifecycleTestBean bean3 = (LifecycleTestBean) context.getBeanFactory().getBean("bean3");
LifecycleTestBean bean4 = (LifecycleTestBean) context.getBeanFactory().getBean("bean4");
context.close();
String notStoppedError = "bean was not stopped";
assertThat(bean1.getStopOrder()).as(notStoppedError).isGreaterThan(0);
assertThat(bean2.getStopOrder()).as(notStoppedError).isGreaterThan(0);
@ -102,7 +119,36 @@ class ApplicationContextLifecycleTests { @@ -102,7 +119,36 @@ class ApplicationContextLifecycleTests {
assertThat(bean2.getStopOrder()).as(orderError).isLessThan(bean1.getStopOrder());
assertThat(bean3.getStopOrder()).as(orderError).isLessThan(bean2.getStopOrder());
assertThat(bean4.getStopOrder()).as(orderError).isLessThan(bean2.getStopOrder());
context.close();
}
@Test
void cancelledRefresh() {
GenericApplicationContext context = new GenericApplicationContext();
new XmlBeanDefinitionReader(context).loadBeanDefinitions(new ClassPathResource("smartLifecycleTests.xml", getClass()));
context.registerBean(FailingContextRefreshedListener.class);
LifecycleTestBean bean1 = (LifecycleTestBean) context.getBeanFactory().getBean("bean1");
LifecycleTestBean bean2 = (LifecycleTestBean) context.getBeanFactory().getBean("bean2");
LifecycleTestBean bean3 = (LifecycleTestBean) context.getBeanFactory().getBean("bean3");
LifecycleTestBean bean4 = (LifecycleTestBean) context.getBeanFactory().getBean("bean4");
assertThatIllegalStateException().isThrownBy(context::refresh);
String notStoppedError = "bean was not stopped";
assertThat(bean1.getStopOrder()).as(notStoppedError).isGreaterThan(0);
assertThat(bean2.getStopOrder()).as(notStoppedError).isGreaterThan(0);
assertThat(bean3.getStopOrder()).as(notStoppedError).isGreaterThan(0);
assertThat(bean4.getStopOrder()).as(notStoppedError).isGreaterThan(0);
String orderError = "dependent bean must stop before the bean it depends on";
assertThat(bean2.getStopOrder()).as(orderError).isLessThan(bean1.getStopOrder());
assertThat(bean3.getStopOrder()).as(orderError).isLessThan(bean2.getStopOrder());
assertThat(bean4.getStopOrder()).as(orderError).isLessThan(bean2.getStopOrder());
}
private static class FailingContextRefreshedListener implements ApplicationListener<ContextRefreshedEvent> {
public void onApplicationEvent(ContextRefreshedEvent event) {
throw new IllegalStateException();
}
}
}

26
spring-context/src/test/java/org/springframework/context/support/SmartLifecycleTestBean.java

@ -0,0 +1,26 @@ @@ -0,0 +1,26 @@
/*
* Copyright 2002-present 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.context.support;
import org.springframework.context.SmartLifecycle;
/**
* @author Juergen Hoeller
*/
public class SmartLifecycleTestBean extends LifecycleTestBean implements SmartLifecycle {
}

19
spring-context/src/test/resources/org/springframework/context/support/smartLifecycleTests.xml

@ -0,0 +1,19 @@ @@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans-2.0.xsd">
<bean id="bean4" class="org.springframework.context.support.SmartLifecycleTestBean" depends-on="bean2"/>
<bean id="bean3" class="org.springframework.context.support.SmartLifecycleTestBean" depends-on="bean2"/>
<bean id="bean1" class="org.springframework.context.support.SmartLifecycleTestBean"/>
<bean id="bean2" class="org.springframework.context.support.SmartLifecycleTestBean" depends-on="bean1"/>
<bean id="bean2Proxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="bean2"/>
</bean>
</beans>
Loading…
Cancel
Save