2. Monitor mode

Keywords: Programming Spring SpringBoot

When you click Download Sources in the IDEA, you will get an error and cannot download sources. To solve this problem, enter in Terminal
mvn dependency:resolve -Dclassifier=sources
 Reference: https://www.cnblogs.com/wwjj4811/p/10364580.html
When the system runs to some key nodes, it will broadcast some events, and there are some listeners in the system who are interested in these events, and they will be monitored and heard, thus triggering some behavior

2.1. Add listener to SpringBoot container

@SpringBootApplication
public class SpringbootApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringbootApplication.class, args);
    }
}

// Go to the constructor of SpringApplication. The first section of the code also analyzes
public SpringApplication(ResourceLoader resourceLoader, Class<!--?-->... primarySources) {
    this.resourceLoader = resourceLoader;
    Assert.notNull(primarySources, "PrimarySources must not be null");
    this.primarySources = new LinkedHashSet&lt;&gt;(Arrays.asList(primarySources));
    this.webApplicationType = WebApplicationType.deduceFromClasspath();
    // Get the initializer configured in spring.factories, and also get the listener and put it into the cache
    setInitializers((Collection)getSpringFactoriesInstances(ApplicationContextInitializer.class));
    // This step is the same as the step of getting the initializer. It is also read through the spring factors loader. Since it has been obtained, it is directly taken from the cache
    // The following figure shows the listener configured by spring boot by default
    setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    this.mainApplicationClass = deduceMainApplicationClass();
}

2.2 release events

Spring boot will publish some events at the key nodes of the container. The figure above shows the event publishing sequence of spring boot
 1) The starting event will be issued when the framework starts, and will be issued as soon as the framework starts
 2) Then an environmentPrepared event will be issued after the environment is ready, which means that spring boot has loaded the system properties and some properties we specified
 3) Then issue a contextInitialized event to indicate that the spring boot context is ready to be published before any Bean definitions are loaded
 4) Then send a prepared event to indicate that the spring boot application context has been created, but the bean has not been fully loaded
 5) Send the started event to indicate that SpringBoot has completed the bean of the single example, but has not yet called the two extension interfaces of ApplicationRunner and CommandLineRunner,
   Analysis of the two interfaces
 6) Send the ready event and publish it after the two extension interface calls
 7) Spring boot container fails, send failed event
// SpringApplicationRunListener defines the method to publish the above events. Through this interface, the method to publish the events is exposed, thus hiding the specific implementation
public interface SpringApplicationRunListener {

	/**
	 * Called immediately when the run method has first started. Can be used for very
	 * early initialization.
	 */
	void starting();

	/**
	 * Called once the environment has been prepared, but before the
	 * {@link ApplicationContext} has been created.
	 * @param environment the environment
	 */
	void environmentPrepared(ConfigurableEnvironment environment);

	/**
	 * Called once the {@link ApplicationContext} has been created and prepared, but
	 * before sources have been loaded.
	 * @param context the application context
	 */
	void contextPrepared(ConfigurableApplicationContext context);

	/**
	 * Called once the application context has been loaded but before it has been
	 * refreshed.
	 * @param context the application context
	 */
	void contextLoaded(ConfigurableApplicationContext context);

	/**
	 * The context has been refreshed and the application has started but
	 * {@link CommandLineRunner CommandLineRunners} and {@link ApplicationRunner
	 * ApplicationRunners} have not been called.
	 * @param context the application context.
	 * @since 2.0.0
	 */
	void started(ConfigurableApplicationContext context);

	/**
	 * Called immediately before the run method finishes, when the application context 
	 * has been refreshed and all {@link CommandLineRunner CommandLineRunners} and
	 * {@link ApplicationRunner ApplicationRunners} have been called.
	 * @param context the application context.
	 * @since 2.0.0
	 */
	void running(ConfigurableApplicationContext context);

