X Tutup
Skip to content

Commit 021663b

Browse files
committed
SPR-5507 The 'shutdownOrder' property of SmartLifecycle has been renamed 'phase'. The order no longer applies to shutdown only; now startup order is determined by the phase value as well. Components start in ascending order and stop in descending order.
1 parent 1f5568f commit 021663b

File tree

7 files changed

+539
-174
lines changed

7 files changed

+539
-174
lines changed

org.springframework.context.support/src/main/java/org/springframework/scheduling/quartz/SchedulerFactoryBean.java

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ public static DataSource getConfigTimeNonTransactionalDataSource() {
190190

191191
private int startupDelay = 0;
192192

193-
private int shutdownOrder = Integer.MAX_VALUE;
193+
private int phase = Integer.MAX_VALUE;
194194

195195
private boolean exposeSchedulerInRepository = false;
196196

@@ -376,18 +376,21 @@ public boolean isAutoStartup() {
376376
}
377377

378378
/**
379-
* Specify the order in which this scheduler should be stopped.
380-
* By default it will be stopped in the last group.
379+
* Specify the phase in which this scheduler should be started and
380+
* stopped. The startup order proceeds from lowest to highest, and
381+
* the shutdown order is the reverse of that. By default this value
382+
* is Integer.MAX_VALUE meaning that this scheduler starts as late
383+
* as possible and stops as soon as possible.
381384
*/
382-
public void setShutdownOrder(int shutdownOrder) {
383-
this.shutdownOrder = shutdownOrder;
385+
public void setPhase(int phase) {
386+
this.phase = phase;
384387
}
385388

386389
/**
387-
* Return the order in which this scheduler will be stopped.
390+
* Return the phase in which this scheduler will be started and stopped.
388391
*/
389-
public int getShutdownOrder() {
390-
return this.shutdownOrder;
392+
public int getPhase() {
393+
return this.phase;
391394
}
392395

393396
/**

org.springframework.context.support/src/test/resources/org/springframework/scheduling/quartz/quartzSchedulerLifecycleTests.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
<bean id="lazyInitSchedulerWithDefaultShutdownOrder" class="org.springframework.scheduling.quartz.SchedulerFactoryBean" lazy-init="true"/>
88

99
<bean id="lazyInitSchedulerWithCustomShutdownOrder" class="org.springframework.scheduling.quartz.SchedulerFactoryBean" lazy-init="true">
10-
<property name="shutdownOrder" value="99"/>
10+
<property name="phase" value="99"/>
1111
</bean>
1212

1313
</beans>

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

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -33,14 +33,28 @@ public interface SmartLifecycle extends Lifecycle {
3333
boolean isAutoStartup();
3434

3535
/**
36-
* Return the order in which this Lifecycle component should be stopped.
37-
* The shutdown process begins with the component(s) having the <i>lowest</i>
38-
* value and ends with the highest value (Integer.MIN_VALUE is the lowest
39-
* possible, and Integer.MAX_VALUE is the highest possible). Any Lifecycle
40-
* components within the context that do not also implement SmartLifecycle
41-
* will be treated as if they have a value of Integer.MAX_VALUE.
36+
* Return the phase within which this Lifecycle component should be started
37+
* and stopped. The startup process begins with the <i>lowest</i> phase
38+
* value and ends with the <i>highest</i> phase value (Integer.MIN_VALUE is
39+
* the lowest possible, and Integer.MAX_VALUE is the highest possible). The
40+
* shutdown process will apply the reverse order. Any components with the
41+
* same value will be arbitrarily ordered within the same phase.
42+
* <p>
43+
* Example: if component B depends on component A having already started, then
44+
* component A should have a lower phase value than component B. During the
45+
* shutdown process, component B would be stopped before component A.
46+
* <p>
47+
* Any Lifecycle components within the context that do not also implement
48+
* SmartLifecycle will be treated as if they have a phase value of 0. That
49+
* way a SmartLifecycle implementation may start before those Lifecycle
50+
* components if it has a negative phase value, or it may start after
51+
* those components if it has a positive phase value.
52+
* <p>
53+
* Any explicit "depends-on" relationship will take precedence over
54+
* the phase order such that the dependent bean always starts after its
55+
* dependency and always stops before its dependency.
4256
*/
43-
int getShutdownOrder();
57+
int getPhase();
4458

4559
/**
4660
* Indicates that a Lifecycle component must stop if it is currently running.

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

Lines changed: 95 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
import java.util.Collections;
2121
import java.util.HashMap;
2222
import java.util.LinkedHashMap;
23-
import java.util.LinkedHashSet;
2423
import java.util.List;
2524
import java.util.Map;
2625
import java.util.concurrent.CountDownLatch;
@@ -47,20 +46,20 @@ public class DefaultLifecycleProcessor implements LifecycleProcessor, BeanFactor
4746

4847
private final Log logger = LogFactory.getLog(this.getClass());
4948

50-
private volatile long shutdownGroupTimeout = 30000;
49+
private volatile long timeoutPerShutdownPhase = 30000;
5150

5251
private volatile boolean running;
5352

5453
private volatile ConfigurableListableBeanFactory beanFactory;
5554

5655

5756
/**
58-
* Specify the maximum time allotted for the shutdown of any group of
59-
* SmartLifecycle beans (those with the same 'order' value). The default
60-
* value is 30 seconds.
57+
* Specify the maximum time allotted for the shutdown of any phase
58+
* (group of SmartLifecycle beans with the same 'phase' value).
59+
* The default value is 30 seconds.
6160
*/
62-
public void setShutdownGroupTimeout(long shutdownGroupTimeout) {
63-
this.shutdownGroupTimeout = shutdownGroupTimeout;
61+
public void setTimeoutPerShutdownPhase(long timeoutPerShutdownPhase) {
62+
this.timeoutPerShutdownPhase = timeoutPerShutdownPhase;
6463
}
6564

6665
public void setBeanFactory(BeanFactory beanFactory) {
@@ -77,58 +76,84 @@ public boolean isRunning() {
7776
return this.running;
7877
}
7978

79+
/**
80+
* Start all registered beans that implement Lifecycle and are
81+
* <i>not</i> already running. Any bean that implements SmartLifecycle
82+
* will be started within its 'phase', and all phases will be ordered
83+
* from lowest to highest value. All beans that do not implement
84+
* SmartLifecycle will be started in the default phase 0. A bean
85+
* declared as a dependency of another bean will be started before
86+
* the dependent bean regardless of the declared phase.
87+
*/
8088
public void start() {
81-
Map<String, Lifecycle> lifecycleBeans = getLifecycleBeans();
82-
for (String beanName : new LinkedHashSet<String>(lifecycleBeans.keySet())) {
83-
doStart(lifecycleBeans, beanName);
84-
}
85-
this.running = true;
89+
this.startBeans(false);
8690
}
8791

92+
/**
93+
* Stop all registered beans that implement Lifecycle and <i>are</i>
94+
* currently running. Any bean that implements SmartLifecycle
95+
* will be stopped within its 'phase', and all phases will be ordered
96+
* from highest to lowest value. All beans that do not implement
97+
* SmartLifecycle will be stopped in the default phase 0. A bean
98+
* declared as dependent on another bean will be stopped before
99+
* the dependency bean regardless of the declared phase.
100+
*/
88101
public void stop() {
89102
Map<String, Lifecycle> lifecycleBeans = getLifecycleBeans();
90-
Map<Integer, ShutdownGroup> shutdownGroups = new HashMap<Integer, ShutdownGroup>();
103+
Map<Integer, LifecycleGroup> phases = new HashMap<Integer, LifecycleGroup>();
91104
for (Map.Entry<String, Lifecycle> entry : lifecycleBeans.entrySet()) {
92105
Lifecycle lifecycle = entry.getValue();
93-
int shutdownOrder = getShutdownOrder(lifecycle);
94-
ShutdownGroup group = shutdownGroups.get(shutdownOrder);
106+
int shutdownOrder = getPhase(lifecycle);
107+
LifecycleGroup group = phases.get(shutdownOrder);
95108
if (group == null) {
96-
group = new ShutdownGroup(shutdownOrder, this.shutdownGroupTimeout, lifecycleBeans);
97-
shutdownGroups.put(shutdownOrder, group);
109+
group = new LifecycleGroup(shutdownOrder, this.timeoutPerShutdownPhase, lifecycleBeans);
110+
phases.put(shutdownOrder, group);
98111
}
99112
group.add(entry.getKey(), lifecycle);
100113
}
101-
if (shutdownGroups.size() > 0) {
102-
List<Integer> keys = new ArrayList<Integer>(shutdownGroups.keySet());
103-
Collections.sort(keys);
114+
if (phases.size() > 0) {
115+
List<Integer> keys = new ArrayList<Integer>(phases.keySet());
116+
Collections.sort(keys, Collections.reverseOrder());
104117
for (Integer key : keys) {
105-
shutdownGroups.get(key).shutdown();
118+
phases.get(key).stop();
106119
}
107120
}
108121
this.running = false;
109122
}
110123

111124
public void onRefresh() {
112-
Map<String, SmartLifecycle> lifecycleBeans = getSmartLifecycleBeans();
113-
for (String beanName : new LinkedHashSet<String>(lifecycleBeans.keySet())) {
114-
SmartLifecycle bean = lifecycleBeans.get(beanName);
115-
if (bean != null && bean.isAutoStartup()) {
116-
String[] dependenciesForBean = this.beanFactory.getDependenciesForBean(beanName);
117-
for (String dependency : dependenciesForBean) {
118-
doStart(lifecycleBeans, dependency);
119-
}
120-
if (!bean.isRunning()) {
121-
bean.start();
122-
}
123-
lifecycleBeans.remove(beanName);
124-
}
125-
}
125+
this.startBeans(true);
126126
}
127127

128128
public void onClose() {
129129
stop();
130130
}
131131

132+
private void startBeans(boolean autoStartupOnly) {
133+
Map<String, Lifecycle> lifecycleBeans = getLifecycleBeans();
134+
Map<Integer, LifecycleGroup> phases = new HashMap<Integer, LifecycleGroup>();
135+
for (Map.Entry<String, ? extends Lifecycle> entry : lifecycleBeans.entrySet()) {
136+
Lifecycle lifecycle = entry.getValue();
137+
if (!autoStartupOnly || (lifecycle instanceof SmartLifecycle && ((SmartLifecycle) lifecycle).isAutoStartup())) {
138+
int phase = getPhase(lifecycle);
139+
LifecycleGroup group = phases.get(phase);
140+
if (group == null) {
141+
group = new LifecycleGroup(phase, this.timeoutPerShutdownPhase, lifecycleBeans);
142+
phases.put(phase, group);
143+
}
144+
group.add(entry.getKey(), lifecycle);
145+
}
146+
}
147+
if (phases.size() > 0) {
148+
List<Integer> keys = new ArrayList<Integer>(phases.keySet());
149+
Collections.sort(keys);
150+
for (Integer key : keys) {
151+
phases.get(key).start();
152+
}
153+
}
154+
this.running = true;
155+
}
156+
132157
/**
133158
* Start the specified bean as part of the given set of Lifecycle beans,
134159
* making sure that any beans that it depends on are started first.
@@ -155,7 +180,7 @@ private void doStart(Map<String, ? extends Lifecycle> lifecycleBeans, String bea
155180
* @param lifecycleBeans Map with bean name as key and Lifecycle instance as value
156181
* @param beanName the name of the bean to stop
157182
*/
158-
private void doStop(Map<String, Lifecycle> lifecycleBeans, String beanName, final CountDownLatch latch) {
183+
private void doStop(Map<String, ? extends Lifecycle> lifecycleBeans, String beanName, final CountDownLatch latch) {
159184
Lifecycle bean = lifecycleBeans.get(beanName);
160185
if (bean != null) {
161186
String[] dependentBeans = this.beanFactory.getDependentBeans(beanName);
@@ -194,44 +219,31 @@ private Map<String, Lifecycle> getLifecycleBeans() {
194219
return beans;
195220
}
196221

197-
private Map<String, SmartLifecycle> getSmartLifecycleBeans() {
198-
String[] beanNames = beanFactory.getSingletonNames();
199-
Map<String, SmartLifecycle> beans = new LinkedHashMap<String, SmartLifecycle>();
200-
for (String beanName : beanNames) {
201-
Object bean = beanFactory.getSingleton(beanName);
202-
if (bean instanceof SmartLifecycle) {
203-
beans.put(beanName, (SmartLifecycle) bean);
204-
}
205-
}
206-
return beans;
207-
}
208-
209-
210-
private static int getShutdownOrder(Lifecycle bean) {
222+
private static int getPhase(Lifecycle bean) {
211223
return (bean instanceof SmartLifecycle) ?
212-
((SmartLifecycle) bean).getShutdownOrder() : Integer.MAX_VALUE;
224+
((SmartLifecycle) bean).getPhase() : 0;
213225
}
214226

215227

216228
/**
217-
* Helper class for maintaining a group of Lifecycle beans that should be shutdown
218-
* together based on their 'shutdownOrder' value (or the default MAX_INTEGER value).
229+
* Helper class for maintaining a group of Lifecycle beans that should be started
230+
* and stopped together based on their 'phase' value (or the default value of 0).
219231
*/
220-
private class ShutdownGroup {
232+
private class LifecycleGroup {
221233

222-
private final List<ShutdownGroupMember> members = new ArrayList<ShutdownGroupMember>();
234+
private final List<LifecycleGroupMember> members = new ArrayList<LifecycleGroupMember>();
223235

224-
private Map<String, Lifecycle> lifecycleBeans = getLifecycleBeans();
236+
private Map<String, ? extends Lifecycle> lifecycleBeans = getLifecycleBeans();
225237

226238
private volatile int smartMemberCount;
227239

228-
private final int order;
240+
private final int phase;
229241

230242
private final long timeout;
231243

232244

233-
ShutdownGroup(int order, long timeout, Map<String, Lifecycle> lifecycleBeans) {
234-
this.order = order;
245+
LifecycleGroup(int phase, long timeout, Map<String, ? extends Lifecycle> lifecycleBeans) {
246+
this.phase = phase;
235247
this.timeout = timeout;
236248
this.lifecycleBeans = lifecycleBeans;
237249
}
@@ -240,16 +252,28 @@ void add(String name, Lifecycle bean) {
240252
if (bean instanceof SmartLifecycle) {
241253
this.smartMemberCount++;
242254
}
243-
this.members.add(new ShutdownGroupMember(name, bean));
255+
this.members.add(new LifecycleGroupMember(name, bean));
244256
}
245257

246-
void shutdown() {
258+
void start() {
247259
if (members.size() == 0) {
248260
return;
249261
}
250262
Collections.sort(members);
263+
for (LifecycleGroupMember member : members) {
264+
if (lifecycleBeans.containsKey(member.name)) {
265+
doStart(lifecycleBeans, member.name);
266+
}
267+
}
268+
}
269+
270+
void stop() {
271+
if (members.size() == 0) {
272+
return;
273+
}
274+
Collections.sort(members, Collections.reverseOrder());
251275
final CountDownLatch latch = new CountDownLatch(this.smartMemberCount);
252-
for (ShutdownGroupMember member : members) {
276+
for (LifecycleGroupMember member : members) {
253277
if (lifecycleBeans.containsKey(member.name)) {
254278
doStop(lifecycleBeans, member.name, latch);
255279
}
@@ -262,8 +286,8 @@ else if (member.bean instanceof SmartLifecycle) {
262286
latch.await(this.timeout, TimeUnit.MILLISECONDS);
263287
if (latch.getCount() != 0) {
264288
if (logger.isWarnEnabled()) {
265-
logger.warn("failed to shutdown beans with order " +
266-
this.order + " within timeout of " + this.timeout);
289+
logger.warn("failed to shutdown beans with phase value " +
290+
this.phase + " within timeout of " + this.timeout);
267291
}
268292
}
269293
}
@@ -274,20 +298,20 @@ else if (member.bean instanceof SmartLifecycle) {
274298
}
275299

276300

277-
private static class ShutdownGroupMember implements Comparable<ShutdownGroupMember> {
301+
private static class LifecycleGroupMember implements Comparable<LifecycleGroupMember> {
278302

279-
private String name;
303+
private final String name;
280304

281-
private Lifecycle bean;
305+
private final Lifecycle bean;
282306

283-
ShutdownGroupMember(String name, Lifecycle bean) {
307+
LifecycleGroupMember(String name, Lifecycle bean) {
284308
this.name = name;
285309
this.bean = bean;
286310
}
287311

288-
public int compareTo(ShutdownGroupMember other) {
289-
int thisOrder = getShutdownOrder(this.bean);
290-
int otherOrder = getShutdownOrder(other.bean);
312+
public int compareTo(LifecycleGroupMember other) {
313+
int thisOrder = getPhase(this.bean);
314+
int otherOrder = getPhase(other.bean);
291315
return (thisOrder == otherOrder) ? 0 : (thisOrder < otherOrder) ? -1 : 1;
292316
}
293317
}

0 commit comments

Comments
 (0)
X Tutup