Spring source code: what have you done since ApplicationContext was started?

1, Introduction

When using ApplicationContext to start the Spring container, the code is as follows:

ApplicationContext applicationContext = new ClassPathXmlApplicationContext(
                "applicationContext.xml");

This line of code, including the whole process of Spring startup, will go through many steps. What we're going to talk about today is the last of many of these steps: finishRefresh()
The purpose is to see what else Spring does after all Bean instances in the container are initialized?

2, Source code analysis

1. Application context refresh classic entry

The steps to start the container are completed by the refresh method of the AbstractApplicationContext class. The code is as follows:

	public void refresh() throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {
			// 1. Context environment to be refreshed
			prepareRefresh();

			// 2. Initialize BeanFactory and load XML file
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

			// 3. Fill in various functions of BeanFactory
			prepareBeanFactory(beanFactory);

			try {
				// 4. Subclass covering violation for additional treatment
				postProcessBeanFactory(beanFactory);

				// 5. Call BeanFactory postprocessor
				invokeBeanFactoryPostProcessors(beanFactory);

				// 6. Register bean postprocessors and call back the methods of these bean postprocessors when calling getBean
				registerBeanPostProcessors(beanFactory);

				// 7. Initialize Message source for context
				initMessageSource();

				// 8. Initialize event multicast
				initApplicationEventMulticaster();

				// 9. Leave the subclass to initialize other bean s
				onRefresh();

				// 10. Register monitor
				registerListeners();

				// 11. Initialize the remaining singleton beans (non inert)
				finishBeanFactoryInitialization(beanFactory);

				// 12. Last step, issue notification event
				finishRefresh();
			}

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

				// Destroy the singleton that has been created to avoid suspending 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();
			}
		}
	}

Obviously, the focus of this chapter is step 12. Let's take a look at it~

2. Application context refresh completed

Spring's official explanation of this method: call the onRefresh() method of LifecycleProcessor, and publish ContextRefreshedEvent

	protected void finishRefresh() {
		// 1. Clear the context level resource cache (such as scanned ASM metadata).
		clearResourceCaches();

		// 2. Initialize lifecycle processor for context
		initLifecycleProcessor();

		// 3. Call the onRefresh method of lifecycle processor
		getLifecycleProcessor().onRefresh();

		// 4. Event of publishing context refresh completed
		publishEvent(new ContextRefreshedEvent(this));

		// 5. Participate in LiveBeansView MBean, if it is active.
		LiveBeansView.registerApplicationContext(this);
	}

In terms of the number of lines of code, five things have been done. We don't need to pay attention to the first and fifth steps, which are not practical.
When Spring completes the context refresh, it can briefly summarize the following three steps:

  • Initialize lifecycle processor
  • Call the onRefresh method of lifecycle processor
  • Publish contextrefreshedevent (context refresh completion event)
    Used to notify listeners in the Spring container

Lifecycle processor is the lifecycle processor. When ApplicationContext starts and stops, it informs Spring of the objects that implement the lifecycle interface.

Let's look at these three steps separately

3. Initialize lifecycle processor
	protected void initLifecycleProcessor() {
		ConfigurableListableBeanFactory beanFactory = getBeanFactory();
		if (beanFactory.containsLocalBean(LIFECYCLE_PROCESSOR_BEAN_NAME)) {
			this.lifecycleProcessor =
					beanFactory.getBean(LIFECYCLE_PROCESSOR_BEAN_NAME, LifecycleProcessor.class);
			if (logger.isDebugEnabled()) {
				logger.debug("Using LifecycleProcessor [" + this.lifecycleProcessor + "]");
			}
		}
		else {
			// Default lifecycle processor
			DefaultLifecycleProcessor defaultProcessor = new DefaultLifecycleProcessor();
			defaultProcessor.setBeanFactory(beanFactory);
			this.lifecycleProcessor = defaultProcessor;
			// Register the default lifecycle processor to the container		
			beanFactory.registerSingleton(LIFECYCLE_PROCESSOR_BEAN_NAME, this.lifecycleProcessor);
			if (logger.isDebugEnabled()) {
				logger.debug("Unable to locate LifecycleProcessor with name '" +
						LIFECYCLE_PROCESSOR_BEAN_NAME +
						"': using default [" + this.lifecycleProcessor + "]");
			}
		}
	}