	/**
	 * Called when a failure occurs when running the application.
	 * @param context the application context or {@code null} if a failure occurred 
	 * before the context was created
	 * @param exception the failure
	 * @since 2.0.0
	 */
	void failed(ConfigurableApplicationContext context, Throwable exception);
}
// Spring boot will publish some events at the key nodes of the container
// run method is the core method of SpringBoot, and some irrelevant code is deleted
public ConfigurableApplicationContext run(String... args) {
  // Step 1) 
  SpringApplicationRunListeners listeners = getRunListeners(args);
  // Step 2) publish the starting event when the framework has started
  listeners.starting();
  try {
     context = createApplicationContext();
     prepareContext(context, environment, listeners, applicationArguments,printedBanner);
     refreshContext(context);
     afterRefresh(context, applicationArguments);
     return context;
  }
}

2.2.1 step 1

// Step 1) 
SpringApplicationRunListeners listeners = getRunListeners(args);

private SpringApplicationRunListeners getRunListeners(String[] args) {
    // types is the parameter to get eventpublishingrunnistener constructor later
    Class<!--?-->[] types = new Class<!--?-->[] { SpringApplication.class, String[].class };
    return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(SpringApplicationRunListener.class,
									         types, this, args));
}

// The first section of the code here has been analyzed. Get the implementation class of the specified Type through SpringFactoriesLoader and instantiate it
private <t> Collection<t> getSpringFactoriesInstances(Class<t> type, 
                                                      Class<!--?-->[] parameterTypes, Object... args) {
    ClassLoader classLoader = getClassLoader();
    // Get the implementation class of the specified Type through SpringFactoriesLoader. Here, get the implementation class of SpringApplicationRunListener       
    // The implementation class is eventpublishingrunnistener, which is also the class exposed by SpringBoot for event publishing
    Set<string> names = new LinkedHashSet&lt;&gt;(
                              SpringFactoriesLoader.loadFactoryNames(type, classLoader));
    // Instantiate eventpublishing runlistener
    List<t> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
    AnnotationAwareOrderComparator.sort(instances);
    return instances;
}

// When the eventpublishing runlistener is instantiated above, its constructor is called through reflection
public EventPublishingRunListener(SpringApplication application, String[] args) {
    this.application = application;
    this.args = args;
    this.initialMulticaster = new SimpleApplicationEventMulticaster();
    // application.getListeners is obtained through springfactorysloader, which has been analyzed in step 2.1)
    for (ApplicationListener<!--?--> listener : application.getListeners()) {
        this.initialMulticaster.addApplicationListener(listener);
    }
}

public void addApplicationListener(ApplicationListener<!--?--> listener) {
    synchronized (this.retrievalMutex) {
        // Explicitly remove target for a proxy, if registered already,
        // in order to avoid double invocations of the same listener.
        Object singletonTarget = AopProxyUtils.getSingletonTarget(listener);
        if (singletonTarget instanceof ApplicationListener) {
            this.defaultRetriever.applicationListeners.remove(singletonTarget);
        }
        // Add to this.defaultRetriever.applicationListeners, which will be used later
        this.defaultRetriever.applicationListeners.add(listener);
        this.retrieverCache.clear();
    }
}

2.2.2 release events

// Step 2) publish the starting event when the framework has started
listeners.starting();

public void starting() {
    for (SpringApplicationRunListener listener : this.listeners) {
        // The listener here is eventpublishingrunnistener, which is used to publish events
        listener.starting();
    }
}

@Override
public void starting() {
    // Publish container start event
    this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(this.application, this.args));
}

@Override
public void multicastEvent(ApplicationEvent event) {
    // Parameter 2 is the resolution event type
    multicastEvent(event, resolveDefaultEventType(event));
}

