|
| 1 | +### 事件监听器触发机制解析 |
| 2 | + |
| 3 | + |
| 4 | + |
| 5 | +#### 以SpringBoot Staring事件具例子 |
| 6 | + |
| 7 | +- SpringContext#run方法中的,listeners.staring()方法 |
| 8 | + |
| 9 | +```java |
| 10 | + /** |
| 11 | + * Run the Spring application, creating and refreshing a new |
| 12 | + * {@link ApplicationContext}. |
| 13 | + * @param args the application arguments (usually passed from a Java main method) |
| 14 | + * @return a running {@link ApplicationContext} |
| 15 | + */ |
| 16 | + public ConfigurableApplicationContext run(String... args) { |
| 17 | + StopWatch stopWatch = new StopWatch(); |
| 18 | + stopWatch.start(); |
| 19 | + ConfigurableApplicationContext context = null; |
| 20 | + Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>(); |
| 21 | + configureHeadlessProperty(); |
| 22 | + SpringApplicationRunListeners listeners = getRunListeners(args); |
| 23 | + // 告知框架已经开始启动 |
| 24 | + listeners.starting(); |
| 25 | + try { |
| 26 | +``` |
| 27 | + |
| 28 | + |
| 29 | + |
| 30 | +- SpringApplicationRunListeners#starting() 内部方法 |
| 31 | + |
| 32 | +```java |
| 33 | + public void starting() { |
| 34 | + // 调用 SpringApplicationRunListenser#staring() 方法 |
| 35 | + for (SpringApplicationRunListener listener : this.listeners) { |
| 36 | + listener.starting(); |
| 37 | + } |
| 38 | + } |
| 39 | +``` |
| 40 | + |
| 41 | + |
| 42 | + |
| 43 | +- SpringApplicationRunListener方法,定义了框架各个阶段的事件监听方法 |
| 44 | + |
| 45 | +```java |
| 46 | +/** |
| 47 | + * Listener for the {@link SpringApplication} {@code run} method. |
| 48 | + * {@link SpringApplicationRunListener}s are loaded via the {@link SpringFactoriesLoader} |
| 49 | + * and should declare a public constructor that accepts a {@link SpringApplication} |
| 50 | + * instance and a {@code String[]} of arguments. A new |
| 51 | + * {@link SpringApplicationRunListener} instance will be created for each run. |
| 52 | + * |
| 53 | + * @author Phillip Webb |
| 54 | + * @author Dave Syer |
| 55 | + * @author Andy Wilkinson |
| 56 | + * @since 1.0.0 |
| 57 | + */ |
| 58 | +public interface SpringApplicationRunListener { |
| 59 | + |
| 60 | + /** |
| 61 | + * Called immediately when the run method has first started. Can be used for very |
| 62 | + * early initialization. |
| 63 | + */ |
| 64 | + void starting(); |
| 65 | + |
| 66 | + /** |
| 67 | + * Called once the environment has been prepared, but before the |
| 68 | + * {@link ApplicationContext} has been created. |
| 69 | + * @param environment the environment |
| 70 | + */ |
| 71 | + void environmentPrepared(ConfigurableEnvironment environment); |
| 72 | + |
| 73 | + /** |
| 74 | + * Called once the {@link ApplicationContext} has been created and prepared, but |
| 75 | + * before sources have been loaded. |
| 76 | + * @param context the application context |
| 77 | + */ |
| 78 | + void contextPrepared(ConfigurableApplicationContext context); |
| 79 | + |
| 80 | + /** |
| 81 | + * Called once the application context has been loaded but before it has been |
| 82 | + * refreshed. |
| 83 | + * @param context the application context |
| 84 | + */ |
| 85 | + void contextLoaded(ConfigurableApplicationContext context); |
| 86 | + |
| 87 | + /** |
| 88 | + * The context has been refreshed and the application has started but |
| 89 | + * {@link CommandLineRunner CommandLineRunners} and {@link ApplicationRunner |
| 90 | + * ApplicationRunners} have not been called. |
| 91 | + * @param context the application context. |
| 92 | + * @since 2.0.0 |
| 93 | + */ |
| 94 | + void started(ConfigurableApplicationContext context); |
| 95 | + |
| 96 | + /** |
| 97 | + * Called immediately before the run method finishes, when the application context has |
| 98 | + * been refreshed and all {@link CommandLineRunner CommandLineRunners} and |
| 99 | + * {@link ApplicationRunner ApplicationRunners} have been called. |
| 100 | + * @param context the application context. |
| 101 | + * @since 2.0.0 |
| 102 | + */ |
| 103 | + void running(ConfigurableApplicationContext context); |
| 104 | + |
| 105 | + /** |
| 106 | + * Called when a failure occurs when running the application. |
| 107 | + * @param context the application context or {@code null} if a failure occurred before |
| 108 | + * the context was created |
| 109 | + * @param exception the failure |
| 110 | + * @since 2.0.0 |
| 111 | + */ |
| 112 | + void failed(ConfigurableApplicationContext context, Throwable exception); |
| 113 | + |
| 114 | +} |
| 115 | +``` |
| 116 | + |
| 117 | + |
| 118 | + |
| 119 | +- SpringApplicationRunListener#starting() 方法,通过 SimpleApplicationEventMulticaster 广播器发送 ApplicationStartingEvent 事件 |
| 120 | + |
| 121 | +```java |
| 122 | +/** |
| 123 | + * {@link SpringApplicationRunListener} to publish {@link SpringApplicationEvent}s. |
| 124 | + * <p> |
| 125 | + * Uses an internal {@link ApplicationEventMulticaster} for the events that are fired |
| 126 | + * before the context is actually refreshed. |
| 127 | + * |
| 128 | + * @author Phillip Webb |
| 129 | + * @author Stephane Nicoll |
| 130 | + * @author Andy Wilkinson |
| 131 | + * @author Artsiom Yudovin |
| 132 | + * @since 1.0.0 |
| 133 | + */ |
| 134 | +public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered { |
| 135 | + |
| 136 | + private final SpringApplication application; |
| 137 | + |
| 138 | + private final String[] args; |
| 139 | + |
| 140 | + private final SimpleApplicationEventMulticaster initialMulticaster; |
| 141 | + |
| 142 | + public EventPublishingRunListener(SpringApplication application, String[] args) { |
| 143 | + this.application = application; |
| 144 | + this.args = args; |
| 145 | + this.initialMulticaster = new SimpleApplicationEventMulticaster(); |
| 146 | + for (ApplicationListener<?> listener : application.getListeners()) { |
| 147 | + this.initialMulticaster.addApplicationListener(listener); |
| 148 | + } |
| 149 | + } |
| 150 | + |
| 151 | + @Override |
| 152 | + public void starting() { |
| 153 | + // 通过广播器,发送ApplicationStaringEvent事件 |
| 154 | + this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(this.application, this.args)); |
| 155 | + } |
| 156 | + |
| 157 | +``` |
| 158 | + |
| 159 | + |
| 160 | + |
| 161 | +- SimpleApplicationEventMulticaster#multicastEvent() 广播器发送方法 |
| 162 | + |
| 163 | +```java |
| 164 | +public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster { |
| 165 | + |
| 166 | + @Override |
| 167 | + public void multicastEvent(ApplicationEvent event) { |
| 168 | + multicastEvent(event, resolveDefaultEventType(event)); |
| 169 | + } |
| 170 | + |
| 171 | + @Override |
| 172 | + public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) { |
| 173 | + ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event)); |
| 174 | + Executor executor = getTaskExecutor(); |
| 175 | + // 通过AbstractApplicationEventMulticaster#getApplicationListeners() 方法获得对该事件感兴趣的监听器列表 |
| 176 | + for (ApplicationListener<?> listener : getApplicationListeners(event, type)) { |
| 177 | + if (executor != null) { |
| 178 | + executor.execute(() -> invokeListener(listener, event)); |
| 179 | + } |
| 180 | + else { |
| 181 | + invokeListener(listener, event); |
| 182 | + } |
| 183 | + } |
| 184 | + } |
| 185 | +``` |
| 186 | + |
| 187 | + |
| 188 | + |
| 189 | +- AbstractApplicationEventMulticaster#getApplicationListeners() 方法,获得对该事件感兴趣的监听器列表 |
| 190 | + |
| 191 | +```java |
| 192 | + /** |
| 193 | + * Return a Collection of ApplicationListeners matching the given |
| 194 | + * event type. Non-matching listeners get excluded early. |
| 195 | + * @param event the event to be propagated. Allows for excluding |
| 196 | + * non-matching listeners early, based on cached matching information. |
| 197 | + * @param eventType the event type |
| 198 | + * @return a Collection of ApplicationListeners |
| 199 | + * @see org.springframework.context.ApplicationListener |
| 200 | + */ |
| 201 | + protected Collection<ApplicationListener<?>> getApplicationListeners( |
| 202 | + ApplicationEvent event, ResolvableType eventType) { |
| 203 | + |
| 204 | + Object source = event.getSource(); |
| 205 | + Class<?> sourceType = (source != null ? source.getClass() : null); |
| 206 | + ListenerCacheKey cacheKey = new ListenerCacheKey(eventType, sourceType); |
| 207 | + |
| 208 | + // Quick check for existing entry on ConcurrentHashMap... |
| 209 | + ListenerRetriever retriever = this.retrieverCache.get(cacheKey); |
| 210 | + if (retriever != null) { |
| 211 | + return retriever.getApplicationListeners(); |
| 212 | + } |
| 213 | + |
| 214 | + if (this.beanClassLoader == null || |
| 215 | + (ClassUtils.isCacheSafe(event.getClass(), this.beanClassLoader) && |
| 216 | + (sourceType == null || ClassUtils.isCacheSafe(sourceType, this.beanClassLoader)))) { |
| 217 | + // Fully synchronized building and caching of a ListenerRetriever |
| 218 | + // 避免其它线程操作ListenersCache缓存 |
| 219 | + synchronized (this.retrievalMutex) { |
| 220 | + retriever = this.retrieverCache.get(cacheKey); |
| 221 | + // 双重检测锁 |
| 222 | + if (retriever != null) { |
| 223 | + return retriever.getApplicationListeners(); |
| 224 | + } |
| 225 | + retriever = new ListenerRetriever(true); |
| 226 | + // 通过AbstractApplicationEventMulicaster#retrieveApplicationListeners()获取感兴趣的事件监听列表 |
| 227 | + Collection<ApplicationListener<?>> listeners = |
| 228 | + retrieveApplicationListeners(eventType, sourceType, retriever); |
| 229 | + this.retrieverCache.put(cacheKey, retriever); |
| 230 | + return listeners; |
| 231 | + } |
| 232 | + } |
| 233 | + else { |
| 234 | + // No ListenerRetriever caching -> no synchronization necessary |
| 235 | + return retrieveApplicationListeners(eventType, sourceType, null); |
| 236 | + } |
| 237 | + } |
| 238 | +``` |
| 239 | + |
| 240 | + |
| 241 | + |
| 242 | +- 如果 this.retrieverCache 缓存获取不到 监听器列表,就从AbstractApplicationEventMulticaster#retrieveApplicationListeners()中获取 |
| 243 | + |
| 244 | +```java |
| 245 | +/** |
| 246 | + * Actually retrieve the application listeners for the given event and source type. |
| 247 | + * @param eventType the event type |
| 248 | + * @param sourceType the event source type |
| 249 | + * @param retriever the ListenerRetriever, if supposed to populate one (for caching purposes) |
| 250 | + * @return the pre-filtered list of application listeners for the given event and source type |
| 251 | + */ |
| 252 | + private Collection<ApplicationListener<?>> retrieveApplicationListeners( |
| 253 | + ResolvableType eventType, @Nullable Class<?> sourceType, @Nullable ListenerRetriever retriever) { |
| 254 | + |
| 255 | + List<ApplicationListener<?>> allListeners = new ArrayList<>(); |
| 256 | + Set<ApplicationListener<?>> listeners; |
| 257 | + Set<String> listenerBeans; |
| 258 | + // 通过互斥锁初始化Set<ApplicationListener>对象 |
| 259 | + synchronized (this.retrievalMutex) { |
| 260 | + listeners = new LinkedHashSet<>(this.defaultRetriever.applicationListeners); |
| 261 | + listenerBeans = new LinkedHashSet<>(this.defaultRetriever.applicationListenerBeans); |
| 262 | + } |
| 263 | + /** |
| 264 | + 获取所有事件监听器 |
| 265 | + 在 spring.factories 中定义的实践监听器 |
| 266 | + # Application Listeners |
| 267 | + org.springframework.context.ApplicationListener=\ |
| 268 | + org.springframework.boot.ClearCachesApplicationListener,\ |
| 269 | + org.springframework.boot.builder.ParentContextCloserApplicationListener,\ |
| 270 | + org.springframework.boot.context.FileEncodingApplicationListener,\ |
| 271 | + org.springframework.boot.context.config.AnsiOutputApplicationListener,\ |
| 272 | + org.springframework.boot.context.config.ConfigFileApplicationListener,\ |
| 273 | + org.springframework.boot.context.config.DelegatingApplicationListener,\ |
| 274 | + org.springframework.boot.context.logging.ClasspathLoggingApplicationListener,\ |
| 275 | + org.springframework.boot.context.logging.LoggingApplicationListener,\ |
| 276 | + org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener |
| 277 | + */ |
| 278 | + for (ApplicationListener<?> listener : listeners) { |
| 279 | + // 判断是否对该事件感兴趣 |
| 280 | + if (supportsEvent(listener, eventType, sourceType)) { |
| 281 | + if (retriever != null) { |
| 282 | + retriever.applicationListeners.add(listener); |
| 283 | + } |
| 284 | + // 感兴趣就加入到集合当中 |
| 285 | + allListeners.add(listener); |
| 286 | + } |
| 287 | + } |
| 288 | + if (!listenerBeans.isEmpty()) { |
| 289 | + BeanFactory beanFactory = getBeanFactory(); |
| 290 | + for (String listenerBeanName : listenerBeans) { |
| 291 | + try { |
| 292 | + Class<?> listenerType = beanFactory.getType(listenerBeanName); |
| 293 | + if (listenerType == null || supportsEvent(listenerType, eventType)) { |
| 294 | + ApplicationListener<?> listener = |
| 295 | + beanFactory.getBean(listenerBeanName, ApplicationListener.class); |
| 296 | + if (!allListeners.contains(listener) && supportsEvent(listener, eventType, sourceType)) { |
| 297 | + if (retriever != null) { |
| 298 | + if (beanFactory.isSingleton(listenerBeanName)) { |
| 299 | + retriever.applicationListeners.add(listener); |
| 300 | + } |
| 301 | + else { |
| 302 | + retriever.applicationListenerBeans.add(listenerBeanName); |
| 303 | + } |
| 304 | + } |
| 305 | + allListeners.add(listener); |
| 306 | + } |
| 307 | + } |
| 308 | + } |
| 309 | + catch (NoSuchBeanDefinitionException ex) { |
| 310 | + // Singleton listener instance (without backing bean definition) disappeared - |
| 311 | + // probably in the middle of the destruction phase |
| 312 | + } |
| 313 | + } |
| 314 | + } |
| 315 | + // 监听器排序 |
| 316 | + AnnotationAwareOrderComparator.sort(allListeners); |
| 317 | + if (retriever != null && retriever.applicationListenerBeans.isEmpty()) { |
| 318 | + retriever.applicationListeners.clear(); |
| 319 | + retriever.applicationListeners.addAll(allListeners); |
| 320 | + } |
| 321 | + return allListeners; |
| 322 | + } |
| 323 | +``` |
| 324 | + |
| 325 | + |
| 326 | + |
| 327 | +- 通过AbstractApplicationEventMuliticaster#supportEvent() 方法,判断该事件监听器,是否对该事件感兴趣? |
| 328 | + |
| 329 | +```java |
| 330 | + /** |
| 331 | + * Determine whether the given listener supports the given event. |
| 332 | + * <p>The default implementation detects the {@link SmartApplicationListener} |
| 333 | + * and {@link GenericApplicationListener} interfaces. In case of a standard |
| 334 | + * {@link ApplicationListener}, a {@link GenericApplicationListenerAdapter} |
| 335 | + * will be used to introspect the generically declared type of the target listener. |
| 336 | + * @param listener the target listener to check |
| 337 | + * @param eventType the event type to check against |
| 338 | + * @param sourceType the source type to check against |
| 339 | + * @return whether the given listener should be included in the candidates |
| 340 | + * for the given event type |
| 341 | + */ |
| 342 | + protected boolean supportsEvent( |
| 343 | + ApplicationListener<?> listener, ResolvableType eventType, @Nullable Class<?> sourceType) { |
| 344 | + |
| 345 | + // 判断当前事件监听器是否是GenericApplicationListerner接口类的实现类,如果不是的话,就做个委派,方便获取该事件监听器感兴趣事件 |
| 346 | + GenericApplicationListener smartListener = (listener instanceof GenericApplicationListener ? |
| 347 | + (GenericApplicationListener) listener : new GenericApplicationListenerAdapter(listener)); |
| 348 | + // 判断当前监听器是否感兴趣该事件 |
| 349 | + return (smartListener.supportsEventType(eventType) && smartListener.supportsSourceType(sourceType)); |
| 350 | + } |
| 351 | +``` |
| 352 | + |
| 353 | + |
| 354 | + |
| 355 | + |
| 356 | + |
| 357 | +#### 获取监听器列表逻辑 |
| 358 | + |
| 359 | +<img src="https://tva1.sinaimg.cn/large/008i3skNly1gunchq5avkj618e0u0q5l02.jpg" alt="image-20210920200242597" align="left" width="700" /> |
| 360 | + |
| 361 | +- 通过AbstractApplicationEventMulticaster#getApplicationListeners()方法获取感兴趣事件监听器列表 |
| 362 | +- 判断当前SpringBoot缓存中是否存在该事件感兴趣的监听器列表 |
| 363 | +- 如果没有,调用通过AbstractApplicationEventMulicaster#retrieveApplicationListener()方法,获取所有事件监听器列表 |
| 364 | +- 遍历所有事件监听器列表 |
| 365 | +- 通过AbstractApplicationEventMuliticaster#supportsEvent() 方法查找出对该事件感兴趣的事件监听器 |
| 366 | +- 将感兴趣的事件监听器列表加入SpirngBoot缓存中,并返回给事件触发器 |
| 367 | + |
| 368 | + |
| 369 | + |
| 370 | +#### AbstractApplicationEventMuliticaster#supportEvent() 事件监听器匹配器触发条件 |
| 371 | + |
| 372 | +<img src="/Users/huangyan110110114/Library/Application Support/typora-user-images/image-20210920201046825.png" alt="image-20210920201046825" align="left" width="850" /> |
0 commit comments