Browse Source

SmartLifecycle beans will get auto-started on demand even if marked as lazy-init (SPR-6515)

git-svn-id: https://src.springframework.org/svn/spring-framework/trunk@2602 50f2f4bb-b051-0410-bef5-90022cba6387
pull/1/head
Juergen Hoeller 16 years ago
parent
commit
aac72ddbed
  1. 8
      org.springframework.context/src/main/java/org/springframework/context/Lifecycle.java
  2. 27
      org.springframework.context/src/main/java/org/springframework/context/SmartLifecycle.java
  3. 86
      org.springframework.context/src/main/java/org/springframework/context/support/DefaultLifecycleProcessor.java
  4. 53
      org.springframework.context/src/test/java/org/springframework/context/support/DefaultLifecycleProcessorTests.java

8
org.springframework.context/src/main/java/org/springframework/context/Lifecycle.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2007 the original author or authors.
* Copyright 2002-2009 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.
@ -32,8 +32,14 @@ package org.springframework.context; @@ -32,8 +32,14 @@ package org.springframework.context;
* restricting the visibility of activity-controlled components to the Lifecycle
* interface.
*
* <p>Note that the Lifecycle interface is only supported on <b>top-level singleton beans</b>.
* On any other component, the Lifecycle interface will remain undetected and hence ignored.
* Also, note that the extended {@link SmartLifecycle} interface provides more sophisticated
* integration with the container's startup and shutdown phases.
*
* @author Juergen Hoeller
* @since 2.0
* @see SmartLifecycle
* @see ConfigurableApplicationContext
* @see org.springframework.jms.listener.AbstractMessageListenerContainer
* @see org.springframework.scheduling.quartz.SchedulerFactoryBean

27
org.springframework.context/src/main/java/org/springframework/context/SmartLifecycle.java