@Override
public void multicastEvent(final ApplicationEvent event,
                           @Nullable ResolvableType eventType) {
    ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
    // No configuration, null by default
    Executor executor = getTaskExecutor();
    // Step a) obtain which listeners are interested in this event
    for (ApplicationListener<!--?--> listener : getApplicationListeners(event, type)) {
        if (executor != null) {
            executor.execute(() -&gt; invokeListener(listener, event));
        }
        else {
            // Step b) this way
            invokeListener(listener, event);
        }
    }
}
// Step a) obtain which listeners are interested in this event
protected Collection<applicationlistener<?>&gt; getApplicationListeners(ApplicationEvent event, ResolvableType eventType) {
    // Get the event source, here is spring application
    Object source = event.getSource();
    Class<!--?--> sourceType = (source != null ? source.getClass() : null);
    // Construct cache key according to event type and event source
    ListenerCacheKey cacheKey = new ListenerCacheKey(eventType, sourceType);

    // Quick check for existing entry on ConcurrentHashMap...
    // Get from cache
    ListenerRetriever retriever = this.retrieverCache.get(cacheKey);
    if (retriever != null) {
        return retriever.getApplicationListeners();
    }

    // beanClassLoader is null, take this branch
    if (this.beanClassLoader == null ||
        (ClassUtils.isCacheSafe(event.getClass(), this.beanClassLoader) &amp;&amp;
         (sourceType == null || ClassUtils.isCacheSafe(sourceType, this.beanClassLoader)))) {
        // Fully synchronized building and caching of a ListenerRetriever
        synchronized (this.retrievalMutex) {
            // Try getting from cache again
            retriever = this.retrieverCache.get(cacheKey);
            if (retriever != null) {
                return retriever.getApplicationListeners();
            }
            retriever = new ListenerRetriever(true);
            // Get the listeners and put them in the retriever. Analyze this step below
            Collection<applicationlistener<?>&gt; listeners =
                retrieveApplicationListeners(eventType, sourceType, retriever);
            this.retrieverCache.put(cacheKey, retriever);
            return listeners;
        }
    }
    else {
        // No ListenerRetriever caching -&gt; no synchronization necessary
        return retrieveApplicationListeners(eventType, sourceType, null);
    }
}
private Collection<applicationlistener<?>&gt; retrieveApplicationListeners(ResolvableType eventType, 
				           @Nullable Class<!--?--> sourceType, @Nullable ListenerRetriever retriever) {
    List<applicationlistener<?>&gt; allListeners = new ArrayList&lt;&gt;();
    Set<applicationlistener<?>&gt; listeners;
    Set<string> listenerBeans;
    synchronized (this.retrievalMutex) {
        // This is analyzed in 2.2.1 and step 1, which are some listeners configured by spring boot by default
        listeners = new LinkedHashSet&lt;&gt;(this.defaultRetriever.applicationListeners);
        listenerBeans = new LinkedHashSet&lt;&gt;
            (this.defaultRetriever.applicationListenerBeans);
    }
    // Then, each listener is traversed to determine whether the listener is interested in the event
    for (ApplicationListener<!--?--> listener : listeners) {
        // The key methods are as follows:
        if (supportsEvent(listener, eventType, sourceType)) {
            if (retriever != null) {
                retriever.applicationListeners.add(listener);
            }
            allListeners.add(listener);
        }
    }
    if (!listenerBeans.isEmpty()) {
        // The default value is blank. Delete the analysis here
    }
    // Sort listeners
    AnnotationAwareOrderComparator.sort(allListeners);
    if (retriever != null &amp;&amp; retriever.applicationListenerBeans.isEmpty()) {
        retriever.applicationListeners.clear();
        retriever.applicationListeners.addAll(allListeners);
    }
    return allListeners;
}
protected boolean supportsEvent(ApplicationListener<!--?--> listener, ResolvableType eventType, 
								 @Nullable Class<!--?--> sourceType) {
    // Determine whether it is a GenericApplicationListener. If it is not packaged as a GenericApplicationListener adapter
    GenericApplicationListener smartListener = (listener instanceof GenericApplicationListener ?
                                                                 (GenericApplicationListener) listener : 
                                                                            new GenericApplicationListenerAdapter(listener));
    // Formally determine whether you are interested in the event, which is divided into two steps: supportsexventtype and supportsSourceType
    return (smartListener.supportsEventType(eventType) &amp;&amp; smartListener.supportsSourceType(sourceType));
}

