I am a graduate of 21 years with one year's work experience. My resume and professional skills are as follows. Now I review and prepare for the interview according to my resume and knowledge.
Record date: 2022.1.4
Most knowledge points are only briefly introduced, and the specific contents are reviewed in detail according to the recommended blog links.
Framework principle - Spring (14) Spring IOC source code finishRefresh()
AbstractApplicationContext#finishRefresh()
Refresh completion, including initializing lifecycle processor and publishing refresh completion events.
@SuppressWarnings("deprecation") protected void finishRefresh() { // Clear context-level resource caches (such as ASM metadata from scanning). // 1. Clear the context level resource cache clearResourceCaches(); // Initialize lifecycle processor for this context. // 2. Initialize the lifecycle processor for this context initLifecycleProcessor(); // Propagate refresh to lifecycle processor first. // 3. First, propagate the refresh completion event to the lifecycle processor (trigger the start method of SmartLifecycle that returns true through the isAutoStartup method) getLifecycleProcessor().onRefresh(); // Publish the final event. // 4. Push the context refresh completion event to the corresponding listener publishEvent(new ContextRefreshedEvent(this)); // Participate in LiveBeansView MBean, if active. if (!NativeDetector.inNativeImage()) { // 5. Register MBean s for monitoring and management through JMX LiveBeansView.registerApplicationContext(this); } }
initLifecycleProcessor()
Let's look at 2 in the finishRefresh() code block.
When the ApplicationContext is started or stopped, it will update the status with the cycles of all declared beans through the lifecycle processor. This step is to initialize the lifecycle processor.
It should be noted that Spring will first obtain the Bean instance with BeanName of "lifecycle processor" from the container. If not, Spring's default defaultlifecycle processor will be used. Therefore, when developers need to customize the lifecycle processor, they must declare their defined lifecycle processor as "lifecycle processor".
// LIFECYCLE_PROCESSOR_BEAN_NAME = "lifecycleProcessor"; protected void initLifecycleProcessor() { // 1. Get the current BeanFactory ConfigurableListableBeanFactory beanFactory = getBeanFactory(); // 2. Judge whether BeanFactory already has a lifecycle processor if (beanFactory.containsLocalBean(LIFECYCLE_PROCESSOR_BEAN_NAME)) { // 2.1 if it already exists, assign the bean to lifecycle processor this.lifecycleProcessor = beanFactory.getBean(LIFECYCLE_PROCESSOR_BEAN_NAME, LifecycleProcessor.class); if (logger.isTraceEnabled()) { logger.trace("Using LifecycleProcessor [" + this.lifecycleProcessor + "]"); } } else { // 2.2 if it does not exist, use defaultlifecycle processor DefaultLifecycleProcessor defaultProcessor = new DefaultLifecycleProcessor(); defaultProcessor.setBeanFactory(beanFactory); this.lifecycleProcessor = defaultProcessor; // The defaultlifecycle processor is registered in BeanFactory as the default lifecycle processor beanFactory.registerSingleton(LIFECYCLE_PROCESSOR_BEAN_NAME, this.lifecycleProcessor); if (logger.isTraceEnabled()) { logger.trace("No '" + LIFECYCLE_PROCESSOR_BEAN_NAME + "' bean, using " + "[" + this.lifecycleProcessor.getClass().getSimpleName() + "]"); } } }
getLifecycleProcessor().onRefresh()
Let's look at 3 in the finishRefresh() code block.
After initializing the LifecycleProcessor, the onRefresh() method of the LifecycleProcessor interface is invoked to refresh all Bean that implements Lifecycle.
LifecycleProcessor getLifecycleProcessor() throws IllegalStateException { if (this.lifecycleProcessor == null) { throw new IllegalStateException( "LifecycleProcessor not initialized - " + "call 'refresh' before invoking lifecycle methods via the context: " + this); } return this.lifecycleProcessor; } // DefaultLifecycleProcessor 122 @Override public void onRefresh() { startBeans(true); this.running = true; } // DefaultLifecycleProcessor 141 private void startBeans(boolean autoStartupOnly) { // 1. Get all lifecycle beans Map<String, Lifecycle> lifecycleBeans = getLifecycleBeans(); // The lifecycle beans are grouped by phases, which are obtained by implementing the Phased interface Map<Integer, LifecycleGroup> phases = new TreeMap<>(); lifecycleBeans.forEach((beanName, bean) -> { /* autoStartupOnly=true Indicates that the container starts automatically when the ApplicationContext is refreshed autoStartupOnly=false The delegate is started through the displayed call */ // 3. When autoStartupOnly=false, that is, start through the displayed call, all lifecycles will be triggered; // When autoStartupOnly=true, that is, when the ApplicationContext is refreshed, the container starts automatically, which will only trigger the SmartLifecycle in which the isAutoStartup method returns true if (!autoStartupOnly || ( bean instanceof SmartLifecycle && ((SmartLifecycle) bean).isAutoStartup() ) ) { // 3.1 get the phase value of the bean (if the Phased interface is not implemented, the value is 0) int phase = getPhase(bean); // 3.2 add the bean to the lifecycle group where the phase value is stored phases.computeIfAbsent( phase, // 3.3 if the lifecycle group of the phase value is null, create a new one p -> new LifecycleGroup(phase, this.timeoutPerShutdownPhase, lifecycleBeans, autoStartupOnly) ).add(beanName, bean); } }); // 4. If phases is not empty if (!phases.isEmpty()) { // 4.1 call the start method of all lifecycles in the Lifecycle group phases.values().forEach(LifecycleGroup::start); } }
The Phased interface is also introduced here. This interface is similar to the Ordered interface. There is only one method for returning a "phase value" with the range of integer MIN_ VALUE ~ Integer. MAX_ VALUE. In the start-up process, the one with small "phase value" will be called first, while in the shutdown process, the one with large "phase value" will be called first.
publishEvent()
Let's look at 4 in the finishRefresh() code block.
When the ApplicationContext container refresh is completed, Spring publishes the ContextRefreshedEvent event through the event publishing mechanism. To ensure that the corresponding listener can do further logical processing.
Spring's publish event uses listener mode.
@Override public void publishEvent(ApplicationEvent event) { publishEvent(event, null); } protected void publishEvent(Object event, @Nullable ResolvableType eventType) { Assert.notNull(event, "Event must not be null"); // Decorate event as an ApplicationEvent if necessary // 1. If it is an ApplicationEvent, turn it forcibly. If not, package it into a PayloadApplicationEvent ApplicationEvent applicationEvent; if (event instanceof ApplicationEvent) { applicationEvent = (ApplicationEvent) event; } else { applicationEvent = new PayloadApplicationEvent<>(this, event); if (eventType == null) { // Wrap it into a PayloadApplicationEvent and get the actual event type with generics // For example, payloadapplicationevent < myevent > eventType = ((PayloadApplicationEvent<?>) applicationEvent).getResolvableType(); } } // Multicast right now if possible - or lazily once the multicaster is initialized if (this.earlyApplicationEvents != null) { this.earlyApplicationEvents.add(applicationEvent); } else { // 2. Use the event broadcaster to broadcast the event to the corresponding listener // Here is the event broadcaster previously initialized in initapplicationeventmulticast // There are also listeners previously registered in the registerListeners method getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType); } // Publish event via parent context as well... // 3. If the parent container is easy to exist at present, the parent container should also publish events if (this.parent != null) { if (this.parent instanceof AbstractApplicationContext) { ((AbstractApplicationContext) this.parent).publishEvent(event, eventType); } else { this.parent.publishEvent(event); } } }
multicastEvent()
Let's look at 2 in the publishEvent() code block.
How it uses the event broadcaster to broadcast events to the corresponding listeners.
// SimpleApplicationEventMulticaster 135 @Override public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) { ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event)); Executor executor = getTaskExecutor(); // 1. Getapplicationlisters: returns the application listener collection matching the given event type for (ApplicationListener<?> listener : getApplicationListeners(event, type)) { // 2. Return to the current task executor of this broadcaster if (executor != null) { // 3.1 if the executor is not null, use the executor to call the listener executor.execute(() -> invokeListener(listener, event)); } else { // 3.2 otherwise, call the listener directly invokeListener(listener, event); } } }
It can be found that when notifying the listener, it will check whether the Executor executor is configured in the applicationeventmulticast object. If so, it will encapsulate the invokeListener(listener, event) into a thread executable task and execute it to the Executor.
invokeListener()
Let's look at 3.1 and 3.2 of the invokeListener() code block. All are implemented in simpleapplicationeventmulticast.
Call the method of the listener.
protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) { // 1. Returns the current error handler for this broadcaster ErrorHandler errorHandler = getErrorHandler(); if (errorHandler != null) { try { // 2.1 if errorHandler is not null, call the given listener with error handling doInvokeListener(listener, event); } catch (Throwable err) { errorHandler.handleError(err); } } else { // 2.2 otherwise, call the given listener directly doInvokeListener(listener, event); } } @SuppressWarnings({"rawtypes", "unchecked"}) private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) { try { // Trigger the onApplicationEvent method of the listener, and the parameter is the given event listener.onApplicationEvent(event); } catch (ClassCastException ex) { String msg = ex.getMessage(); if (msg == null || matchesClassCastMessage(msg, event.getClass()) || (event instanceof PayloadApplicationEvent && matchesClassCastMessage(msg, ((PayloadApplicationEvent) event).getPayload().getClass()))) { // Possibly a lambda-defined listener which we could not resolve the generic event type for // -> let's suppress the exception. Log loggerToUse = this.lazyLogger; if (loggerToUse == null) { loggerToUse = LogFactory.getLog(getClass()); this.lazyLogger = loggerToUse; } if (loggerToUse.isTraceEnabled()) { loggerToUse.trace("Non-matching event type for listener: " + listener, ex); } } else { throw ex; } } }
Implementation of custom broadcaster
We can customize an applicationeventmulticast:
//It should be noted that our name must be application event multicast, otherwise it will not take effect. Just look back at the source code @Component("applicationEventMulticaster") public class MyMulticaster extends SimpleApplicationEventMulticaster { @Override protected Executor getTaskExecutor() { // Here, you can configure the thread pool according to the actual needs return new SimpleAsyncTaskExecutor(); } // This method is mainly used to configure error handling. By default, only a warning level log will be printed @Override protected ErrorHandler getErrorHandler() { return (() -> { System.out.println("Error "); }); } }
After the above configuration, all our publish events will be executed through the asynchronous mechanism, but we want to ask a question. Sometimes we don't want to be asynchronous? Some of us want to be synchronous and some want to be asynchronous. What should we do at this time? We can do this by:
- Annotate the configuration class @ EnableAsync
- Annotate the executed method @ Async
// Add @ Async annotation to the executed method @EventListener @Async public void on(MySecondEvent event) throws Exception { // ...... }
In this way, our method will be executed asynchronously through aop. An error may be reported when we do not configure the thread pool:
org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'org.springframework.core.task.TaskExecutor' available
However, our method will still execute asynchronously. This is mainly the following code:
protected Executor getDefaultExecutor(@Nullable BeanFactory beanFactory) { if (beanFactory != null) { try { // A NoSuchBeanDefinitionException will be thrown here return beanFactory.getBean(TaskExecutor.class); } catch (NoUniqueBeanDefinitionException ex) { logger.debug("Could not find unique TaskExecutor bean", ex); try { return beanFactory.getBean(DEFAULT_TASK_EXECUTOR_BEAN_NAME, Executor.class); } catch (NoSuchBeanDefinitionException ex2) { if (logger.isInfoEnabled()) { logger.info("More than one TaskExecutor bean found within the context, and none is named " + "'taskExecutor'. Mark one of them as primary or name it 'taskExecutor' (possibly " + "as an alias) in order to use it for async processing: " + ex.getBeanNamesFound()); } } } // Captured here catch (NoSuchBeanDefinitionException ex) { logger.debug("Could not find default TaskExecutor bean", ex); try { // We also didn't define a name as DEFAULT_TASK_EXECUTOR_BEAN_NAME = "taskExecutor" // Thread pool for return beanFactory.getBean(DEFAULT_TASK_EXECUTOR_BEAN_NAME, Executor.class); } catch (NoSuchBeanDefinitionException ex2) { logger.info("No task executor bean found for async processing: " + "no bean of type TaskExecutor and no bean named 'taskExecutor' either"); } // Giving up -> either using local default executor or none at all... } } // Finally, a null is returned return null; }
@Override @Nullable // Finally, a SimpleAsyncTaskExecutor is returned protected Executor getDefaultExecutor(@Nullable BeanFactory beanFactory) { Executor defaultExecutor = super.getDefaultExecutor(beanFactory); return (defaultExecutor != null ? defaultExecutor : new SimpleAsyncTaskExecutor()); }
Implementation of custom listener
If we want to do some logic after the Spring IoC container is built, we can implement it through the listener.
For example, create a custom listener, implement the ApplicationListener interface, listen to the ContextRefreshedEvent (context refresh completion event), and register the listener with the Spring IoC container.
@Component("myRefreshedListener") public class MyRefreshedListener implements ApplicationListener<ContextRefreshedEvent> { @Override public void onApplicationEvent(ContextRefreshedEvent event) { // ... } }
In this way, when Spring executes the finishRefresh method, it will push the ContextRefreshedEvent event to MyRefreshedListener.
Similar to ContextRefreshedEvent are ContextStartedEvent, contextcloseedevent, and ContextStoppedEvent. If you are interested, you can see the usage scenarios of these events.
Of course, we can also customize the listening event. We only need to inherit the ApplicationContextEvent abstract class.