Spring official document highlights series 3: lifeCycle and SmartLifeCycle

LifeCycle

LifeCycle defines three methods. Any Bean implements the LifeCycle method. When ApplicationContext receives signals such as start, stop or restart, it will call the corresponding method. So in the application program, the callback of the container life cycle can be obtained by implementing the life cycle interface to realize the business extension. LifeCycle is defined as follows:

public interface Lifecycle {

    void start();

    void stop();

    boolean isRunning();
}

LifecycleProcessor

Spring container implements this function through LifeCycle processor agent. LifeCycle processor also implements the LifeCycle interface and provides two methods:

public interface LifecycleProcessor extends Lifecycle {

    void onRefresh();

    void onClose();
}

Two key descriptions in Spring official documents:

Note that the regular org.springframework.context.Lifecycle interface is a plain contract for explicit start and stop notifications and does not imply auto-startup at context refresh time. For fine-grained control over auto-startup of a specific bean (including startup phases), consider implementing org.springframework.context.SmartLifecycle instead.

Also, please note that stop notifications are not guaranteed to come before destruction. On regular shutdown, all Lifecycle beans first receive a stop notification before the general destruction callbacks are being propagated. However, on hot refresh during a context's lifetime or on aborted refresh attempts, only destroy methods are called.

It is concluded that life cycle only perceives the container start and start life cycle, and will not be called during the container refresh period of automatic startUp (onRefresh mentioned in the following chapters). At the same time, the stop notification is not guaranteed to trigger. The specific reason is described above.

SmartLifeCycle

Smartlife cycle adds refresh awareness to the container context on the basis of life cycle. At the same time, it inherits the Phase interface and provides the Bean with some intervention ability to create and destroy. Smartlife cycle is defined as follows:

public interface SmartLifecycle extends Lifecycle, Phased {

    boolean isAutoStartup();

    void stop(Runnable callback);
}

The order in which beans are created and destroyed

As mentioned earlier, the dependency order between two beans can be determined through dependencies on, so as to control the creation and destruction order of dependency on connectivity beans. However, most of the time, you may only think that a write bean needs to be created first, and some beans need to be destroyed finally. At this time, you can control the bean creation and destruction order through the implementation of smartlife cycle and the getPhases method.

For beans that do not implement the smartlife cycle interface, it can be seen that the default Phase is 0. That is, if the return value of the getPhase method is less than 0, it will be created first. As with dependencies on, beans that are created first are destroyed later.

It should be noted that the dependencies on priority can be considered the highest. When you check the source implementation, you will find that when the starter stops, the dependencies on relationship will be checked first.

About Stop and Timeout

Smartlife cycle's stop method passes in a run nable method, which must be called by the implementer. In fact, you can see the default implementation of lifecycle processor defaultlifecycle processor.

In default lifecycle processor, groups are sorted by different phase s, and a countdownlatch is created for each group. The countdown latch is counted in the incoming callback.

This is because this operation may be executed by multiple threads. It needs to control the concurrency through countdownLatch. At the same time, the timeout parameter is provided to control the startup and destruction timeout of each group.

Currently, thread pools are not used in the default implementation, which may be for future expansion?

onRefresh and isAutoStartup

The isAutoStartup method of smartlife cycle is used to indicate whether the bean wants to accept the onRefresh callback. If true is returned, the callback method will be triggered when the container refreshes. As mentioned in the Spring document, not every context must implement the start method to call. When getting the refresh callback, you can sense ahead of time without waiting for the start event to trigger.

When the context is refreshed (after all objects have been instantiated and initialized), that callback is invoked. At that point, the default lifecycle processor checks the boolean value returned by each SmartLifecycle object's isAutoStartup() method. If true, that object is started at that point rather than waiting for an explicit invocation of the context's or its own start() method (unlike the context refresh, the context start does not happen automatically for a standard context implementation). The phase value and any "depends-on" relationships determine the startup order as described earlier.

Let's analyze this process through the source code. The initialization of the container is called by the onRefresh method in the ApplicationContext constructor of Spring.

