diff --git "a/springboot-source-code-analysis/(7)344円272円213円344円273円266円347円233円221円345円220円254円345円231円250円350円247円246円345円217円221円346円234円272円345円210円266円350円247円243円346円236円220円.md" "b/springboot-source-code-analysis/(7)344円272円213円344円273円266円347円233円221円345円220円254円345円231円250円350円247円246円345円217円221円346円234円272円345円210円266円350円247円243円346円236円220円.md" new file mode 100644 index 0000000..de9c491 --- /dev/null +++ "b/springboot-source-code-analysis/(7)344円272円213円344円273円266円347円233円221円345円220円254円345円231円250円350円247円246円345円217円221円346円234円272円345円210円266円350円247円243円346円236円220円.md" @@ -0,0 +1,372 @@ +### 事件监听器触发机制解析 + + + +#### 以SpringBoot Staring事件具例子 + +- SpringContext#run方法中的,listeners.staring()方法 + +```java + /** + * Run the Spring application, creating and refreshing a new + * {@link ApplicationContext}. + * @param args the application arguments (usually passed from a Java main method) + * @return a running {@link ApplicationContext} + */ + public ConfigurableApplicationContext run(String... args) { + StopWatch stopWatch = new StopWatch(); + stopWatch.start(); + ConfigurableApplicationContext context = null; + Collection exceptionReporters = new ArrayList(); + configureHeadlessProperty(); + SpringApplicationRunListeners listeners = getRunListeners(args); + // 告知框架已经开始启动 + listeners.starting(); + try { +``` + + + +- SpringApplicationRunListeners#starting() 内部方法 + +```java + public void starting() { + // 调用 SpringApplicationRunListenser#staring() 方法 + for (SpringApplicationRunListener listener : this.listeners) { + listener.starting(); + } + } +``` + + + +- SpringApplicationRunListener方法,定义了框架各个阶段的事件监听方法 + +```java +/** + * Listener for the {@link SpringApplication} {@code run} method. + * {@link SpringApplicationRunListener}s are loaded via the {@link SpringFactoriesLoader} + * and should declare a public constructor that accepts a {@link SpringApplication} + * instance and a {@code String[]} of arguments. A new + * {@link SpringApplicationRunListener} instance will be created for each run. + * + * @author Phillip Webb + * @author Dave Syer + * @author Andy Wilkinson + * @since 1.0.0 + */ +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); + +} +``` + + + +- SpringApplicationRunListener#starting() 方法,通过 SimpleApplicationEventMulticaster 广播器发送 ApplicationStartingEvent 事件 + +```java +/** + * {@link SpringApplicationRunListener} to publish {@link SpringApplicationEvent}s. + *

