事件及監聽並非SpringBoot的新功能,Spring框架早已提供了完善的事件監聽機制,在Spring框架中實現事件監聽的流程以下:java
自定義事件,繼承org.springframework.context.ApplicationEvent抽象類spring
定義事件監聽器,實現org.springframework.context.ApplicationListener接口編程
在Spring容器中發佈事件springboot
1 //自定義事件 2 public class ApplicationEventTest extends ApplicationEvent { 3 4 public ApplicationEventTest(Object source) { 5 super(source); 6 } 7 8 /** 9 * 事件處理事項 10 * @param msg 11 */ 12 public void printMsg(String msg) 13 { 14 System.out.println("監聽到事件:"+ApplicationEventTest.class); 15 } 16 }
1 //自定義事件監聽器 2 //@Component 3 public class ApplicationListenerTest implements ApplicationListener<ApplicationEventTest> { 4 5 @Override 6 public void onApplicationEvent(ApplicationEventTest event) { 7 8 event.printMsg(null); 9 } 10 }
1 public static void main(String[] args) { 2 3 SpringApplication application = new SpringApplication(SpringbootdemoApplication.class); 4 //須要把監聽器加入到spring容器中 5 application.addListeners(new ApplicationListenerTest()); 6 Set<ApplicationListener<?>> listeners = application.getListeners(); 7 ConfigurableApplicationContext context = application.run(args); 8 //發佈事件 9 context.publishEvent(new ApplicationEventTest(new Object())); 10 11 context.close(); 12 }
上面的示例是在SpringBoot應用中簡單的測試一下。app
實際開發中實現監聽還有其餘的方式,在Spring框架中提供了兩種事件監聽的方式:框架
編程式:經過實現ApplicationListener接口來監聽指定類型的事件ide
註解式:經過在方法上加@EventListener註解的方式監聽指定參數類型的事件,寫該類須要託管到Spring容器中測試
在SpringBoot應用中還能夠經過配置的方式實現監聽:this
3. 經過application.properties中配置context.listener.classes屬性指定監聽器lua
下面分別分析一下這三種監聽方式
實現ApplicationListenser接口:
1 @Component 2 public class ApplicationListenerTest implements ApplicationListener<ApplicationEventTest> { 3 4 @Override 5 public void onApplicationEvent(ApplicationEventTest event) { 6 7 event.printMsg(null); 8 } 9 }
控制檯輸出測試:
1 public static void main(String[] args) { 2 3 SpringApplication application = new SpringApplication(SpringbootdemoApplication.class); 4 //須要把監聽器加入到spring容器中 5 //application.addListeners(new ApplicationListenerTest()); 6 //Set<ApplicationListener<?>> listeners = application.getListeners(); 7 8 ConfigurableApplicationContext context = application.run(args); 9 //發佈事件 10 context.publishEvent(new ApplicationEventTest(new Object())); 11 }
那麼咱們跟蹤一下源碼,看一下事件是如何發佈出去的,又是如何被監聽到的呢?
AbstractApplicationContext.java中截取部分代碼
1 protected void publishEvent(Object event, @Nullable ResolvableType eventType) { 2 Assert.notNull(event, "Event must not be null"); 3 if (logger.isTraceEnabled()) { 4 logger.trace("Publishing event in " + getDisplayName() + ": " + event); 5 } 6 7 // Decorate event as an ApplicationEvent if necessary 8 /將object轉成ApplicationEvent 9 ApplicationEvent applicationEvent; 10 if (event instanceof ApplicationEvent) { 11 applicationEvent = (ApplicationEvent) event; 12 } 13 else { 14 applicationEvent = new PayloadApplicationEvent<>(this, event); 15 if (eventType == null) { 16 eventType = ((PayloadApplicationEvent) applicationEvent).getResolvableType(); 17 } 18 } 19 20 // Multicast right now if possible - or lazily once the multicaster is initialized 22 if (this.earlyApplicationEvents != null) { 23 this.earlyApplicationEvents.add(applicationEvent); 24 } 25 else { 26 // SimpleApplicationEventMulticaster 獲取事件發佈器,發佈事件 27 getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType); 28 } 29 30 // Publish event via parent context as well... 31 if (this.parent != null) { 32 if (this.parent instanceof AbstractApplicationContext) { 33 ((AbstractApplicationContext) this.parent).publishEvent(event, eventType); 34 } 35 else { 36 this.parent.publishEvent(event); 37 } 38 } 39 }
查看一下ApplicationContext類結構圖能夠發現:應用上下文AbstractApplicationContext實際仍是經過繼承ApplicationEventPublisher接口,實現了其中的事件發佈的方法,使得Spring應用上下文有了發佈事件的功能,在AbstractApplicationContext內部經過SimpleApplicationEventMulticaster事件發佈類,將具體事件ApplicationEvent發佈出去。
那麼事件發佈出去後又是如何被監聽到的呢?下面看一下具Spring中負責處理事件發佈類SimpleApplicationEventMulticaster 中multicastEvent方法具體實現過程
SimpleApplicationEventMulticaster.java部分代碼,實際嘗試將當前事件逐個廣播到指定類型的監聽器中(listeners已經根據當前事件類型過濾了)
1 @Override 2 public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) { 3 ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event)); 4 // getApplicationListeners(event, type) 篩選監聽器,在context.publish(ApplicationEvent event)中已經將事件傳入,getApplicationListeners中將能夠根據這個event類型從Spring容器中檢索出符合條件的監聽器 5 6 for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) { 7 Executor executor = getTaskExecutor(); 8 if (executor != null) { 9 executor.execute(() -> invokeListener(listener, event)); 10 } 11 else { 12 //嘗試逐個向監聽器廣播 13 invokeListener(listener, event); 14 } 15 } 16 }
定義註解方法
@Component public class MyEventHandleTest { /** * 參數爲Object類型時,全部事件都會監聽到 * 參數爲指定類型事件時,該參數類型事件或者其子事件(子類)均可以接收到 */ @EventListener public void event(ApplicationEventTest event){ event.printMsg(null); } }
實現過程分析:
@EventListener註解主要經過EventListenerMethodProcessor掃描出全部帶有@EventListener註解的方法,而後動態構造事件監聽器,並將監聽器託管到Spring應用上文中。
1 protected void processBean( 2 final List<EventListenerFactory> factories, final String beanName, final Class<?> targetType) { 3 4 if (!this.nonAnnotatedClasses.contains(targetType)) { 5 Map<Method, EventListener> annotatedMethods = null; 6 try { 7 //查找含有@EventListener註解的全部方法 8 annotatedMethods = MethodIntrospector.selectMethods(targetType, 9 (MethodIntrospector.MetadataLookup<EventListener>) method -> 10 AnnotatedElementUtils.findMergedAnnotation(method, EventListener.class)); 11 } 12 catch (Throwable ex) { 13 // An unresolvable type in a method signature, probably from a lazy bean - let's ignore it. 14 if (logger.isDebugEnabled()) { 15 logger.debug("Could not resolve methods for bean with name '" + beanName + "'", ex); 16 } 17 } 18 if (CollectionUtils.isEmpty(annotatedMethods)) { 19 this.nonAnnotatedClasses.add(targetType); 20 if (logger.isTraceEnabled()) { 21 logger.trace("No @EventListener annotations found on bean class: " + targetType.getName()); 22 } 23 } 24 else { 25 // Non-empty set of methods 26 ConfigurableApplicationContext context = getApplicationContext(); 27 //遍歷含有@EventListener註解的方法 28 for (Method method : annotatedMethods.keySet()) { 29 for (EventListenerFactory factory : factories) { 30 if (factory.supportsMethod(method)) { 31 Method methodToUse = AopUtils.selectInvocableMethod(method, context.getType(beanName)); 32 //動態構造相對應的事件監聽器 33 ApplicationListener<?> applicationListener = 34 factory.createApplicationListener(beanName, targetType, methodToUse); 35 if (applicationListener instanceof ApplicationListenerMethodAdapter) { 36 ((ApplicationListenerMethodAdapter) applicationListener).init(context, this.evaluator); 37 } 38 //將監聽器添加的Spring應用上下文中託管 39 context.addApplicationListener(applicationListener); 40 break; 41 } 42 } 43 } 44 if (logger.isDebugEnabled()) { 45 logger.debug(annotatedMethods.size() + " @EventListener methods processed on bean '" + 46 beanName + "': " + annotatedMethods); 47 } 48 } 49 } 50 }
添加以下配置:
context.listener.classes=com.sl.springbootdemo.Listeners.ApplicationListenerTest
查看一下DelegatingApplicationListener類中實現邏輯:
1 public class DelegatingApplicationListener 2 implements ApplicationListener<ApplicationEvent>, Ordered { 3 4 private static final String PROPERTY_NAME = "context.listener.classes"; 5 6 private int order = 0; 7 //Spring framework提供的負責處理髮布事件的類,前面說的Spring應用上下文中也是經過這個類發佈事件的 8 private SimpleApplicationEventMulticaster multicaster; 9 10 @Override 11 public void onApplicationEvent(ApplicationEvent event) { 12 if (event instanceof ApplicationEnvironmentPreparedEvent) { 13 // getListeners內部實現讀取context.listener.classes配置的監聽器 14 List<ApplicationListener<ApplicationEvent>> delegates = getListeners( 15 ((ApplicationEnvironmentPreparedEvent) event).getEnvironment()); 16 if (delegates.isEmpty()) { 17 return; 18 } 19 this.multicaster = new SimpleApplicationEventMulticaster(); 20 for (ApplicationListener<ApplicationEvent> listener : delegates) { 21 this.multicaster.addApplicationListener(listener); 22 } 23 } 24 //發佈事件 25 if (this.multicaster != null) { 26 this.multicaster.multicastEvent(event); 27 } 28 }
Spring-boot-{version}.jar包中提供一個類DelegatingApplicationListener,該類的做用是從application.properties中讀取配置context.listener.classes,並將事件廣播給這些配置的監聽器。經過前面一章對SpringBoot啓動流程分析,咱們已經瞭解到SpringBoot啓動時會從META-INF/spring.factories中讀取key爲org.springframework.context.ApplicationListener的全部監聽器。DelegatingApplicationListener的功能能夠讓咱們不須要建立META-INF/spring.factories,直接在application.properties中配置便可。