@ -24,30 +24,35 @@ package org.springframework.context; @@ -24,30 +24,35 @@ package org.springframework.context;
* {@link #stop(Runnable)} method is useful for objects that have an asynchronous
* shutdown process. Any implementation of this interface <i>must</i> invoke the
* callback's run() method upon shutdown completion to avoid unnecessary delays
* in the overall ApplicationContext shutdown.
* <p>
* This interface extends {@link Phased}, and the {@link #getPhase()} method's
* in the overall ApplicationContext shutdown.
*
* <p>This interface extends {@link Phased}, and the {@link #getPhase()} method's
* return value indicates the phase within which this Lifecycle component should
* be started and stopped. The startup process begins with the <i>lowest</i>
* phase value and ends with the <i>highest</i> phase value (Integer.MIN_VALUE
* is the lowest possible, and Integer.MAX_VALUE is the highest possible). The
* shutdown process will apply the reverse order. Any components with the
* same value will be arbitrarily ordered within the same phase.
* <p>
* Example: if component B depends on component A having already started, then
*
* <p>Example: if component B depends on component A having already started, then
* component A should have a lower phase value than component B. During the
* shutdown process, component B would be stopped before component A.
* <p>
* Any explicit "depends-on" relationship will take precedence over
*
* <p>Any explicit "depends-on" relationship will take precedence over
* the phase order such that the dependent bean always starts after its
* dependency and always stops before its dependency.
* <p>
* Any Lifecycle components within the context that do not also implement
*
* <p>Any Lifecycle components within the context that do not also implement
* SmartLifecycle will be treated as if they have a phase value of 0. That
* way a SmartLifecycle implementation may start before those Lifecycle
* components if it has a negative phase value, or it may start after
* those components if it has a positive phase value.
*
*
* <p>Note that, due to the auto-startup support in SmartLifecycle,
* a SmartLifecycle bean instance will get initialized on startup of the
* application context in any case. As a consequence, the bean definition
* lazy-init flag has very limited actual effect on SmartLifecycle beans.
*
* @author Mark Fisher
* @since 3.0
*/
@ -62,7 +67,7 @@ public interface SmartLifecycle extends Lifecycle, Phased { @@ -62,7 +67,7 @@ public interface SmartLifecycle extends Lifecycle, Phased {
/**
* Indicates that a Lifecycle component must stop if it is currently running.
* The provided callback is used by the LifecycleProcessor to support an
* <p>The provided callback is used by the LifecycleProcessor to support an
* ordered, and potentially concurrent, shutdown of all components having a
* common shutdown order value. The callback <b>must</b> be executed after
* the SmartLifecycle component does indeed stop.

86
org.springframework.context/src/main/java/org/springframework/context/support/DefaultLifecycleProcessor.java

@ -39,8 +39,9 @@ import org.springframework.util.Assert; @@ -39,8 +39,9 @@ import org.springframework.util.Assert;
/**
* Default implementation of the {@link LifecycleProcessor} strategy.
*
*
* @author Mark Fisher
* @author Juergen Hoeller
* @since 3.0
*/
public class DefaultLifecycleProcessor implements LifecycleProcessor, BeanFactoryAware {
@ -64,18 +65,12 @@ public class DefaultLifecycleProcessor implements LifecycleProcessor, BeanFactor @@ -64,18 +65,12 @@ public class DefaultLifecycleProcessor implements LifecycleProcessor, BeanFactor
}
public void setBeanFactory(BeanFactory beanFactory) {
Assert.isTrue(beanFactory instanceof ConfigurableListableBeanFactory,
"A ConfigurableListableBeanFactory is required.");
Assert.isInstanceOf(ConfigurableListableBeanFactory.class, beanFactory);
this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;
}
/*
* Lifecycle implementation
*/
public boolean isRunning() {
return this.running;
}
// Lifecycle implementation
/**
* Start all registered beans that implement Lifecycle and are
@ -87,7 +82,8 @@ public class DefaultLifecycleProcessor implements LifecycleProcessor, BeanFactor @@ -87,7 +82,8 @@ public class DefaultLifecycleProcessor implements LifecycleProcessor, BeanFactor
* the dependent bean regardless of the declared phase.
*/
public void start() {
this.startBeans(false);
startBeans(false);
this.running = true;
}
/**
@ -100,36 +96,27 @@ public class DefaultLifecycleProcessor implements LifecycleProcessor, BeanFactor @@ -100,36 +96,27 @@ public class DefaultLifecycleProcessor implements LifecycleProcessor, BeanFactor
* the dependency bean regardless of the declared phase.
*/
public void stop() {
Map<String, Lifecycle> lifecycleBeans = getLifecycleBeans();
Map<Integer, LifecycleGroup> phases = new HashMap<Integer, LifecycleGroup>();
for (Map.Entry<String, Lifecycle> entry : lifecycleBeans.entrySet()) {
Lifecycle lifecycle = entry.getValue();
int shutdownOrder = getPhase(lifecycle);
LifecycleGroup group = phases.get(shutdownOrder);
if (group == null) {
group = new LifecycleGroup(shutdownOrder, this.timeoutPerShutdownPhase, lifecycleBeans);
phases.put(shutdownOrder, group);
}
group.add(entry.getKey(), lifecycle);
}
if (phases.size() > 0) {
List<Integer> keys = new ArrayList<Integer>(phases.keySet());
Collections.sort(keys, Collections.reverseOrder());
for (Integer key : keys) {
phases.get(key).stop();
}
}
stopBeans();
this.running = false;
}
public void onRefresh() {
this.startBeans(true);
startBeans(true);
this.running = true;
}
public void onClose() {
stop();
stopBeans();
this.running = false;
}
public boolean isRunning() {
return this.running;
}
// internal helpers
private void startBeans(boolean autoStartupOnly) {
Map<String, Lifecycle> lifecycleBeans = getLifecycleBeans();
Map<Integer, LifecycleGroup> phases = new HashMap<Integer, LifecycleGroup>();
@ -152,7 +139,6 @@ public class DefaultLifecycleProcessor implements LifecycleProcessor, BeanFactor @@ -152,7 +139,6 @@ public class DefaultLifecycleProcessor implements LifecycleProcessor, BeanFactor
phases.get(key).start();
}
}
this.running = true;
}
/**
@ -175,6 +161,28 @@ public class DefaultLifecycleProcessor implements LifecycleProcessor, BeanFactor @@ -175,6 +161,28 @@ public class DefaultLifecycleProcessor implements LifecycleProcessor, BeanFactor
}
}
private void stopBeans() {
Map<String, Lifecycle> lifecycleBeans = getLifecycleBeans();
Map<Integer, LifecycleGroup> phases = new HashMap<Integer, LifecycleGroup>();
for (Map.Entry<String, Lifecycle> entry : lifecycleBeans.entrySet()) {
Lifecycle lifecycle = entry.getValue();
int shutdownOrder = getPhase(lifecycle);
LifecycleGroup group = phases.get(shutdownOrder);
if (group == null) {
group = new LifecycleGroup(shutdownOrder, this.timeoutPerShutdownPhase, lifecycleBeans);
phases.put(shutdownOrder, group);
}
group.add(entry.getKey(), lifecycle);
}
if (phases.size() > 0) {
List<Integer> keys = new ArrayList<Integer>(phases.keySet());
Collections.sort(keys, Collections.reverseOrder());
for (Integer key : keys) {
phases.get(key).stop();
}
}
}
/**
* Stop the specified bean as part of the given set of Lifecycle beans,
* making sure that any beans that depends on it are stopped first.
@ -209,20 +217,22 @@ public class DefaultLifecycleProcessor implements LifecycleProcessor, BeanFactor @@ -209,20 +217,22 @@ public class DefaultLifecycleProcessor implements LifecycleProcessor, BeanFactor
}
private Map<String, Lifecycle> getLifecycleBeans() {
String[] beanNames = beanFactory.getSingletonNames();
String[] beanNames = this.beanFactory.getBeanNamesForType(Lifecycle.class, false, false);
Map<String, Lifecycle> beans = new LinkedHashMap<String, Lifecycle>();
for (String beanName : beanNames) {
Object bean = beanFactory.getSingleton(beanName);
if (bean instanceof Lifecycle && !this.equals(bean)) {
beans.put(beanName, (Lifecycle) bean);
if (this.beanFactory.containsSingleton(beanName) ||
SmartLifecycle.class.isAssignableFrom(this.beanFactory.getType(beanName))) {
Object bean = this.beanFactory.getBean(beanName);
if (!this.equals(bean)) {
beans.put(beanName, (Lifecycle) bean);
}
}
}
return beans;
}
private static int getPhase(Lifecycle bean) {
return (bean instanceof Phased) ?
((Phased) bean).getPhase() : 0;
return (bean instanceof Phased) ? ((Phased) bean).getPhase() : 0;
}

53
org.springframework.context/src/test/java/org/springframework/context/support/DefaultLifecycleProcessorTests.java

@ -16,14 +16,9 @@ @@ -16,14 +16,9 @@
package org.springframework.context.support;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import java.util.concurrent.CopyOnWriteArrayList;
import static org.junit.Assert.*;
import org.junit.Test;
import org.springframework.beans.DirectFieldAccessor;
@ -77,6 +72,19 @@ public class DefaultLifecycleProcessorTests { @@ -77,6 +72,19 @@ public class DefaultLifecycleProcessorTests {
assertEquals(1, startedBeans.size());
}
@Test
public void singleSmartLifecycleAutoStartupWithLazyInit() throws Exception {
StaticApplicationContext context = new StaticApplicationContext();
RootBeanDefinition bd = new RootBeanDefinition(DummySmartLifecycleBean.class);
bd.setLazyInit(true);
context.registerBeanDefinition("bean", bd);
context.refresh();
DummySmartLifecycleBean bean = context.getBean("bean", DummySmartLifecycleBean.class);
assertTrue(bean.isRunning());
context.stop();
assertFalse(bean.isRunning());
}
@Test
public void singleSmartLifecycleWithoutAutoStartup() throws Exception {
CopyOnWriteArrayList<Lifecycle> startedBeans = new CopyOnWriteArrayList<Lifecycle>();
@ -572,7 +580,7 @@ public class DefaultLifecycleProcessorTests { @@ -572,7 +580,7 @@ public class DefaultLifecycleProcessorTests {
return new TestSmartLifecycleBean(phase, 0, startedBeans, null);
}
static TestSmartLifecycleBean forShutdownTests(int phase, int shutdownDelay, CopyOnWriteArrayList<Lifecycle> stoppedBeans) {
static TestSmartLifecycleBean forShutdownTests(int phase, int shutdownDelay, CopyOnWriteArrayList<Lifecycle> stoppedBeans) {
return new TestSmartLifecycleBean(phase, shutdownDelay, null, stoppedBeans);
}
@ -615,4 +623,35 @@ public class DefaultLifecycleProcessorTests { @@ -615,4 +623,35 @@ public class DefaultLifecycleProcessorTests {
}
}
public static class DummySmartLifecycleBean implements SmartLifecycle {
public boolean running = false;
public boolean isAutoStartup() {
return true;
}
public void stop(Runnable callback) {
this.running = false;
callback.run();
}
public void start() {
this.running = true;
}
public void stop() {
this.running = false;
}
public boolean isRunning() {
return this.running;
}
public int getPhase() {
return 0;
}
}
}

Loading…
Cancel
Save