// Two things were done in packing
public GenericApplicationListenerAdapter(ApplicationListener<!--?--> delegate) {
    Assert.notNull(delegate, "Delegate listener must not be null");
    // Get listener type
    this.delegate = (ApplicationListener<applicationevent>) delegate;
    // Get the event types of interest to the listener
    this.declaredEventType = resolveDeclaredEventType(this.delegate);
}
@Override
public boolean supportsEventType(ResolvableType eventType) {
    // Determine whether it is a SmartApplicationListener. If it is, it has its own supportsexventtype method. Call the method
    // Judge whether it is supported
    if (this.delegate instanceof SmartApplicationListener) {
        Class<!--? extends ApplicationEvent--> eventClass = (Class<!--? extends ApplicationEvent-->) eventType.resolve();
        return (eventClass != null &amp;&amp; ((SmartApplicationListener) this.delegate).supportsEventType(eventClass));
    }
    else {
        // If not, judge whether it is a declaredEventType subclass directly based on eventType
        return (this.declaredEventType == null || this.declaredEventType.isAssignableFrom(eventType));
    }
}
@Override
public boolean supportsSourceType(@Nullable Class<!--?--> sourceType) {
    // If it does not belong to SmartApplicationListener, return true directly
    // If yes, then judge supportsSourceType
    return !(this.delegate instanceof SmartApplicationListener) ||
                                      ((SmartApplicationListener) this.delegate).supportsSourceType(sourceType);
}
// Step b) this way
invokeListener(listener, event);

protected void invokeListener(ApplicationListener<!--?--> listener, ApplicationEvent event) {
    ErrorHandler errorHandler = getErrorHandler();
    if (errorHandler != null) {
        try {
            doInvokeListener(listener, event);
        }
        catch (Throwable err) {
            errorHandler.handleError(err);
        }
    }
    else {
        // Come this way.
        doInvokeListener(listener, event);
    }
}

private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
    try {
        // Call onApplicationEvent method of each listener
        listener.onApplicationEvent(event);
    }
}

2.3. Custom listener

2.3.1 mode 1

@Order(1)
public class FirstListener implements ApplicationListener<applicationstartedevent> {

    @Override
    public void onApplicationEvent(ApplicationStartedEvent event) {
        System.out.println("hello first");
    }
}
// It is configured in the META-INF/spring.factories file under the resources directory. The listener configuration is the same as the initializer
org.springframework.context.ApplicationListener=com.lwh.springboot.listener.FirstListener

2.3.2 mode 2

@Order(2)
public class SecondListener implements ApplicationListener<applicationstartedevent> {
    @Override
    public void onApplicationEvent(ApplicationStartedEvent event) {
        System.out.println("hello second");
    }
}
@SpringBootApplication
public class SpringbootApplication {

    public static void main(String[] args) {
         //When testing the SecondInitializer, use this startup mode to add manually
         SpringApplication springApplication = new SpringApplication(SpringbootApplication.class);
         springApplication.addListeners(new SecondListener());
         springApplication.run(args);
    }
}

2.3.3. Mode 3

@Order(3)
public class ThirdListener implements ApplicationListener<applicationstartedevent> {
    @Override
    public void onApplicationEvent(ApplicationStartedEvent event) {
        System.out.println("hello third");
    }
}
// Configure in application.properties
context.listener.classes=com.lwh.springboot.listener.ThirdListener
//Principle analysis of adding spring boot to application.properties configuration
 There is a DelegatingApplicationListener, which will get the context.listener.classes property in application.properties,
Add it to the listeners collection

2.3.4. Mode 4

@Order(4)
public class FourthListener implements SmartApplicationListener {
    @Override
    public boolean supportsEventType(Class<!--? extends ApplicationEvent--> eventType) {
        return ApplicationStartedEvent.class.isAssignableFrom(eventType) || 
               ApplicationPreparedEvent.class.isAssignableFrom(eventType);
    }

    @Override
    public void onApplicationEvent(ApplicationEvent event) {
        System.out.println("hello fourth");
    }
}
Add in the same way as the previous three

2.4 interview questions

1. Introduce the lower monitor mode
 2. What are the implementation classes related to listeners in SpringBoot?
3. What are the events in the spring boot framework and their order?
4. Introduce the triggering mechanism of listening events
 5. How to customize the implementation of system listener and precautions
 6. Difference between implementing ApplicationListener interface and SmartApplicationListener interface
   ApplicationListener can only specify one event of interest, while SmartApplicationListener defines the supportseventype method, which can specify multiple events of interest,
   The supportsSourceType method is also defined to specify the event source
````</applicationstartedevent></applicationstartedevent></applicationstartedevent></applicationevent></string></applicationlistener<?></applicationlistener<?></applicationlistener<?></applicationlistener<?></applicationlistener<?></t></string></t></t></t>

Posted by Develop_Sake on Sun, 16 Feb 2020 22:43:36 -0800