This step is relatively simple. If there is no user-defined lifecycle processor in the container, the default lifecycle processor will be used

4. onRefresh
	public void onRefresh() {
		startBeans(true);
		this.running = true;
	}

	private void startBeans(boolean autoStartupOnly) {
		// 1. Get all beans of Lifecycle type in the container
		Map<String, Lifecycle> lifecycleBeans = getLifecycleBeans();
		Map<Integer, LifecycleGroup> phases = new HashMap<>();
		// If it is a Bean of SmartLifecycle type, isAutoStartup needs to return true to continue
		// Non SmartLifecycle type Bean skipped because autoStartupOnly value is true
		lifecycleBeans.forEach((beanName, bean) -> {
			if (!autoStartupOnly || (bean instanceof SmartLifecycle && ((SmartLifecycle) bean).isAutoStartup())) {
				// 2. Get the phase value of the Lifecycle to control the call order
				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());
			// sort
			Collections.sort(keys);
			for (Integer key : keys) {
				// Cycle the start method of Lifecycle
				phases.get(key).start();
			}
		}
	}

Multiple beans that implement the Lifecycle interface can be defined in the container. As you can see from the code, the start methods of these beans will not be called at this time.
We usually implement the SmartLifecycle interface and set the isAutoStartup() return value to true.
ps: when implementing the SmartLifecycle interface, you need to override the getPhase() method, which is used to control the execution order of the start() and stop() methods of Lifecycle. The smaller the value, the earlier the execution. The default value is 0

5. Event of publishing context refresh completed

Students who haven't used monitoring, please move first Complete use steps of Spring listener

The purpose of publishing events is to inform listeners in the container and then execute the custom logic in the listener. The bottom layer of publishing event is calling the multicasevent method of SimpleApplicationEventMulticaster class. Let's see

	public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
		ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
		// Loop monitor
		for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
			Executor executor = getTaskExecutor();
			if (executor != null) {
				executor.execute(() -> invokeListener(listener, event));
			}
			else {
				// Call listening method
				invokeListener(listener, event);
			}
		}
	}

Take a look at the invokeListener method

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

There is no logic in this method. Continue to see the doInvokeListener method

	private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
		try {
			// Call the onApplicationEvent method of the listener
			listener.onApplicationEvent(event);
		}
		catch (ClassCastException ex) {
			String msg = ex.getMessage();
			if (msg == null || matchesClassCastMessage(msg, event.getClass().getName())) {
				// Possibly a lambda-defined listener which we could not resolve the generic event type for
				// -> let's suppress the exception and just log a debug message.
				Log logger = LogFactory.getLog(getClass());
				if (logger.isDebugEnabled()) {
					logger.debug("Non-matching event type for listener: " + listener, ex);
				}
			}
			else {
				throw ex;
			}
		}
	}

Seeing this place, we can see why the custom listener will override the onApplicationEvent method, because after the container publishes the event, it will eventually call the onApplicationEvent method of the listener.

3, Summary

                         .

  • Lifecycle processor is to notify the Bean in the container that implements the SmartLifecycle interface of the declaration cycle of ApplicationContext. The start() and stop() methods of these beans are called. The return value of isAutoStartup() method needs to be set to true for it to take effect.
  • The ContextRefreshedEvent is issued to notify the beans (listeners) in the container that implement the ApplicationListener interface, that is, to call the onApplicationEvent() method of these beans

Thinking: to write an easy to understand article, you need to find the pre knowledge that you understand, but others don't understand, and express it.

Tags: Spring xml Lambda

Posted on Sun, 24 May 2020 04:01:23 -0700 by yalag