Make writing a habit together! This is the 14th day of my participation in the “Gold Digging Day New Plan ยท April More Text Challenge”. Click here for more details
Accumulate over a long period, constant dripping wears away a stone ๐
preface
You can use @postConstruct, InitializingBean, XML init, @Predestroy, DisposableBean, and XML Destroy to handle Bean initialization and destruction. These operations are part of the Bean life cycle.
What if I want to do some work on the lifecycle of the container itself, such as start and stop the container? Spring provides the following interfaces.
Lifecycle
Define a generic interface for start/stop lifecycle methods.
public interface Lifecycle {
/** ** is called when the container is started
void start(a);
/** ** is called when the container stops
void stop(a);
/** * Check if this component is running. * 1. The start method is executed only if the method returns false. * 2. The stop() method is executed only if the method returns true. * /
boolean isRunning(a);
}
Copy the code
Custom Lifecycle implementation classes
@Component
public class CustomizeLifecycle implements Lifecycle {
private volatile boolean running = false;
@Override
public void start(a) {
running = true;
System.out.println("start ==== ");
}
@Override
public void stop(a) {
running = false;
System.out.println("stop ==== ");
}
@Override
public boolean isRunning(a) {
returnrunning; }}Copy the code
test
@ComponentScan(basePackages = {"com.gongj.lifecycle"})
public class AppApplication {
public static void main(String[] args) {
AnnotationConfigApplicationContext context =
newAnnotationConfigApplicationContext(AppApplication.class); context.start(); context.stop(); }} result: isRunning ====>false
start ====>
isRunning ====> true
stop ====>
Copy the code
With Lifecycle, the start and stop methods of the container need to be called explicitly. It was cumbersome, so we used another approach: SmartLifecycle.
SmartLifecycle
public interface SmartLifecycle extends Lifecycle.Phased {
int DEFAULT_PHASE = Integer.MAX_VALUE;
default boolean isAutoStartup(a) {
return true;
}
default void stop(Runnable callback) {
stop();
callback.run();
}
@Override
default int getPhase(a) {
returnDEFAULT_PHASE; }}Copy the code
See the source code definition, SmartLifecycle in addition to Lifecycle, also inherited Phased. Several new methods have been added:
- IsAutoStartup: indicates whether to start automatically. Treu is automatically invoked
start()
; If false, the call needs to be displayedstart()
. - Stop: This method is called only when isRunning returns true.
- GetPhase: from
Phased
Interface, when there are multiple implementations in the containerSmartLifecycle
Class, this time can be based ongetPhase
Method returns a value to determinestart
Methods,stop
The order in which methods are executed.start
Methods are executed in the following ordergetPhase
Method returns values from small to large whilestop
The approach is the opposite. For example, when starting, 1 to 3 is executed first, -1 to 1 is executed first, and when exiting, 3 to 1 is exited first and 1 to -1 is exited first.
Custom SmartLifecycle implementation classes
@Component
public class CustomizeSmartLifecycle implements SmartLifecycle {
private volatile boolean running = false;
@Override
public void start(a) {
running = true;
System.out.println("CustomizeSmartLifecycle start()");
}
@Override
public void stop(a) {
System.out.println("CustomizeSmartLifecycle stop()");
running = false;
}
@Override
public boolean isRunning(a) {
System.out.println("CustomizeSmartLifecycle isRunning() ==== " + running);
return running;
}
@Override
public boolean isAutoStartup(a) {
return true;
}
@Override
public void stop(Runnable callback) {
System.out.println("CustomizeSmartLifecycle stop(Runnable callback)");
callback.run();
}
@Override
public int getPhase(a) {
return 1; }}Copy the code
Start the code and the console prints the following:
public static void main(String[] args) {
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext(AppApplication.class);
//context.start();
//context.stop();} the result:CustomizeSmartLifecycle isRunning(a) = = = =false
CustomizeSmartLifecycle start(a)
Copy the code
The stop method was not executed because of a container problem. If the Spring Boot project is automatically executed, you can try it out.
In this case, you still need to show that the stop method is called.
public static void main(String[] args) {
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext(AppApplication.class);
//context.start();context.stop(); } the result:CustomizeSmartLifecycle isRunning(a) = = = =false
CustomizeSmartLifecycle start(a)
CustomizeSmartLifecycle isRunning(a) = = = =true
CustomizeSmartLifecycle stop(Runnable callback)
Copy the code
A stop(Runnable callback) was executed, but the stop method was not. This is because the current class is a subclass of SmartLifecycle and if you want to execute the stop method, you need to display the call at stop(Runnable callback).
@Override
public void stop(Runnable callback) {
System.out.println("CustomizeSmartLifecycle stop(Runnable callback)"); stop(); callback.run(); } or@Override
public void stop(Runnable callback) {
SmartLifecycle.super.stop(callback);
}
Copy the code
The startup result is as follows:
CustomizeSmartLifecycle isRunning(a) = = = =false
CustomizeSmartLifecycle start(a)
CustomizeSmartLifecycle isRunning(a) = = = =true
CustomizeSmartLifecycle stop(Runnable callback)
CustomizeSmartLifecycle stop(a)
Copy the code
Note: Don’t forget to call callback.run() in the stop(Runnable callback) method. Otherwise DefaultLifecycleProcessor will complete, don’t think this SmartLifecycle stop program will wait for a certain time, will automatically end after 30 seconds by default.
@Override
public void stop(Runnable callback) {
System.out.println("CustomizeSmartLifecycle stop(Runnable callback)");
//stop();
//callback.run();
}
Copy the code
Multiple implementation classes
@Component
public class CustomizeSmartLifecycle2 implements SmartLifecycle {
private volatile boolean running = false;
@Override
public void start(a) {
running = true;
System.out.println("CustomizeSmartLifecycle2 start() =====>");
}
@Override
public void stop(a) {
System.out.println("CustomizeSmartLifecycle1 stop() =====>");
running = false;
}
@Override
public boolean isRunning(a) {
System.out.println("CustomizeSmartLifecycle2 isRunning() =====> " + running);
return running;
}
@Override
public boolean isAutoStartup(a) {
return true;
}
@Override
public void stop(Runnable callback) {
System.out.println("CustomizeSmartLifecycle2 stop(Runnable callback) =====>");
callback.run();
}
@Override
public int getPhase(a) {
return -1; }}Copy the code
The getPhase method returns -1. The CustomizeSmartLifecycle2 start method is executed first, and the CustomizeSmartLifecycle2 Stop method is executed last. Let’s take a look at the results!
public static void main(String[] args) {
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext(AppApplication.class);
//context.start();
context.stop();
}
Copy the code
Execution Result:
CustomizeSmartLifecycle2 isRunning(a) = = = = = >false
CustomizeSmartLifecycle2 start(a) = = = = = >CustomizeSmartLifecycle isRunning(a) = = = =false
CustomizeSmartLifecycle start(a)
CustomizeSmartLifecycle isRunning(a) = = = =true
CustomizeSmartLifecycle stop(Runnable callback)
CustomizeSmartLifecycle2 isRunning(a) = = = = = >true
CustomizeSmartLifecycle2 stop(Runnable callback) = = = = = >Copy the code
Be consistent with the conclusion.
Source code analysis
That’s the end of basic use! Let’s combine source code analysis!
We go to getLifecycleProcessor().onRefresh() of the finishRefresh method; Methods.
protected void finishRefresh(a) {
// Clear context-level resource caches (such as ASM metadata from scanning).
clearResourceCaches();
// Initialize lifecycle processor for this context.
// Initialize the lifecycle handler for this context
initLifecycleProcessor();
// Propagate refresh to lifecycle processor first.
/ / the default calling DefaultLifecycleProcessor onRefresh method
Lifecycle beans are not available by default and will need to be defined by Lifecycle beans
getLifecycleProcessor().onRefresh();
// Publish the final event.
ContextRefreshedEvent is an event
publishEvent(new ContextRefreshedEvent(this));
// Participate in LiveBeansView MBean, if active.
LiveBeansView.registerApplicationContext(this);
}
Copy the code
GetLifecycleProcessor () method to obtain LifecycleProcessor objects in the container, the default is DefaultLifecycleProcessor.
LifecycleProcessor
public interface LifecycleProcessor extends Lifecycle {
/** * Notification of context refresh, e.g., for auto-starting components
void onRefresh(a);
/** * Notification of context close phase, e.g., for auto-stopping components. * /
void onClose(a);
}
Copy the code
Two methods defined in the interface. OnRefresh, onClose.
onRefresh
Let’s first look at the internal logic of the onRefresh method and see how it automatically calls the Start method.
@Override
public void onRefresh(a) {
startBeans(true);
this.running = true;
}
private void startBeans(boolean autoStartupOnly) {
// Get all Lifecycle beans
Map<String, Lifecycle> lifecycleBeans = getLifecycleBeans();
// Lifecycle beans are grouped by phases, which are obtained by implementing the Phased interface
Map<Integer, LifecycleGroup> phases = new HashMap<>();
// Walk through all Lifecycle beans grouped by stage value
lifecycleBeans.forEach((beanName, bean) -> {
// When autoStartupOnly=false, Lifecycle will be triggered by calling start();
// When autoStartupOnly=true, calling the container's refresh method to start will only trigger the SmartLifecycle that isAutoStartup returns true
if(! autoStartupOnly || (beaninstanceof SmartLifecycle && ((SmartLifecycle) bean).isAutoStartup())) {
int phase = getPhase(bean);
LifecycleGroup group = phases.get(phase);
if (group == null) {
group = new LifecycleGroup(phase, this.timeoutPerShutdownPhase, lifecycleBeans, autoStartupOnly); phases.put(phase, group); } group.add(beanName, bean); }});if(! phases.isEmpty()) { List<Integer> keys =new ArrayList<>(phases.keySet());
Collections.sort(keys);
for (Integer key : keys) {
Lifecycle is called with the start methodphases.get(key).start(); }}}Copy the code
Notice here that autoStartupOnly has the value true.
- AutoStartupOnly =true, calling the container’s
refresh
Method, called by the containeronRefresh
Method starts automatically and only firesisAutoStartup
Method returnstrue
็SmartLifecycle
. whileisAutoStartup
Attributes. Jay’s analyzing themSmartLifecycle
I already talked about that. - AutoStartupOnly =false to display the call
start()
Method that fires allLifecycle
@Override
public void start(a) {
getLifecycleProcessor().start();
publishEvent(new ContextStartedEvent(this));
}
/ / to false
@Override
public void start(a) {
startBeans(false);
this.running = true;
}
Copy the code
onClose
OnClose I didn’t find the call point, but the stopBeans() method will be called inside onClose. We examine the stopBeans method directly.
The stopBeans method has roughly the same logic as startBeans and then calls the Stop method.
@Override
public void onClose(a) {
stopBeans();
this.running = false;
}
private void stopBeans(a) {
// Get all Lifecycle beans
Map<String, Lifecycle> lifecycleBeans = getLifecycleBeans();
Map<Integer, LifecycleGroup> phases = new HashMap<>();
lifecycleBeans.forEach((beanName, bean) -> {
// Get the value of phase
int shutdownPhase = getPhase(bean);
LifecycleGroup group = phases.get(shutdownPhase);
// Group by stage value
if (group == null) {
/ / this. TimeoutPerShutdownPhase is wait for time
group = new LifecycleGroup(shutdownPhase, this.timeoutPerShutdownPhase, lifecycleBeans, false);
phases.put(shutdownPhase, group);
}
group.add(beanName, bean);
});
if(! phases.isEmpty()) { List<Integer> keys =new ArrayList<>(phases.keySet());
keys.sort(Collections.reverseOrder());
for (Integer key : keys) {
// Call stopphases.get(key).stop(); }}}Copy the code
stop
Create a CountDownLatch object. If count is not zero, the thread waits, by default, for 30 seconds.
public void stop(a) {
if(this.members.isEmpty()) {
return;
}
if(logger.isDebugEnabled()) {
logger.debug("Stopping beans in phase " + this.phase);
}
this.members.sort(Collections.reverseOrder());
// Create the CountDownLatch object with count as smartMemberCount
// smartMemberCount is the number of SmartLifecycle beans
CountDownLatch latch = new CountDownLatch(this.smartMemberCount);
Set < String > countDownBeanNames = Collections.synchronizedSet(new LinkedHashSet < > ());
Set < String > lifecycleBeanNames = new HashSet < > (this.lifecycleBeans.keySet());
for(LifecycleGroupMember member: this.members) {
if(lifecycleBeanNames.contains(member.name)) {
// Call dostop
doStop(this.lifecycleBeans, member.name, latch, countDownBeanNames);
} else if(member.bean instanceof SmartLifecycle) {
// Already removed: must have been a dependent bean from another phase
// Subtract count by 1latch.countDown(); }}try {
// The thread calling the await() method will be suspended and wait a certain amount of time before continuing if count has not been 0
latch.await(this.timeout, TimeUnit.MILLISECONDS);
if(latch.getCount() > 0 && !countDownBeanNames.isEmpty() && logger.isInfoEnabled()) {
logger.info("Failed to shut down " + countDownBeanNames.size() + " bean" + (countDownBeanNames.size() > 1 ? "s" : "") + " with phase value " + this.phase + " within timeout of " + this.timeout + ":"+ countDownBeanNames); }}catch(InterruptedException ex) { Thread.currentThread().interrupt(); }}}Copy the code
doStop
The logic of the doStop method is simple. Distinguish between SmartLifecycle and stop(Runnable callback) and stop().
private void doStop(Map<String, ? extends Lifecycle> lifecycleBeans, final String beanName,
final CountDownLatch latch, final Set<String> countDownBeanNames) {
Lifecycle bean = lifecycleBeans.remove(beanName);
if(bean ! =null) {
String[] dependentBeans = getBeanFactory().getDependentBeans(beanName);
for (String dependentBean : dependentBeans) {
doStop(lifecycleBeans, dependentBean, latch, countDownBeanNames);
}
try {
// Determine the value of isRunning
if (bean.isRunning()) {
// If it is SmartLifecycle, the stop(Runnable callback) method is executed
if (bean instanceof SmartLifecycle) {
if (logger.isTraceEnabled()) {
logger.trace("Asking bean '" + beanName + "' of type [" +
bean.getClass().getName() + "] to stop");
}
countDownBeanNames.add(beanName);
// This is why callback.run() is called
// A lambad expression is passed in, so callback.run() is required to execute the lambad body
// The lambad body latches. CountDown ()
((SmartLifecycle) bean).stop(() -> {
// Subtract count by 1
latch.countDown();
countDownBeanNames.remove(beanName);
if (logger.isDebugEnabled()) {
logger.debug("Bean '" + beanName + "' completed its stop procedure"); }}); }else {
if (logger.isTraceEnabled()) {
logger.trace("Stopping bean '" + beanName + "' of type [" +
bean.getClass().getName() + "]");
}
// Otherwise, execute the stop() method
bean.stop();
if (logger.isDebugEnabled()) {
logger.debug("Successfully stopped bean '" + beanName + "'"); }}}else if (bean instanceof SmartLifecycle) {
// Don't wait for beans that aren't running...latch.countDown(); }}catch (Throwable ex) {
if (logger.isWarnEnabled()) {
logger.warn("Failed to stop bean '" + beanName + "'", ex); }}}}Copy the code
That concludes the use of Lifecycle and the source code analysis!
- If you have any questions or errors in this article, please feel free to comment. If you find this article helpful, please like it and follow it.