diff --git a/org.springframework.context/src/main/java/org/springframework/context/Lifecycle.java b/org.springframework.context/src/main/java/org/springframework/context/Lifecycle.java index 112aebcea5f..1d489a80b9a 100644 --- a/org.springframework.context/src/main/java/org/springframework/context/Lifecycle.java +++ b/org.springframework.context/src/main/java/org/springframework/context/Lifecycle.java @@ -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; * restricting the visibility of activity-controlled components to the Lifecycle * interface. * + *

Note that the Lifecycle interface is only supported on top-level singleton beans. + * 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 diff --git a/org.springframework.context/src/main/java/org/springframework/context/SmartLifecycle.java b/org.springframework.context/src/main/java/org/springframework/context/SmartLifecycle.java index bbee8cdbce8..8ee94816b7c 100644 --- a/org.springframework.context/src/main/java/org/springframework/context/SmartLifecycle.java +++ b/org.springframework.context/src/main/java/org/springframework/context/SmartLifecycle.java @@ -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 must invoke the * callback's run() method upon shutdown completion to avoid unnecessary delays - * in the overall ApplicationContext shutdown. - *

- * This interface extends {@link Phased}, and the {@link #getPhase()} method's + * in the overall ApplicationContext shutdown. + * + *

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 lowest * phase value and ends with the highest 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. - *

- * Example: if component B depends on component A having already started, then + * + *

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. - *

- * Any explicit "depends-on" relationship will take precedence over + * + *

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. - *

- * Any Lifecycle components within the context that do not also implement + * + *

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. - * + * + *

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 { /** * Indicates that a Lifecycle component must stop if it is currently running. - * The provided callback is used by the LifecycleProcessor to support an + *

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 must be executed after * the SmartLifecycle component does indeed stop. diff --git a/org.springframework.context/src/main/java/org/springframework/context/support/DefaultLifecycleProcessor.java b/org.springframework.context/src/main/java/org/springframework/context/support/DefaultLifecycleProcessor.java index 5f5b670f196..e45356103ad 100644 --- a/org.springframework.context/src/main/java/org/springframework/context/support/DefaultLifecycleProcessor.java +++ b/org.springframework.context/src/main/java/org/springframework/context/support/DefaultLifecycleProcessor.java @@ -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 } 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 * 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 * the dependency bean regardless of the declared phase. */ public void stop() { - Map lifecycleBeans = getLifecycleBeans(); - Map phases = new HashMap(); - for (Map.Entry 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 keys = new ArrayList(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 lifecycleBeans = getLifecycleBeans(); Map phases = new HashMap(); @@ -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 } } + private void stopBeans() { + Map lifecycleBeans = getLifecycleBeans(); + Map phases = new HashMap(); + for (Map.Entry 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 keys = new ArrayList(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 } private Map getLifecycleBeans() { - String[] beanNames = beanFactory.getSingletonNames(); + String[] beanNames = this.beanFactory.getBeanNamesForType(Lifecycle.class, false, false); Map beans = new LinkedHashMap(); 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; } diff --git a/org.springframework.context/src/test/java/org/springframework/context/support/DefaultLifecycleProcessorTests.java b/org.springframework.context/src/test/java/org/springframework/context/support/DefaultLifecycleProcessorTests.java index ea3fc2b3618..955ad0f8f7f 100644 --- a/org.springframework.context/src/test/java/org/springframework/context/support/DefaultLifecycleProcessorTests.java +++ b/org.springframework.context/src/test/java/org/springframework/context/support/DefaultLifecycleProcessorTests.java @@ -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 { 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 startedBeans = new CopyOnWriteArrayList(); @@ -572,7 +580,7 @@ public class DefaultLifecycleProcessorTests { return new TestSmartLifecycleBean(phase, 0, startedBeans, null); } - static TestSmartLifecycleBean forShutdownTests(int phase, int shutdownDelay, CopyOnWriteArrayList stoppedBeans) { + static TestSmartLifecycleBean forShutdownTests(int phase, int shutdownDelay, CopyOnWriteArrayList stoppedBeans) { return new TestSmartLifecycleBean(phase, shutdownDelay, null, stoppedBeans); } @@ -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; + } + } + }