+ * Uses an internal {@link ApplicationEventMulticaster} for the events that are fired + * before the context is actually refreshed. + * + * @author Phillip Webb + * @author Stephane Nicoll + * @author Andy Wilkinson + * @author Artsiom Yudovin + * @since 1.0.0 + */ +public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered { + + private final SpringApplication application; + + private final String[] args; + + private final SimpleApplicationEventMulticaster initialMulticaster; + + public EventPublishingRunListener(SpringApplication application, String[] args) { + this.application = application; + this.args = args; + this.initialMulticaster = new SimpleApplicationEventMulticaster(); + for (ApplicationListener listener : application.getListeners()) { + this.initialMulticaster.addApplicationListener(listener); + } + } + + @Override + public void starting() { + // 通过广播器,发送ApplicationStaringEvent事件 + this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(this.application, this.args)); + } + +``` + + + +- SimpleApplicationEventMulticaster#multicastEvent() 广播器发送方法 + +```java +public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster { + + @Override + public void multicastEvent(ApplicationEvent event) { + multicastEvent(event, resolveDefaultEventType(event)); + } + + @Override + public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) { + ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event)); + Executor executor = getTaskExecutor(); + // 通过AbstractApplicationEventMulticaster#getApplicationListeners() 方法获得对该事件感兴趣的监听器列表 + for (ApplicationListener listener : getApplicationListeners(event, type)) { + if (executor != null) { + executor.execute(() -> invokeListener(listener, event)); + } + else { + invokeListener(listener, event); + } + } + } +``` + + + +- AbstractApplicationEventMulticaster#getApplicationListeners() 方法,获得对该事件感兴趣的监听器列表 + +```java + /** + * Return a Collection of ApplicationListeners matching the given + * event type. Non-matching listeners get excluded early. + * @param event the event to be propagated. Allows for excluding + * non-matching listeners early, based on cached matching information. + * @param eventType the event type + * @return a Collection of ApplicationListeners + * @see org.springframework.context.ApplicationListener + */ + protected Collection> getApplicationListeners( + ApplicationEvent event, ResolvableType eventType) { + + Object source = event.getSource(); + Class sourceType = (source != null ? source.getClass() : null); + ListenerCacheKey cacheKey = new ListenerCacheKey(eventType, sourceType); + + // Quick check for existing entry on ConcurrentHashMap... + ListenerRetriever retriever = this.retrieverCache.get(cacheKey); + if (retriever != null) { + return retriever.getApplicationListeners(); + } + + if (this.beanClassLoader == null || + (ClassUtils.isCacheSafe(event.getClass(), this.beanClassLoader) && + (sourceType == null || ClassUtils.isCacheSafe(sourceType, this.beanClassLoader)))) { + // Fully synchronized building and caching of a ListenerRetriever + // 避免其它线程操作ListenersCache缓存 + synchronized (this.retrievalMutex) { + retriever = this.retrieverCache.get(cacheKey); + // 双重检测锁 + if (retriever != null) { + return retriever.getApplicationListeners(); + } + retriever = new ListenerRetriever(true); + // 通过AbstractApplicationEventMulicaster#retrieveApplicationListeners()获取感兴趣的事件监听列表 + Collection> listeners = + retrieveApplicationListeners(eventType, sourceType, retriever); + this.retrieverCache.put(cacheKey, retriever); + return listeners; + } + } + else { + // No ListenerRetriever caching -> no synchronization necessary + return retrieveApplicationListeners(eventType, sourceType, null); + } + } +``` + + + +- 如果 this.retrieverCache 缓存获取不到 监听器列表,就从AbstractApplicationEventMulticaster#retrieveApplicationListeners()中获取 + +```java +/** + * Actually retrieve the application listeners for the given event and source type. + * @param eventType the event type + * @param sourceType the event source type + * @param retriever the ListenerRetriever, if supposed to populate one (for caching purposes) + * @return the pre-filtered list of application listeners for the given event and source type + */ + private Collection> retrieveApplicationListeners( + ResolvableType eventType, @Nullable Class sourceType, @Nullable ListenerRetriever retriever) { + + List> allListeners = new ArrayList(); + Set> listeners; + Set listenerBeans; + // 通过互斥锁初始化Set对象 + synchronized (this.retrievalMutex) { + listeners = new LinkedHashSet(this.defaultRetriever.applicationListeners); + listenerBeans = new LinkedHashSet(this.defaultRetriever.applicationListenerBeans); + } + /** + 获取所有事件监听器 + 在 spring.factories 中定义的实践监听器 + # Application Listeners + org.springframework.context.ApplicationListener=\ + org.springframework.boot.ClearCachesApplicationListener,\ + org.springframework.boot.builder.ParentContextCloserApplicationListener,\ + org.springframework.boot.context.FileEncodingApplicationListener,\ + org.springframework.boot.context.config.AnsiOutputApplicationListener,\ + org.springframework.boot.context.config.ConfigFileApplicationListener,\ + org.springframework.boot.context.config.DelegatingApplicationListener,\ + org.springframework.boot.context.logging.ClasspathLoggingApplicationListener,\ + org.springframework.boot.context.logging.LoggingApplicationListener,\ + org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener + */ + for (ApplicationListener listener : listeners) { + // 判断是否对该事件感兴趣 + if (supportsEvent(listener, eventType, sourceType)) { + if (retriever != null) { + retriever.applicationListeners.add(listener); + } + // 感兴趣就加入到集合当中 + allListeners.add(listener); + } + } + if (!listenerBeans.isEmpty()) { + BeanFactory beanFactory = getBeanFactory(); + for (String listenerBeanName : listenerBeans) { + try { + Class listenerType = beanFactory.getType(listenerBeanName); + if (listenerType == null || supportsEvent(listenerType, eventType)) { + ApplicationListener listener = + beanFactory.getBean(listenerBeanName, ApplicationListener.class); + if (!allListeners.contains(listener) && supportsEvent(listener, eventType, sourceType)) { + if (retriever != null) { + if (beanFactory.isSingleton(listenerBeanName)) { + retriever.applicationListeners.add(listener); + } + else { + retriever.applicationListenerBeans.add(listenerBeanName); + } + } + allListeners.add(listener); + } + } + } + catch (NoSuchBeanDefinitionException ex) { + // Singleton listener instance (without backing bean definition) disappeared - + // probably in the middle of the destruction phase + } + } + } + // 监听器排序 + AnnotationAwareOrderComparator.sort(allListeners); + if (retriever != null && retriever.applicationListenerBeans.isEmpty()) { + retriever.applicationListeners.clear(); + retriever.applicationListeners.addAll(allListeners); + } + return allListeners; + } +``` + + + +- 通过AbstractApplicationEventMuliticaster#supportEvent() 方法,判断该事件监听器,是否对该事件感兴趣? + +```java + /** + * Determine whether the given listener supports the given event. + *

The default implementation detects the {@link SmartApplicationListener} + * and {@link GenericApplicationListener} interfaces. In case of a standard + * {@link ApplicationListener}, a {@link GenericApplicationListenerAdapter} + * will be used to introspect the generically declared type of the target listener. + * @param listener the target listener to check + * @param eventType the event type to check against + * @param sourceType the source type to check against + * @return whether the given listener should be included in the candidates + * for the given event type + */ + protected boolean supportsEvent( + ApplicationListener listener, ResolvableType eventType, @Nullable Class sourceType) { + + // 判断当前事件监听器是否是GenericApplicationListerner接口类的实现类,如果不是的话,就做个委派,方便获取该事件监听器感兴趣事件 + GenericApplicationListener smartListener = (listener instanceof GenericApplicationListener ? + (GenericApplicationListener) listener : new GenericApplicationListenerAdapter(listener)); + // 判断当前监听器是否感兴趣该事件 + return (smartListener.supportsEventType(eventType) && smartListener.supportsSourceType(sourceType)); + } +``` + + + + + +#### 获取监听器列表逻辑 + +image-20210920200242597 + +- 通过AbstractApplicationEventMulticaster#getApplicationListeners()方法获取感兴趣事件监听器列表 +- 判断当前SpringBoot缓存中是否存在该事件感兴趣的监听器列表 +- 如果没有,调用通过AbstractApplicationEventMulicaster#retrieveApplicationListener()方法,获取所有事件监听器列表 +- 遍历所有事件监听器列表 +- 通过AbstractApplicationEventMuliticaster#supportsEvent() 方法查找出对该事件感兴趣的事件监听器 +- 将感兴趣的事件监听器列表加入SpirngBoot缓存中,并返回给事件触发器 + + + +#### AbstractApplicationEventMuliticaster#supportEvent() 事件监听器匹配器触发条件 + +image-20210920201046825 \ No newline at end of file diff --git a/springboot-source-code-analysis/pom.xml b/springboot-source-code-analysis/pom.xml index 68aeeaf..568bd37 100644 --- a/springboot-source-code-analysis/pom.xml +++ b/springboot-source-code-analysis/pom.xml @@ -5,7 +5,7 @@ org.springframework.boot spring-boot-starter-parent - 2.4.0 + 2.1.7.RELEASE com.ipman.source.code.analysis.springboot diff --git a/springboot-source-code-analysis/src/test/java/com/example/springboot/source/code/analysis/SpringbootSourceCodeAnalysisApplicationTests.java b/springboot-source-code-analysis/src/test/java/com/example/springboot/source/code/analysis/SpringbootSourceCodeAnalysisApplicationTests.java index a1a9cf5..3dd0749 100644 --- a/springboot-source-code-analysis/src/test/java/com/example/springboot/source/code/analysis/SpringbootSourceCodeAnalysisApplicationTests.java +++ b/springboot-source-code-analysis/src/test/java/com/example/springboot/source/code/analysis/SpringbootSourceCodeAnalysisApplicationTests.java @@ -2,7 +2,7 @@ import com.example.springboot.source.code.analysis.event.RainListener; import com.example.springboot.source.code.analysis.event.WeatherRunListener; -import org.junit.jupiter.api.Test; +import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest;

AltStyle によって変換されたページ (->オリジナル) /