@Override
public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
        // Prepare this context for refreshing.
        prepareRefresh();

        // Tell the subclass to refresh the internal bean factory.
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

        // Prepare the bean factory for use in this context.
        prepareBeanFactory(beanFactory);

        try {
            // Allows post-processing of the bean factory in context subclasses.
            postProcessBeanFactory(beanFactory);

            // Invoke factory processors registered as beans in the context.
            invokeBeanFactoryPostProcessors(beanFactory);

            // Register bean processors that intercept bean creation.
            registerBeanPostProcessors(beanFactory);

            // Initialize message source for this context.
            initMessageSource();

            // Initialize event multicaster for this context.
            initApplicationEventMulticaster();

            // Initialize other special beans in specific context subclasses.
            onRefresh();

            // Check for listener beans and register them.
            registerListeners();

            // Instantiate all remaining (non-lazy-init) singletons.
            finishBeanFactoryInitialization(beanFactory);

            // Last step: publish corresponding event.
            finishRefresh();
        }

        catch (BeansException ex) {
            if (logger.isWarnEnabled()) {
                logger.warn("Exception encountered during context initialization - " +
                        "cancelling refresh attempt: " + ex);
            }

            // Destroy already created singletons to avoid dangling resources.
            destroyBeans();

            // Reset 'active' flag.
            cancelRefresh(ex);

            // Propagate exception to caller.
            throw ex;
        }

        finally {
            // Reset common introspection caches in Spring's core, since we
            // might not ever need metadata for singleton beans anymore...
            resetCommonCaches();
        }
    }
}

As you can see, in the onRefresh (context entry) method of the container, after the bean loading, parsing, factory initialization, BeanFactoryPostProcessor, BeanPostProcessor, listner registration, and initialization (dependency injection, completion of bean initialization), the finishRefresh method is called. The finishRefresh code logic is as follows:

protected void finishRefresh() {  
  // Initialize lifecycle processor for this context.  
  initLifecycleProcessor();  
  
  // Propagate refresh to lifecycle processor first
  getLifecycleProcessor().onRefresh();  
  
  // Publish the final event
  publishEvent(new ContextRefreshedEvent(this));  
  
  // Participate in LiveBeansView MBean, if active.  
  LiveBeansView.registerApplicationContext(this);  
}

First perform the initialization of LifeCycleProcessor, register it in the container, and then execute the onRefresh method of LifeCycleProcessor. Note that the start method has not been called yet. So onRefresh is prior to start.

  • The container implements the life cycle interface

We can find that ConfigurableApplicationContext also implements the lifeCycle interface (but when calling LifeCycleProcessor to get the interface Bean of lifeCycle, there is no ConfigurableApplicationContext implementation class). The start method of context is obtained through this interface. And the start method of context needs to be manually called by the caller who creates the context, so that is to say, the start method of context will not be called. The start of LifeCycleProcessor is triggered in the start of context. As mentioned in the official document above, start does not necessarily implement and call context.

This is a point of great concern. For the container context, onRefresh is called by the constructor that creates the context instance. Start needs to implement the context of lifeCycle interface to have this ability, and also needs the client to call the start method actively, so the start method of lifeCycle can be called.

  • Smartlife cycle and event monitoring mechanism By implementing the ApplicationListener interface, you can get the lifecycle event notification of the container.
public interface ApplicationListener<E extends ApplicationEvent\> extends EventListener {  
  
 /**  
 * Handle an application event.
 * @param event the event to respond to  
 */  
   void onApplicationEvent(E event);  
  
}

Compared with the way of event monitoring to sense container events, the implementation of smartlife cycle interface can obtain the ability of the whole life cycle, including the callback of various methods such as startup and shutdown. The broadcast of event listeners can see that the invocation in fact and lifeCycle is called back and forth. Event listening can get events at all stages of the container, but it needs to determine the type of event and handle different event logic.

ps:

  1. refresh may be because lifeCycle and events will be received multiple times in multiple factory context s (such as the web).
  2. For SmartLifeCycle, based on LifeCycle, refresh and isAutoRefresh methods are added. If the isAutoRefresh method returns true, onRefresh in LifeCycleProcessor will be called to.

Tags: Programming Spring less

Posted on Sun, 02 Feb 2020 10:32:54 -0500 by Altec