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 invokedstart(); If false, the call needs to be displayedstart().
  • Stop: This method is called only when isRunning returns true.
  • GetPhase: fromPhasedInterface, when there are multiple implementations in the containerSmartLifecycleClass, this time can be based ongetPhaseMethod returns a value to determinestartMethods,stopThe order in which methods are executed.startMethods are executed in the following ordergetPhaseMethod returns values from small to large whilestopThe 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’srefreshMethod, called by the containeronRefreshMethod starts automatically and only firesisAutoStartupMethod returnstrueSmartLifecycle. whileisAutoStartupAttributes. Jay’s analyzing themSmartLifecycleI already talked about that.
  • AutoStartupOnly =false to display the callstart()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.