Browse Source

Introduce support for concurrent startup phases with timeouts

Closes gh-34634
pull/34732/head
Juergen Hoeller 9 months ago
parent
commit
48009c8534
  1. 157
      spring-context/src/main/java/org/springframework/context/support/DefaultLifecycleProcessor.java
  2. 85
      spring-context/src/test/java/org/springframework/context/support/DefaultLifecycleProcessorTests.java

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

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2024 the original author or authors. * Copyright 2002-2025 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -26,9 +26,12 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.TreeMap; import java.util.TreeMap;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import java.util.concurrent.CyclicBarrier; import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
@ -52,6 +55,7 @@ import org.springframework.core.SpringProperties;
import org.springframework.lang.Nullable; import org.springframework.lang.Nullable;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import org.springframework.util.ClassUtils; import org.springframework.util.ClassUtils;
import org.springframework.util.CollectionUtils;
/** /**
* Spring's default implementation of the {@link LifecycleProcessor} strategy. * Spring's default implementation of the {@link LifecycleProcessor} strategy.
@ -61,12 +65,23 @@ import org.springframework.util.ClassUtils;
* interactions on a {@link org.springframework.context.ConfigurableApplicationContext}. * interactions on a {@link org.springframework.context.ConfigurableApplicationContext}.
* *
* <p>As of 6.1, this also includes support for JVM checkpoint/restore (Project CRaC) * <p>As of 6.1, this also includes support for JVM checkpoint/restore (Project CRaC)
* when the {@code org.crac:crac} dependency on the classpath. * when the {@code org.crac:crac} dependency is on the classpath. All running beans
* will get stopped and restarted according to the CRaC checkpoint/restore callbacks.
*
* <p>As of 6.2, this processor can be configured with custom timeouts for specific
* shutdown phases, applied to {@link SmartLifecycle#stop(Runnable)} implementations.
* As of 6.2.6, there is also support for the concurrent startup of specific phases
* with individual timeouts, triggering the {@link SmartLifecycle#start()} callbacks
* of all associated beans asynchronously and then waiting for all of them to return,
* as an alternative to the default sequential startup of beans without a timeout.
* *
* @author Mark Fisher * @author Mark Fisher
* @author Juergen Hoeller * @author Juergen Hoeller
* @author Sebastien Deleuze * @author Sebastien Deleuze
* @since 3.0 * @since 3.0
* @see SmartLifecycle#getPhase()
* @see #setConcurrentStartupForPhase
* @see #setTimeoutForShutdownPhase
*/ */
public class DefaultLifecycleProcessor implements LifecycleProcessor, BeanFactoryAware { public class DefaultLifecycleProcessor implements LifecycleProcessor, BeanFactoryAware {
@ -102,6 +117,8 @@ public class DefaultLifecycleProcessor implements LifecycleProcessor, BeanFactor
private final Log logger = LogFactory.getLog(getClass()); private final Log logger = LogFactory.getLog(getClass());
private final Map<Integer, Long> concurrentStartupForPhases = new ConcurrentHashMap<>();
private final Map<Integer, Long> timeoutsForShutdownPhases = new ConcurrentHashMap<>(); private final Map<Integer, Long> timeoutsForShutdownPhases = new ConcurrentHashMap<>();
private volatile long timeoutPerShutdownPhase = 10000; private volatile long timeoutPerShutdownPhase = 10000;
@ -130,20 +147,59 @@ public class DefaultLifecycleProcessor implements LifecycleProcessor, BeanFactor
} }
/**
* Switch to concurrent startup for each given phase (group of {@link SmartLifecycle}
* beans with the same 'phase' value) with corresponding timeouts.
* <p><b>Note: By default, the startup for every phase will be sequential without
* a timeout. Calling this setter with timeouts for the given phases switches to a
* mode where the beans in these phases will be started concurrently, cancelling
* the startup if the corresponding timeout is not met for any of these phases.</b>
* <p>For an actual concurrent startup, a bootstrap {@code Executor} needs to be
* set for the application context, typically through a "bootstrapExecutor" bean.
* @param phasesWithTimeouts a map of phase values (matching
* {@link SmartLifecycle#getPhase()}) and corresponding timeout values
* (in milliseconds)
* @since 6.2.6
* @see SmartLifecycle#getPhase()
* @see org.springframework.beans.factory.config.ConfigurableBeanFactory#getBootstrapExecutor()
*/
public void setConcurrentStartupForPhases(Map<Integer, Long> phasesWithTimeouts) {
this.concurrentStartupForPhases.putAll(phasesWithTimeouts);
}
/**
* Switch to concurrent startup for a specific phase (group of {@link SmartLifecycle}
* beans with the same 'phase' value) with a corresponding timeout.
* <p><b>Note: By default, the startup for every phase will be sequential without
* a timeout. Calling this setter with a timeout for the given phase switches to a
* mode where the beans in this phase will be started concurrently, cancelling
* the startup if the corresponding timeout is not met for this phase.</b>
* <p>For an actual concurrent startup, a bootstrap {@code Executor} needs to be
* set for the application context, typically through a "bootstrapExecutor" bean.
* @param phase the phase value (matching {@link SmartLifecycle#getPhase()})
* @param timeout the corresponding timeout value (in milliseconds)
* @since 6.2.6
* @see SmartLifecycle#getPhase()
* @see org.springframework.beans.factory.config.ConfigurableBeanFactory#getBootstrapExecutor()
*/
public void setConcurrentStartupForPhase(int phase, long timeout) {
this.concurrentStartupForPhases.put(phase, timeout);
}
/** /**
* Specify the maximum time allotted for the shutdown of each given phase * Specify the maximum time allotted for the shutdown of each given phase
* (group of {@link SmartLifecycle} beans with the same 'phase' value). * (group of {@link SmartLifecycle} beans with the same 'phase' value).
* <p>In case of no specific timeout configured, the default timeout per * <p>In case of no specific timeout configured, the default timeout per
* shutdown phase will apply: 10000 milliseconds (10 seconds) as of 6.2. * shutdown phase will apply: 10000 milliseconds (10 seconds) as of 6.2.
* @param timeoutsForShutdownPhases a map of phase values (matching * @param phasesWithTimeouts a map of phase values (matching
* {@link SmartLifecycle#getPhase()}) and corresponding timeout values * {@link SmartLifecycle#getPhase()}) and corresponding timeout values
* (in milliseconds) * (in milliseconds)
* @since 6.2 * @since 6.2
* @see SmartLifecycle#getPhase() * @see SmartLifecycle#getPhase()
* @see #setTimeoutPerShutdownPhase * @see #setTimeoutPerShutdownPhase
*/ */
public void setTimeoutsForShutdownPhases(Map<Integer, Long> timeoutsForShutdownPhases) { public void setTimeoutsForShutdownPhases(Map<Integer, Long> phasesWithTimeouts) {
this.timeoutsForShutdownPhases.putAll(timeoutsForShutdownPhases); this.timeoutsForShutdownPhases.putAll(phasesWithTimeouts);
} }
/** /**
@ -171,17 +227,15 @@ public class DefaultLifecycleProcessor implements LifecycleProcessor, BeanFactor
this.timeoutPerShutdownPhase = timeoutPerShutdownPhase; this.timeoutPerShutdownPhase = timeoutPerShutdownPhase;
} }
private long determineTimeout(int phase) {
Long timeout = this.timeoutsForShutdownPhases.get(phase);
return (timeout != null ? timeout : this.timeoutPerShutdownPhase);
}
@Override @Override
public void setBeanFactory(BeanFactory beanFactory) { public void setBeanFactory(BeanFactory beanFactory) {
if (!(beanFactory instanceof ConfigurableListableBeanFactory clbf)) { if (!(beanFactory instanceof ConfigurableListableBeanFactory clbf)) {
throw new IllegalArgumentException( throw new IllegalArgumentException(
"DefaultLifecycleProcessor requires a ConfigurableListableBeanFactory: " + beanFactory); "DefaultLifecycleProcessor requires a ConfigurableListableBeanFactory: " + beanFactory);
} }
if (!this.concurrentStartupForPhases.isEmpty() && clbf.getBootstrapExecutor() == null) {
throw new IllegalStateException("'bootstrapExecutor' needs to be configured for concurrent startup");
}
this.beanFactory = clbf; this.beanFactory = clbf;
} }
@ -191,6 +245,22 @@ public class DefaultLifecycleProcessor implements LifecycleProcessor, BeanFactor
return beanFactory; return beanFactory;
} }
private Executor getBootstrapExecutor() {
Executor executor = getBeanFactory().getBootstrapExecutor();
Assert.state(executor != null, "No 'bootstrapExecutor' available");
return executor;
}
@Nullable
private Long determineConcurrentStartup(int phase) {
return this.concurrentStartupForPhases.get(phase);
}
private long determineShutdownTimeout(int phase) {
Long timeout = this.timeoutsForShutdownPhases.get(phase);
return (timeout != null ? timeout : this.timeoutPerShutdownPhase);
}
// Lifecycle implementation // Lifecycle implementation
@ -285,9 +355,8 @@ public class DefaultLifecycleProcessor implements LifecycleProcessor, BeanFactor
lifecycleBeans.forEach((beanName, bean) -> { lifecycleBeans.forEach((beanName, bean) -> {
if (!autoStartupOnly || isAutoStartupCandidate(beanName, bean)) { if (!autoStartupOnly || isAutoStartupCandidate(beanName, bean)) {
int startupPhase = getPhase(bean); int startupPhase = getPhase(bean);
phases.computeIfAbsent(startupPhase, phases.computeIfAbsent(startupPhase, phase -> new LifecycleGroup(phase, lifecycleBeans, autoStartupOnly))
phase -> new LifecycleGroup(phase, determineTimeout(phase), lifecycleBeans, autoStartupOnly) .add(beanName, bean);
).add(beanName, bean);
} }
}); });
@ -308,14 +377,27 @@ public class DefaultLifecycleProcessor implements LifecycleProcessor, BeanFactor
* @param lifecycleBeans a Map with bean name as key and Lifecycle instance as value * @param lifecycleBeans a Map with bean name as key and Lifecycle instance as value
* @param beanName the name of the bean to start * @param beanName the name of the bean to start
*/ */
private void doStart(Map<String, ? extends Lifecycle> lifecycleBeans, String beanName, boolean autoStartupOnly) { private void doStart(Map<String, ? extends Lifecycle> lifecycleBeans, String beanName,
boolean autoStartupOnly, @Nullable List<CompletableFuture<?>> futures) {
Lifecycle bean = lifecycleBeans.remove(beanName); Lifecycle bean = lifecycleBeans.remove(beanName);
if (bean != null && bean != this) { if (bean != null && bean != this) {
String[] dependenciesForBean = getBeanFactory().getDependenciesForBean(beanName); String[] dependenciesForBean = getBeanFactory().getDependenciesForBean(beanName);
for (String dependency : dependenciesForBean) { for (String dependency : dependenciesForBean) {
doStart(lifecycleBeans, dependency, autoStartupOnly); doStart(lifecycleBeans, dependency, autoStartupOnly, futures);
} }
if (!bean.isRunning() && (!autoStartupOnly || toBeStarted(beanName, bean))) { if (!bean.isRunning() && (!autoStartupOnly || toBeStarted(beanName, bean))) {
if (futures != null) {
futures.add(CompletableFuture.runAsync(() -> doStart(beanName, bean), getBootstrapExecutor()));
}
else {
doStart(beanName, bean);
}
}
}
}
private void doStart(String beanName, Lifecycle bean) {
if (logger.isTraceEnabled()) { if (logger.isTraceEnabled()) {
logger.trace("Starting bean '" + beanName + "' of type [" + bean.getClass().getName() + "]"); logger.trace("Starting bean '" + beanName + "' of type [" + bean.getClass().getName() + "]");
} }
@ -329,8 +411,6 @@ public class DefaultLifecycleProcessor implements LifecycleProcessor, BeanFactor
logger.debug("Successfully started bean '" + beanName + "'"); logger.debug("Successfully started bean '" + beanName + "'");
} }
} }
}
}
private boolean toBeStarted(String beanName, Lifecycle bean) { private boolean toBeStarted(String beanName, Lifecycle bean) {
Set<String> stoppedBeans = this.stoppedBeans; Set<String> stoppedBeans = this.stoppedBeans;
@ -344,9 +424,8 @@ public class DefaultLifecycleProcessor implements LifecycleProcessor, BeanFactor
lifecycleBeans.forEach((beanName, bean) -> { lifecycleBeans.forEach((beanName, bean) -> {
int shutdownPhase = getPhase(bean); int shutdownPhase = getPhase(bean);
phases.computeIfAbsent(shutdownPhase, phases.computeIfAbsent(shutdownPhase, phase -> new LifecycleGroup(phase, lifecycleBeans, false))
phase -> new LifecycleGroup(phase, determineTimeout(phase), lifecycleBeans, false) .add(beanName, bean);
).add(beanName, bean);
}); });
if (!phases.isEmpty()) { if (!phases.isEmpty()) {
@ -417,7 +496,7 @@ public class DefaultLifecycleProcessor implements LifecycleProcessor, BeanFactor
} }
// overridable hooks // Overridable hooks
/** /**
* Retrieve all applicable Lifecycle beans: all singletons that have already been created, * Retrieve all applicable Lifecycle beans: all singletons that have already been created,
@ -473,8 +552,6 @@ public class DefaultLifecycleProcessor implements LifecycleProcessor, BeanFactor
private final int phase; private final int phase;
private final long timeout;
private final Map<String, ? extends Lifecycle> lifecycleBeans; private final Map<String, ? extends Lifecycle> lifecycleBeans;
private final boolean autoStartupOnly; private final boolean autoStartupOnly;
@ -483,11 +560,8 @@ public class DefaultLifecycleProcessor implements LifecycleProcessor, BeanFactor
private int smartMemberCount; private int smartMemberCount;
public LifecycleGroup( public LifecycleGroup(int phase, Map<String, ? extends Lifecycle> lifecycleBeans, boolean autoStartupOnly) {
int phase, long timeout, Map<String, ? extends Lifecycle> lifecycleBeans, boolean autoStartupOnly) {
this.phase = phase; this.phase = phase;
this.timeout = timeout;
this.lifecycleBeans = lifecycleBeans; this.lifecycleBeans = lifecycleBeans;
this.autoStartupOnly = autoStartupOnly; this.autoStartupOnly = autoStartupOnly;
} }
@ -506,8 +580,26 @@ public class DefaultLifecycleProcessor implements LifecycleProcessor, BeanFactor
if (logger.isDebugEnabled()) { if (logger.isDebugEnabled()) {
logger.debug("Starting beans in phase " + this.phase); logger.debug("Starting beans in phase " + this.phase);
} }
Long concurrentStartup = determineConcurrentStartup(this.phase);
List<CompletableFuture<?>> futures = (concurrentStartup != null ? new ArrayList<>() : null);
for (LifecycleGroupMember member : this.members) { for (LifecycleGroupMember member : this.members) {
doStart(this.lifecycleBeans, member.name, this.autoStartupOnly); doStart(this.lifecycleBeans, member.name, this.autoStartupOnly, futures);
}
if (concurrentStartup != null && !CollectionUtils.isEmpty(futures)) {
try {
CompletableFuture.allOf(futures.toArray(new CompletableFuture<?>[0]))
.get(concurrentStartup, TimeUnit.MILLISECONDS);
}
catch (Exception ex) {
if (ex instanceof ExecutionException exEx) {
Throwable cause = exEx.getCause();
if (cause instanceof ApplicationContextException acEx) {
throw acEx;
}
}
throw new ApplicationContextException("Failed to start beans in phase " + this.phase +
" within timeout of " + concurrentStartup + "ms", ex);
}
} }
} }
@ -531,11 +623,14 @@ public class DefaultLifecycleProcessor implements LifecycleProcessor, BeanFactor
} }
} }
try { try {
latch.await(this.timeout, TimeUnit.MILLISECONDS); long shutdownTimeout = determineShutdownTimeout(this.phase);
if (latch.getCount() > 0 && !countDownBeanNames.isEmpty() && logger.isInfoEnabled()) { if (!latch.await(shutdownTimeout, TimeUnit.MILLISECONDS)) {
// Count is still >0 after timeout
if (!countDownBeanNames.isEmpty() && logger.isInfoEnabled()) {
logger.info("Shutdown phase " + this.phase + " ends with " + countDownBeanNames.size() + logger.info("Shutdown phase " + this.phase + " ends with " + countDownBeanNames.size() +
" bean" + (countDownBeanNames.size() > 1 ? "s" : "") + " bean" + (countDownBeanNames.size() > 1 ? "s" : "") +
" still running after timeout of " + this.timeout + "ms: " + countDownBeanNames); " still running after timeout of " + shutdownTimeout + "ms: " + countDownBeanNames);
}
} }
} }
catch (InterruptedException ex) { catch (InterruptedException ex) {

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

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2024 the original author or authors. * Copyright 2002-2025 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -16,6 +16,7 @@
package org.springframework.context.support; package org.springframework.context.support;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CopyOnWriteArrayList;
import java.util.function.Consumer; import java.util.function.Consumer;
@ -30,6 +31,7 @@ import org.springframework.context.Lifecycle;
import org.springframework.context.LifecycleProcessor; import org.springframework.context.LifecycleProcessor;
import org.springframework.context.SmartLifecycle; import org.springframework.context.SmartLifecycle;
import org.springframework.core.testfixture.EnabledForTestGroups; import org.springframework.core.testfixture.EnabledForTestGroups;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
@ -54,10 +56,11 @@ class DefaultLifecycleProcessorTests {
@Test @Test
void customLifecycleProcessorInstance() { void customLifecycleProcessorInstance() {
StaticApplicationContext context = new StaticApplicationContext();
BeanDefinition beanDefinition = new RootBeanDefinition(DefaultLifecycleProcessor.class); BeanDefinition beanDefinition = new RootBeanDefinition(DefaultLifecycleProcessor.class);
beanDefinition.getPropertyValues().addPropertyValue("timeoutPerShutdownPhase", 1000); beanDefinition.getPropertyValues().addPropertyValue("timeoutPerShutdownPhase", 1000);
StaticApplicationContext context = new StaticApplicationContext(); context.registerBeanDefinition(StaticApplicationContext.LIFECYCLE_PROCESSOR_BEAN_NAME, beanDefinition);
context.registerBeanDefinition("lifecycleProcessor", beanDefinition);
context.refresh(); context.refresh();
LifecycleProcessor bean = context.getBean("lifecycleProcessor", LifecycleProcessor.class); LifecycleProcessor bean = context.getBean("lifecycleProcessor", LifecycleProcessor.class);
Object contextLifecycleProcessor = new DirectFieldAccessor(context).getPropertyValue("lifecycleProcessor"); Object contextLifecycleProcessor = new DirectFieldAccessor(context).getPropertyValue("lifecycleProcessor");
@ -70,11 +73,12 @@ class DefaultLifecycleProcessorTests {
@Test @Test
void singleSmartLifecycleAutoStartup() { void singleSmartLifecycleAutoStartup() {
StaticApplicationContext context = new StaticApplicationContext();
CopyOnWriteArrayList<Lifecycle> startedBeans = new CopyOnWriteArrayList<>(); CopyOnWriteArrayList<Lifecycle> startedBeans = new CopyOnWriteArrayList<>();
TestSmartLifecycleBean bean = TestSmartLifecycleBean.forStartupTests(1, startedBeans); TestSmartLifecycleBean bean = TestSmartLifecycleBean.forStartupTests(1, startedBeans);
bean.setAutoStartup(true); bean.setAutoStartup(true);
StaticApplicationContext context = new StaticApplicationContext();
context.getBeanFactory().registerSingleton("bean", bean); context.getBeanFactory().registerSingleton("bean", bean);
assertThat(bean.isRunning()).isFalse(); assertThat(bean.isRunning()).isFalse();
context.refresh(); context.refresh();
assertThat(bean.isRunning()).isTrue(); assertThat(bean.isRunning()).isTrue();
@ -114,12 +118,13 @@ class DefaultLifecycleProcessorTests {
@Test @Test
void singleSmartLifecycleAutoStartupWithFailingLifecycleBean() { void singleSmartLifecycleAutoStartupWithFailingLifecycleBean() {
StaticApplicationContext context = new StaticApplicationContext();
CopyOnWriteArrayList<Lifecycle> startedBeans = new CopyOnWriteArrayList<>(); CopyOnWriteArrayList<Lifecycle> startedBeans = new CopyOnWriteArrayList<>();
TestSmartLifecycleBean bean = TestSmartLifecycleBean.forStartupTests(1, startedBeans); TestSmartLifecycleBean bean = TestSmartLifecycleBean.forStartupTests(1, startedBeans);
bean.setAutoStartup(true); bean.setAutoStartup(true);
StaticApplicationContext context = new StaticApplicationContext();
context.getBeanFactory().registerSingleton("bean", bean); context.getBeanFactory().registerSingleton("bean", bean);
context.registerSingleton("failingBean", FailingLifecycleBean.class); context.registerSingleton("failingBean", FailingLifecycleBean.class);
assertThat(bean.isRunning()).isFalse(); assertThat(bean.isRunning()).isFalse();
assertThatExceptionOfType(ApplicationContextException.class) assertThatExceptionOfType(ApplicationContextException.class)
.isThrownBy(context::refresh).withCauseInstanceOf(IllegalStateException.class); .isThrownBy(context::refresh).withCauseInstanceOf(IllegalStateException.class);
@ -130,11 +135,12 @@ class DefaultLifecycleProcessorTests {
@Test @Test
void singleSmartLifecycleWithoutAutoStartup() { void singleSmartLifecycleWithoutAutoStartup() {
StaticApplicationContext context = new StaticApplicationContext();
CopyOnWriteArrayList<Lifecycle> startedBeans = new CopyOnWriteArrayList<>(); CopyOnWriteArrayList<Lifecycle> startedBeans = new CopyOnWriteArrayList<>();
TestSmartLifecycleBean bean = TestSmartLifecycleBean.forStartupTests(1, startedBeans); TestSmartLifecycleBean bean = TestSmartLifecycleBean.forStartupTests(1, startedBeans);
bean.setAutoStartup(false); bean.setAutoStartup(false);
StaticApplicationContext context = new StaticApplicationContext();
context.getBeanFactory().registerSingleton("bean", bean); context.getBeanFactory().registerSingleton("bean", bean);
assertThat(bean.isRunning()).isFalse(); assertThat(bean.isRunning()).isFalse();
context.refresh(); context.refresh();
assertThat(bean.isRunning()).isFalse(); assertThat(bean.isRunning()).isFalse();
@ -148,15 +154,16 @@ class DefaultLifecycleProcessorTests {
@Test @Test
void singleSmartLifecycleAutoStartupWithNonAutoStartupDependency() { void singleSmartLifecycleAutoStartupWithNonAutoStartupDependency() {
StaticApplicationContext context = new StaticApplicationContext();
CopyOnWriteArrayList<Lifecycle> startedBeans = new CopyOnWriteArrayList<>(); CopyOnWriteArrayList<Lifecycle> startedBeans = new CopyOnWriteArrayList<>();
TestSmartLifecycleBean bean = TestSmartLifecycleBean.forStartupTests(1, startedBeans); TestSmartLifecycleBean bean = TestSmartLifecycleBean.forStartupTests(1, startedBeans);
bean.setAutoStartup(true); bean.setAutoStartup(true);
TestSmartLifecycleBean dependency = TestSmartLifecycleBean.forStartupTests(1, startedBeans); TestSmartLifecycleBean dependency = TestSmartLifecycleBean.forStartupTests(1, startedBeans);
dependency.setAutoStartup(false); dependency.setAutoStartup(false);
StaticApplicationContext context = new StaticApplicationContext();
context.getBeanFactory().registerSingleton("bean", bean); context.getBeanFactory().registerSingleton("bean", bean);
context.getBeanFactory().registerSingleton("dependency", dependency); context.getBeanFactory().registerSingleton("dependency", dependency);
context.getBeanFactory().registerDependentBean("dependency", "bean"); context.getBeanFactory().registerDependentBean("dependency", "bean");
assertThat(bean.isRunning()).isFalse(); assertThat(bean.isRunning()).isFalse();
assertThat(dependency.isRunning()).isFalse(); assertThat(dependency.isRunning()).isFalse();
context.refresh(); context.refresh();
@ -169,20 +176,42 @@ class DefaultLifecycleProcessorTests {
context.close(); context.close();
} }
@Test
void singleSmartLifecycleAutoStartupWithBootstrapExecutor() {
StaticApplicationContext context = new StaticApplicationContext();
BeanDefinition beanDefinition = new RootBeanDefinition(DefaultLifecycleProcessor.class);
beanDefinition.getPropertyValues().addPropertyValue("concurrentStartupForPhases", Map.of(1, 1000));
context.registerBeanDefinition(StaticApplicationContext.LIFECYCLE_PROCESSOR_BEAN_NAME, beanDefinition);
context.registerSingleton(StaticApplicationContext.BOOTSTRAP_EXECUTOR_BEAN_NAME, ThreadPoolTaskExecutor.class);
CopyOnWriteArrayList<Lifecycle> startedBeans = new CopyOnWriteArrayList<>();
TestSmartLifecycleBean bean = TestSmartLifecycleBean.forStartupTests(1, startedBeans);
bean.setAutoStartup(true);
context.getBeanFactory().registerSingleton("bean", bean);
assertThat(bean.isRunning()).isFalse();
context.refresh();
assertThat(bean.isRunning()).isTrue();
context.stop();
assertThat(bean.isRunning()).isFalse();
assertThat(startedBeans).hasSize(1);
context.close();
}
@Test @Test
void smartLifecycleGroupStartup() { void smartLifecycleGroupStartup() {
StaticApplicationContext context = new StaticApplicationContext();
CopyOnWriteArrayList<Lifecycle> startedBeans = new CopyOnWriteArrayList<>(); CopyOnWriteArrayList<Lifecycle> startedBeans = new CopyOnWriteArrayList<>();
TestSmartLifecycleBean beanMin = TestSmartLifecycleBean.forStartupTests(Integer.MIN_VALUE, startedBeans); TestSmartLifecycleBean beanMin = TestSmartLifecycleBean.forStartupTests(Integer.MIN_VALUE, startedBeans);
TestSmartLifecycleBean bean1 = TestSmartLifecycleBean.forStartupTests(1, startedBeans); TestSmartLifecycleBean bean1 = TestSmartLifecycleBean.forStartupTests(1, startedBeans);
TestSmartLifecycleBean bean2 = TestSmartLifecycleBean.forStartupTests(2, startedBeans); TestSmartLifecycleBean bean2 = TestSmartLifecycleBean.forStartupTests(2, startedBeans);
TestSmartLifecycleBean bean3 = TestSmartLifecycleBean.forStartupTests(3, startedBeans); TestSmartLifecycleBean bean3 = TestSmartLifecycleBean.forStartupTests(3, startedBeans);
TestSmartLifecycleBean beanMax = TestSmartLifecycleBean.forStartupTests(Integer.MAX_VALUE, startedBeans); TestSmartLifecycleBean beanMax = TestSmartLifecycleBean.forStartupTests(Integer.MAX_VALUE, startedBeans);
StaticApplicationContext context = new StaticApplicationContext();
context.getBeanFactory().registerSingleton("bean3", bean3); context.getBeanFactory().registerSingleton("bean3", bean3);
context.getBeanFactory().registerSingleton("beanMin", beanMin); context.getBeanFactory().registerSingleton("beanMin", beanMin);
context.getBeanFactory().registerSingleton("bean2", bean2); context.getBeanFactory().registerSingleton("bean2", bean2);
context.getBeanFactory().registerSingleton("beanMax", beanMax); context.getBeanFactory().registerSingleton("beanMax", beanMax);
context.getBeanFactory().registerSingleton("bean1", bean1); context.getBeanFactory().registerSingleton("bean1", bean1);
assertThat(beanMin.isRunning()).isFalse(); assertThat(beanMin.isRunning()).isFalse();
assertThat(bean1.isRunning()).isFalse(); assertThat(bean1.isRunning()).isFalse();
assertThat(bean2.isRunning()).isFalse(); assertThat(bean2.isRunning()).isFalse();
@ -202,16 +231,17 @@ class DefaultLifecycleProcessorTests {
@Test @Test
void contextRefreshThenStartWithMixedBeans() { void contextRefreshThenStartWithMixedBeans() {
StaticApplicationContext context = new StaticApplicationContext();
CopyOnWriteArrayList<Lifecycle> startedBeans = new CopyOnWriteArrayList<>(); CopyOnWriteArrayList<Lifecycle> startedBeans = new CopyOnWriteArrayList<>();
TestLifecycleBean simpleBean1 = TestLifecycleBean.forStartupTests(startedBeans); TestLifecycleBean simpleBean1 = TestLifecycleBean.forStartupTests(startedBeans);
TestLifecycleBean simpleBean2 = TestLifecycleBean.forStartupTests(startedBeans); TestLifecycleBean simpleBean2 = TestLifecycleBean.forStartupTests(startedBeans);
TestSmartLifecycleBean smartBean1 = TestSmartLifecycleBean.forStartupTests(5, startedBeans); TestSmartLifecycleBean smartBean1 = TestSmartLifecycleBean.forStartupTests(5, startedBeans);
TestSmartLifecycleBean smartBean2 = TestSmartLifecycleBean.forStartupTests(-3, startedBeans); TestSmartLifecycleBean smartBean2 = TestSmartLifecycleBean.forStartupTests(-3, startedBeans);
StaticApplicationContext context = new StaticApplicationContext();
context.getBeanFactory().registerSingleton("simpleBean1", simpleBean1); context.getBeanFactory().registerSingleton("simpleBean1", simpleBean1);
context.getBeanFactory().registerSingleton("smartBean1", smartBean1); context.getBeanFactory().registerSingleton("smartBean1", smartBean1);
context.getBeanFactory().registerSingleton("simpleBean2", simpleBean2); context.getBeanFactory().registerSingleton("simpleBean2", simpleBean2);
context.getBeanFactory().registerSingleton("smartBean2", smartBean2); context.getBeanFactory().registerSingleton("smartBean2", smartBean2);
assertThat(simpleBean1.isRunning()).isFalse(); assertThat(simpleBean1.isRunning()).isFalse();
assertThat(simpleBean2.isRunning()).isFalse(); assertThat(simpleBean2.isRunning()).isFalse();
assertThat(smartBean1.isRunning()).isFalse(); assertThat(smartBean1.isRunning()).isFalse();
@ -233,16 +263,17 @@ class DefaultLifecycleProcessorTests {
@Test @Test
void contextRefreshThenStopAndRestartWithMixedBeans() { void contextRefreshThenStopAndRestartWithMixedBeans() {
StaticApplicationContext context = new StaticApplicationContext();
CopyOnWriteArrayList<Lifecycle> startedBeans = new CopyOnWriteArrayList<>(); CopyOnWriteArrayList<Lifecycle> startedBeans = new CopyOnWriteArrayList<>();
TestLifecycleBean simpleBean1 = TestLifecycleBean.forStartupTests(startedBeans); TestLifecycleBean simpleBean1 = TestLifecycleBean.forStartupTests(startedBeans);
TestLifecycleBean simpleBean2 = TestLifecycleBean.forStartupTests(startedBeans); TestLifecycleBean simpleBean2 = TestLifecycleBean.forStartupTests(startedBeans);
TestSmartLifecycleBean smartBean1 = TestSmartLifecycleBean.forStartupTests(5, startedBeans); TestSmartLifecycleBean smartBean1 = TestSmartLifecycleBean.forStartupTests(5, startedBeans);
TestSmartLifecycleBean smartBean2 = TestSmartLifecycleBean.forStartupTests(-3, startedBeans); TestSmartLifecycleBean smartBean2 = TestSmartLifecycleBean.forStartupTests(-3, startedBeans);
StaticApplicationContext context = new StaticApplicationContext();
context.getBeanFactory().registerSingleton("simpleBean1", simpleBean1); context.getBeanFactory().registerSingleton("simpleBean1", simpleBean1);
context.getBeanFactory().registerSingleton("smartBean1", smartBean1); context.getBeanFactory().registerSingleton("smartBean1", smartBean1);
context.getBeanFactory().registerSingleton("simpleBean2", simpleBean2); context.getBeanFactory().registerSingleton("simpleBean2", simpleBean2);
context.getBeanFactory().registerSingleton("smartBean2", smartBean2); context.getBeanFactory().registerSingleton("smartBean2", smartBean2);
assertThat(simpleBean1.isRunning()).isFalse(); assertThat(simpleBean1.isRunning()).isFalse();
assertThat(simpleBean2.isRunning()).isFalse(); assertThat(simpleBean2.isRunning()).isFalse();
assertThat(smartBean1.isRunning()).isFalse(); assertThat(smartBean1.isRunning()).isFalse();
@ -270,16 +301,17 @@ class DefaultLifecycleProcessorTests {
@Test @Test
void contextRefreshThenStopForRestartWithMixedBeans() { void contextRefreshThenStopForRestartWithMixedBeans() {
StaticApplicationContext context = new StaticApplicationContext();
CopyOnWriteArrayList<Lifecycle> startedBeans = new CopyOnWriteArrayList<>(); CopyOnWriteArrayList<Lifecycle> startedBeans = new CopyOnWriteArrayList<>();
TestLifecycleBean simpleBean1 = TestLifecycleBean.forStartupTests(startedBeans); TestLifecycleBean simpleBean1 = TestLifecycleBean.forStartupTests(startedBeans);
TestLifecycleBean simpleBean2 = TestLifecycleBean.forStartupTests(startedBeans); TestLifecycleBean simpleBean2 = TestLifecycleBean.forStartupTests(startedBeans);
TestSmartLifecycleBean smartBean1 = TestSmartLifecycleBean.forStartupTests(5, startedBeans); TestSmartLifecycleBean smartBean1 = TestSmartLifecycleBean.forStartupTests(5, startedBeans);
TestSmartLifecycleBean smartBean2 = TestSmartLifecycleBean.forStartupTests(-3, startedBeans); TestSmartLifecycleBean smartBean2 = TestSmartLifecycleBean.forStartupTests(-3, startedBeans);
StaticApplicationContext context = new StaticApplicationContext();
context.getBeanFactory().registerSingleton("simpleBean1", simpleBean1); context.getBeanFactory().registerSingleton("simpleBean1", simpleBean1);
context.getBeanFactory().registerSingleton("smartBean1", smartBean1); context.getBeanFactory().registerSingleton("smartBean1", smartBean1);
context.getBeanFactory().registerSingleton("simpleBean2", simpleBean2); context.getBeanFactory().registerSingleton("simpleBean2", simpleBean2);
context.getBeanFactory().registerSingleton("smartBean2", smartBean2); context.getBeanFactory().registerSingleton("smartBean2", smartBean2);
assertThat(simpleBean1.isRunning()).isFalse(); assertThat(simpleBean1.isRunning()).isFalse();
assertThat(simpleBean2.isRunning()).isFalse(); assertThat(simpleBean2.isRunning()).isFalse();
assertThat(smartBean1.isRunning()).isFalse(); assertThat(smartBean1.isRunning()).isFalse();
@ -319,6 +351,7 @@ class DefaultLifecycleProcessorTests {
@Test @Test
@EnabledForTestGroups(LONG_RUNNING) @EnabledForTestGroups(LONG_RUNNING)
void smartLifecycleGroupShutdown() { void smartLifecycleGroupShutdown() {
StaticApplicationContext context = new StaticApplicationContext();
CopyOnWriteArrayList<Lifecycle> stoppedBeans = new CopyOnWriteArrayList<>(); CopyOnWriteArrayList<Lifecycle> stoppedBeans = new CopyOnWriteArrayList<>();
TestSmartLifecycleBean bean1 = TestSmartLifecycleBean.forShutdownTests(1, 300, stoppedBeans); TestSmartLifecycleBean bean1 = TestSmartLifecycleBean.forShutdownTests(1, 300, stoppedBeans);
TestSmartLifecycleBean bean2 = TestSmartLifecycleBean.forShutdownTests(3, 100, stoppedBeans); TestSmartLifecycleBean bean2 = TestSmartLifecycleBean.forShutdownTests(3, 100, stoppedBeans);
@ -327,7 +360,6 @@ class DefaultLifecycleProcessorTests {
TestSmartLifecycleBean bean5 = TestSmartLifecycleBean.forShutdownTests(2, 700, stoppedBeans); TestSmartLifecycleBean bean5 = TestSmartLifecycleBean.forShutdownTests(2, 700, stoppedBeans);
TestSmartLifecycleBean bean6 = TestSmartLifecycleBean.forShutdownTests(Integer.MAX_VALUE, 200, stoppedBeans); TestSmartLifecycleBean bean6 = TestSmartLifecycleBean.forShutdownTests(Integer.MAX_VALUE, 200, stoppedBeans);
TestSmartLifecycleBean bean7 = TestSmartLifecycleBean.forShutdownTests(3, 200, stoppedBeans); TestSmartLifecycleBean bean7 = TestSmartLifecycleBean.forShutdownTests(3, 200, stoppedBeans);
StaticApplicationContext context = new StaticApplicationContext();
context.getBeanFactory().registerSingleton("bean1", bean1); context.getBeanFactory().registerSingleton("bean1", bean1);
context.getBeanFactory().registerSingleton("bean2", bean2); context.getBeanFactory().registerSingleton("bean2", bean2);
context.getBeanFactory().registerSingleton("bean3", bean3); context.getBeanFactory().registerSingleton("bean3", bean3);
@ -335,6 +367,7 @@ class DefaultLifecycleProcessorTests {
context.getBeanFactory().registerSingleton("bean5", bean5); context.getBeanFactory().registerSingleton("bean5", bean5);
context.getBeanFactory().registerSingleton("bean6", bean6); context.getBeanFactory().registerSingleton("bean6", bean6);
context.getBeanFactory().registerSingleton("bean7", bean7); context.getBeanFactory().registerSingleton("bean7", bean7);
context.refresh(); context.refresh();
context.stop(); context.stop();
assertThat(stoppedBeans).satisfiesExactly(hasPhase(Integer.MAX_VALUE), hasPhase(3), assertThat(stoppedBeans).satisfiesExactly(hasPhase(Integer.MAX_VALUE), hasPhase(3),
@ -345,11 +378,12 @@ class DefaultLifecycleProcessorTests {
@Test @Test
@EnabledForTestGroups(LONG_RUNNING) @EnabledForTestGroups(LONG_RUNNING)
void singleSmartLifecycleShutdown() { void singleSmartLifecycleShutdown() {
StaticApplicationContext context = new StaticApplicationContext();
CopyOnWriteArrayList<Lifecycle> stoppedBeans = new CopyOnWriteArrayList<>(); CopyOnWriteArrayList<Lifecycle> stoppedBeans = new CopyOnWriteArrayList<>();
TestSmartLifecycleBean bean = TestSmartLifecycleBean.forShutdownTests(99, 300, stoppedBeans); TestSmartLifecycleBean bean = TestSmartLifecycleBean.forShutdownTests(99, 300, stoppedBeans);
StaticApplicationContext context = new StaticApplicationContext();
context.getBeanFactory().registerSingleton("bean", bean); context.getBeanFactory().registerSingleton("bean", bean);
context.refresh(); context.refresh();
assertThat(bean.isRunning()).isTrue(); assertThat(bean.isRunning()).isTrue();
context.stop(); context.stop();
assertThat(bean.isRunning()).isFalse(); assertThat(bean.isRunning()).isFalse();
@ -359,10 +393,11 @@ class DefaultLifecycleProcessorTests {
@Test @Test
void singleLifecycleShutdown() { void singleLifecycleShutdown() {
StaticApplicationContext context = new StaticApplicationContext();
CopyOnWriteArrayList<Lifecycle> stoppedBeans = new CopyOnWriteArrayList<>(); CopyOnWriteArrayList<Lifecycle> stoppedBeans = new CopyOnWriteArrayList<>();
Lifecycle bean = new TestLifecycleBean(null, stoppedBeans); Lifecycle bean = new TestLifecycleBean(null, stoppedBeans);
StaticApplicationContext context = new StaticApplicationContext();
context.getBeanFactory().registerSingleton("bean", bean); context.getBeanFactory().registerSingleton("bean", bean);
context.refresh(); context.refresh();
assertThat(bean.isRunning()).isFalse(); assertThat(bean.isRunning()).isFalse();
bean.start(); bean.start();
@ -375,6 +410,7 @@ class DefaultLifecycleProcessorTests {
@Test @Test
void mixedShutdown() { void mixedShutdown() {
StaticApplicationContext context = new StaticApplicationContext();
CopyOnWriteArrayList<Lifecycle> stoppedBeans = new CopyOnWriteArrayList<>(); CopyOnWriteArrayList<Lifecycle> stoppedBeans = new CopyOnWriteArrayList<>();
Lifecycle bean1 = TestLifecycleBean.forShutdownTests(stoppedBeans); Lifecycle bean1 = TestLifecycleBean.forShutdownTests(stoppedBeans);
Lifecycle bean2 = TestSmartLifecycleBean.forShutdownTests(500, 200, stoppedBeans); Lifecycle bean2 = TestSmartLifecycleBean.forShutdownTests(500, 200, stoppedBeans);
@ -383,7 +419,6 @@ class DefaultLifecycleProcessorTests {
Lifecycle bean5 = TestSmartLifecycleBean.forShutdownTests(1, 200, stoppedBeans); Lifecycle bean5 = TestSmartLifecycleBean.forShutdownTests(1, 200, stoppedBeans);
Lifecycle bean6 = TestSmartLifecycleBean.forShutdownTests(-1, 100, stoppedBeans); Lifecycle bean6 = TestSmartLifecycleBean.forShutdownTests(-1, 100, stoppedBeans);
Lifecycle bean7 = TestSmartLifecycleBean.forShutdownTests(Integer.MIN_VALUE, 300, stoppedBeans); Lifecycle bean7 = TestSmartLifecycleBean.forShutdownTests(Integer.MIN_VALUE, 300, stoppedBeans);
StaticApplicationContext context = new StaticApplicationContext();
context.getBeanFactory().registerSingleton("bean1", bean1); context.getBeanFactory().registerSingleton("bean1", bean1);
context.getBeanFactory().registerSingleton("bean2", bean2); context.getBeanFactory().registerSingleton("bean2", bean2);
context.getBeanFactory().registerSingleton("bean3", bean3); context.getBeanFactory().registerSingleton("bean3", bean3);
@ -391,6 +426,7 @@ class DefaultLifecycleProcessorTests {
context.getBeanFactory().registerSingleton("bean5", bean5); context.getBeanFactory().registerSingleton("bean5", bean5);
context.getBeanFactory().registerSingleton("bean6", bean6); context.getBeanFactory().registerSingleton("bean6", bean6);
context.getBeanFactory().registerSingleton("bean7", bean7); context.getBeanFactory().registerSingleton("bean7", bean7);
context.refresh(); context.refresh();
assertThat(bean2.isRunning()).isTrue(); assertThat(bean2.isRunning()).isTrue();
assertThat(bean3.isRunning()).isTrue(); assertThat(bean3.isRunning()).isTrue();
@ -418,17 +454,18 @@ class DefaultLifecycleProcessorTests {
@Test @Test
void dependencyStartedFirstEvenIfItsPhaseIsHigher() { void dependencyStartedFirstEvenIfItsPhaseIsHigher() {
StaticApplicationContext context = new StaticApplicationContext();
CopyOnWriteArrayList<Lifecycle> startedBeans = new CopyOnWriteArrayList<>(); CopyOnWriteArrayList<Lifecycle> startedBeans = new CopyOnWriteArrayList<>();
TestSmartLifecycleBean beanMin = TestSmartLifecycleBean.forStartupTests(Integer.MIN_VALUE, startedBeans); TestSmartLifecycleBean beanMin = TestSmartLifecycleBean.forStartupTests(Integer.MIN_VALUE, startedBeans);
TestSmartLifecycleBean bean2 = TestSmartLifecycleBean.forStartupTests(2, startedBeans); TestSmartLifecycleBean bean2 = TestSmartLifecycleBean.forStartupTests(2, startedBeans);
TestSmartLifecycleBean bean99 = TestSmartLifecycleBean.forStartupTests(99, startedBeans); TestSmartLifecycleBean bean99 = TestSmartLifecycleBean.forStartupTests(99, startedBeans);
TestSmartLifecycleBean beanMax = TestSmartLifecycleBean.forStartupTests(Integer.MAX_VALUE, startedBeans); TestSmartLifecycleBean beanMax = TestSmartLifecycleBean.forStartupTests(Integer.MAX_VALUE, startedBeans);
StaticApplicationContext context = new StaticApplicationContext();
context.getBeanFactory().registerSingleton("beanMin", beanMin); context.getBeanFactory().registerSingleton("beanMin", beanMin);
context.getBeanFactory().registerSingleton("bean2", bean2); context.getBeanFactory().registerSingleton("bean2", bean2);
context.getBeanFactory().registerSingleton("bean99", bean99); context.getBeanFactory().registerSingleton("bean99", bean99);
context.getBeanFactory().registerSingleton("beanMax", beanMax); context.getBeanFactory().registerSingleton("beanMax", beanMax);
context.getBeanFactory().registerDependentBean("bean99", "bean2"); context.getBeanFactory().registerDependentBean("bean99", "bean2");
context.refresh(); context.refresh();
assertThat(beanMin.isRunning()).isTrue(); assertThat(beanMin.isRunning()).isTrue();
assertThat(bean2.isRunning()).isTrue(); assertThat(bean2.isRunning()).isTrue();
@ -446,6 +483,7 @@ class DefaultLifecycleProcessorTests {
@Test @Test
@EnabledForTestGroups(LONG_RUNNING) @EnabledForTestGroups(LONG_RUNNING)
void dependentShutdownFirstEvenIfItsPhaseIsLower() { void dependentShutdownFirstEvenIfItsPhaseIsLower() {
StaticApplicationContext context = new StaticApplicationContext();
CopyOnWriteArrayList<Lifecycle> stoppedBeans = new CopyOnWriteArrayList<>(); CopyOnWriteArrayList<Lifecycle> stoppedBeans = new CopyOnWriteArrayList<>();
TestSmartLifecycleBean beanMin = TestSmartLifecycleBean.forShutdownTests(Integer.MIN_VALUE, 100, stoppedBeans); TestSmartLifecycleBean beanMin = TestSmartLifecycleBean.forShutdownTests(Integer.MIN_VALUE, 100, stoppedBeans);
TestSmartLifecycleBean bean1 = TestSmartLifecycleBean.forShutdownTests(1, 200, stoppedBeans); TestSmartLifecycleBean bean1 = TestSmartLifecycleBean.forShutdownTests(1, 200, stoppedBeans);
@ -453,7 +491,6 @@ class DefaultLifecycleProcessorTests {
TestSmartLifecycleBean bean2 = TestSmartLifecycleBean.forShutdownTests(2, 300, stoppedBeans); TestSmartLifecycleBean bean2 = TestSmartLifecycleBean.forShutdownTests(2, 300, stoppedBeans);
TestSmartLifecycleBean bean7 = TestSmartLifecycleBean.forShutdownTests(7, 400, stoppedBeans); TestSmartLifecycleBean bean7 = TestSmartLifecycleBean.forShutdownTests(7, 400, stoppedBeans);
TestSmartLifecycleBean beanMax = TestSmartLifecycleBean.forShutdownTests(Integer.MAX_VALUE, 400, stoppedBeans); TestSmartLifecycleBean beanMax = TestSmartLifecycleBean.forShutdownTests(Integer.MAX_VALUE, 400, stoppedBeans);
StaticApplicationContext context = new StaticApplicationContext();
context.getBeanFactory().registerSingleton("beanMin", beanMin); context.getBeanFactory().registerSingleton("beanMin", beanMin);
context.getBeanFactory().registerSingleton("bean1", bean1); context.getBeanFactory().registerSingleton("bean1", bean1);
context.getBeanFactory().registerSingleton("bean2", bean2); context.getBeanFactory().registerSingleton("bean2", bean2);
@ -461,6 +498,7 @@ class DefaultLifecycleProcessorTests {
context.getBeanFactory().registerSingleton("bean99", bean99); context.getBeanFactory().registerSingleton("bean99", bean99);
context.getBeanFactory().registerSingleton("beanMax", beanMax); context.getBeanFactory().registerSingleton("beanMax", beanMax);
context.getBeanFactory().registerDependentBean("bean99", "bean2"); context.getBeanFactory().registerDependentBean("bean99", "bean2");
context.refresh(); context.refresh();
assertThat(beanMin.isRunning()).isTrue(); assertThat(beanMin.isRunning()).isTrue();
assertThat(bean1.isRunning()).isTrue(); assertThat(bean1.isRunning()).isTrue();
@ -486,17 +524,18 @@ class DefaultLifecycleProcessorTests {
@Test @Test
void dependencyStartedFirstAndIsSmartLifecycle() { void dependencyStartedFirstAndIsSmartLifecycle() {
StaticApplicationContext context = new StaticApplicationContext();
CopyOnWriteArrayList<Lifecycle> startedBeans = new CopyOnWriteArrayList<>(); CopyOnWriteArrayList<Lifecycle> startedBeans = new CopyOnWriteArrayList<>();
TestSmartLifecycleBean beanNegative = TestSmartLifecycleBean.forStartupTests(-99, startedBeans); TestSmartLifecycleBean beanNegative = TestSmartLifecycleBean.forStartupTests(-99, startedBeans);
TestSmartLifecycleBean bean99 = TestSmartLifecycleBean.forStartupTests(99, startedBeans); TestSmartLifecycleBean bean99 = TestSmartLifecycleBean.forStartupTests(99, startedBeans);
TestSmartLifecycleBean bean7 = TestSmartLifecycleBean.forStartupTests(7, startedBeans); TestSmartLifecycleBean bean7 = TestSmartLifecycleBean.forStartupTests(7, startedBeans);
TestLifecycleBean simpleBean = TestLifecycleBean.forStartupTests(startedBeans); TestLifecycleBean simpleBean = TestLifecycleBean.forStartupTests(startedBeans);
StaticApplicationContext context = new StaticApplicationContext();
context.getBeanFactory().registerSingleton("beanNegative", beanNegative); context.getBeanFactory().registerSingleton("beanNegative", beanNegative);
context.getBeanFactory().registerSingleton("bean7", bean7); context.getBeanFactory().registerSingleton("bean7", bean7);
context.getBeanFactory().registerSingleton("bean99", bean99); context.getBeanFactory().registerSingleton("bean99", bean99);
context.getBeanFactory().registerSingleton("simpleBean", simpleBean); context.getBeanFactory().registerSingleton("simpleBean", simpleBean);
context.getBeanFactory().registerDependentBean("bean7", "simpleBean"); context.getBeanFactory().registerDependentBean("bean7", "simpleBean");
context.refresh(); context.refresh();
context.stop(); context.stop();
startedBeans.clear(); startedBeans.clear();
@ -514,6 +553,7 @@ class DefaultLifecycleProcessorTests {
@Test @Test
@EnabledForTestGroups(LONG_RUNNING) @EnabledForTestGroups(LONG_RUNNING)
void dependentShutdownFirstAndIsSmartLifecycle() { void dependentShutdownFirstAndIsSmartLifecycle() {
StaticApplicationContext context = new StaticApplicationContext();
CopyOnWriteArrayList<Lifecycle> stoppedBeans = new CopyOnWriteArrayList<>(); CopyOnWriteArrayList<Lifecycle> stoppedBeans = new CopyOnWriteArrayList<>();
TestSmartLifecycleBean beanMin = TestSmartLifecycleBean.forShutdownTests(Integer.MIN_VALUE, 400, stoppedBeans); TestSmartLifecycleBean beanMin = TestSmartLifecycleBean.forShutdownTests(Integer.MIN_VALUE, 400, stoppedBeans);
TestSmartLifecycleBean beanNegative = TestSmartLifecycleBean.forShutdownTests(-99, 100, stoppedBeans); TestSmartLifecycleBean beanNegative = TestSmartLifecycleBean.forShutdownTests(-99, 100, stoppedBeans);
@ -521,7 +561,6 @@ class DefaultLifecycleProcessorTests {
TestSmartLifecycleBean bean2 = TestSmartLifecycleBean.forShutdownTests(2, 300, stoppedBeans); TestSmartLifecycleBean bean2 = TestSmartLifecycleBean.forShutdownTests(2, 300, stoppedBeans);
TestSmartLifecycleBean bean7 = TestSmartLifecycleBean.forShutdownTests(7, 400, stoppedBeans); TestSmartLifecycleBean bean7 = TestSmartLifecycleBean.forShutdownTests(7, 400, stoppedBeans);
TestLifecycleBean simpleBean = TestLifecycleBean.forShutdownTests(stoppedBeans); TestLifecycleBean simpleBean = TestLifecycleBean.forShutdownTests(stoppedBeans);
StaticApplicationContext context = new StaticApplicationContext();
context.getBeanFactory().registerSingleton("beanMin", beanMin); context.getBeanFactory().registerSingleton("beanMin", beanMin);
context.getBeanFactory().registerSingleton("beanNegative", beanNegative); context.getBeanFactory().registerSingleton("beanNegative", beanNegative);
context.getBeanFactory().registerSingleton("bean1", bean1); context.getBeanFactory().registerSingleton("bean1", bean1);
@ -529,6 +568,7 @@ class DefaultLifecycleProcessorTests {
context.getBeanFactory().registerSingleton("bean7", bean7); context.getBeanFactory().registerSingleton("bean7", bean7);
context.getBeanFactory().registerSingleton("simpleBean", simpleBean); context.getBeanFactory().registerSingleton("simpleBean", simpleBean);
context.getBeanFactory().registerDependentBean("simpleBean", "beanNegative"); context.getBeanFactory().registerDependentBean("simpleBean", "beanNegative");
context.refresh(); context.refresh();
assertThat(beanMin.isRunning()).isTrue(); assertThat(beanMin.isRunning()).isTrue();
assertThat(beanNegative.isRunning()).isTrue(); assertThat(beanNegative.isRunning()).isTrue();
@ -551,15 +591,16 @@ class DefaultLifecycleProcessorTests {
@Test @Test
void dependencyStartedFirstButNotSmartLifecycle() { void dependencyStartedFirstButNotSmartLifecycle() {
StaticApplicationContext context = new StaticApplicationContext();
CopyOnWriteArrayList<Lifecycle> startedBeans = new CopyOnWriteArrayList<>(); CopyOnWriteArrayList<Lifecycle> startedBeans = new CopyOnWriteArrayList<>();
TestSmartLifecycleBean beanMin = TestSmartLifecycleBean.forStartupTests(Integer.MIN_VALUE, startedBeans); TestSmartLifecycleBean beanMin = TestSmartLifecycleBean.forStartupTests(Integer.MIN_VALUE, startedBeans);
TestSmartLifecycleBean bean7 = TestSmartLifecycleBean.forStartupTests(7, startedBeans); TestSmartLifecycleBean bean7 = TestSmartLifecycleBean.forStartupTests(7, startedBeans);
TestLifecycleBean simpleBean = TestLifecycleBean.forStartupTests(startedBeans); TestLifecycleBean simpleBean = TestLifecycleBean.forStartupTests(startedBeans);
StaticApplicationContext context = new StaticApplicationContext();
context.getBeanFactory().registerSingleton("beanMin", beanMin); context.getBeanFactory().registerSingleton("beanMin", beanMin);
context.getBeanFactory().registerSingleton("bean7", bean7); context.getBeanFactory().registerSingleton("bean7", bean7);
context.getBeanFactory().registerSingleton("simpleBean", simpleBean); context.getBeanFactory().registerSingleton("simpleBean", simpleBean);
context.getBeanFactory().registerDependentBean("simpleBean", "beanMin"); context.getBeanFactory().registerDependentBean("simpleBean", "beanMin");
context.refresh(); context.refresh();
assertThat(beanMin.isRunning()).isTrue(); assertThat(beanMin.isRunning()).isTrue();
assertThat(bean7.isRunning()).isTrue(); assertThat(bean7.isRunning()).isTrue();
@ -572,19 +613,20 @@ class DefaultLifecycleProcessorTests {
@Test @Test
@EnabledForTestGroups(LONG_RUNNING) @EnabledForTestGroups(LONG_RUNNING)
void dependentShutdownFirstButNotSmartLifecycle() { void dependentShutdownFirstButNotSmartLifecycle() {
StaticApplicationContext context = new StaticApplicationContext();
CopyOnWriteArrayList<Lifecycle> stoppedBeans = new CopyOnWriteArrayList<>(); CopyOnWriteArrayList<Lifecycle> stoppedBeans = new CopyOnWriteArrayList<>();
TestSmartLifecycleBean bean1 = TestSmartLifecycleBean.forShutdownTests(1, 200, stoppedBeans); TestSmartLifecycleBean bean1 = TestSmartLifecycleBean.forShutdownTests(1, 200, stoppedBeans);
TestLifecycleBean simpleBean = TestLifecycleBean.forShutdownTests(stoppedBeans); TestLifecycleBean simpleBean = TestLifecycleBean.forShutdownTests(stoppedBeans);
TestSmartLifecycleBean bean2 = TestSmartLifecycleBean.forShutdownTests(2, 300, stoppedBeans); TestSmartLifecycleBean bean2 = TestSmartLifecycleBean.forShutdownTests(2, 300, stoppedBeans);
TestSmartLifecycleBean bean7 = TestSmartLifecycleBean.forShutdownTests(7, 400, stoppedBeans); TestSmartLifecycleBean bean7 = TestSmartLifecycleBean.forShutdownTests(7, 400, stoppedBeans);
TestSmartLifecycleBean beanMin = TestSmartLifecycleBean.forShutdownTests(Integer.MIN_VALUE, 400, stoppedBeans); TestSmartLifecycleBean beanMin = TestSmartLifecycleBean.forShutdownTests(Integer.MIN_VALUE, 400, stoppedBeans);
StaticApplicationContext context = new StaticApplicationContext();
context.getBeanFactory().registerSingleton("beanMin", beanMin); context.getBeanFactory().registerSingleton("beanMin", beanMin);
context.getBeanFactory().registerSingleton("bean1", bean1); context.getBeanFactory().registerSingleton("bean1", bean1);
context.getBeanFactory().registerSingleton("bean2", bean2); context.getBeanFactory().registerSingleton("bean2", bean2);
context.getBeanFactory().registerSingleton("bean7", bean7); context.getBeanFactory().registerSingleton("bean7", bean7);
context.getBeanFactory().registerSingleton("simpleBean", simpleBean); context.getBeanFactory().registerSingleton("simpleBean", simpleBean);
context.getBeanFactory().registerDependentBean("bean2", "simpleBean"); context.getBeanFactory().registerDependentBean("bean2", "simpleBean");
context.refresh(); context.refresh();
assertThat(beanMin.isRunning()).isTrue(); assertThat(beanMin.isRunning()).isTrue();
assertThat(bean1.isRunning()).isTrue(); assertThat(bean1.isRunning()).isTrue();
@ -611,6 +653,7 @@ class DefaultLifecycleProcessorTests {
}; };
} }
private static class TestLifecycleBean implements Lifecycle { private static class TestLifecycleBean implements Lifecycle {
private final CopyOnWriteArrayList<Lifecycle> startedBeans; private final CopyOnWriteArrayList<Lifecycle> startedBeans;

Loading…
Cancel
Save