Spring监听器源码解读及使用
监听器模式:监听事件的触发,然后做出相应的操作。(事件驱动模式、事件驱动架构)
当系统运行某些关键节点的时候,会通过广播器去发布一些事件,而系统中存在着一些监听器。对某些事件感兴趣,去订阅这些事件。当这些事件被发布出去之后,监听器监听到这些事件,会触发一些行为。
一、Spring中的监听器实现
组件:
事件(ApplicationEvent):即监听什么。如任务即将执行、任务执行完毕
监听器(ApplicationListener):谁来监听,<E extends ApplicationEvent> :E,对什么事件感兴趣(监听特定类型的事件)
广播器(ApplicationEventMulticaster):发布事件、添加/移除监听器
事件触发机制:事件什么时候发布
源码:
1、事件Event
在Spring最顶层的是EventObject类,它代表的是一个事件对象;抽象类ApplicationEvent继承自EventObject,表示的是一个应用事件
EventObject:
public class EventObject implements java.io.Serializable { private static final long serialVersionUID = 5516075349620653480L; protected transient Object source; public EventObject(Object source) { if (source == null) throw new IllegalArgumentException("null source"); this.source = source; } }
ApplicationEvent:
public abstract class ApplicationEvent extends EventObject { /** System time when the event happened. */ private final long timestamp; /** * Create a new ApplicationEvent. * @param source the object on which the event initially occurred (never {@code null}) */ public ApplicationEvent(Object source) { super(source); this.timestamp = System.currentTimeMillis(); } }
2、监听器
ApplicationListener继承自EventListener,EventListener 是一个空接口,用来声明这是一个事件监听的接口。
ApplicationListener:
@FunctionalInterface public interface ApplicationListener<E extends ApplicationEvent> extends EventListener { /** * Handle an application event. * @param event the event to respond to */ void onApplicationEvent(E event); // 监听到事件发生的时候,去做一些事情 }
ApplicationListener的泛型继承自ApplicationEvent,因此在实现这个接口的时候,可以声明自己监听的具体事件。
系统在触发这个系统监听器的时候会根据其监听的事件做一个过滤。
3、广播器
系统广播器是 ApplicationEventMulticaster ,实现这个接口来管理一些应用监听器,并且广播事件。
其中定义了添加、删除监听器以及广播事件的方法。
ApplicationEventMulticaster :
public interface ApplicationEventMulticaster { /** * Add a listener to be notified of all events. * @param listener the listener to add */ void addApplicationListener(ApplicationListener<?> listener); /** * Add a listener bean to be notified of all events. * @param listenerBeanName the name of the listener bean to add */ void addApplicationListenerBean(String listenerBeanName); /** * Remove a listener from the notification list. * @param listener the listener to remove */ void removeApplicationListener(ApplicationListener<?> listener); /** * Remove a listener bean from the notification list. * @param listenerBeanName the name of the listener bean to add */ void removeApplicationListenerBean(String listenerBeanName); /** * Remove all listeners registered with this multicaster. * <p>After a remove call, the multicaster will perform no action * on event notification until new listeners are being registered. */ void removeAllListeners(); /** * Multicast the given application event to appropriate listeners. * <p>Consider using {@link #multicastEvent(ApplicationEvent, ResolvableType)} * if possible as it provides a better support for generics-based events. * @param event the event to multicast */ void multicastEvent(ApplicationEvent event); /** * Multicast the given application event to appropriate listeners. * <p>If the {@code eventType} is {@code null}, a default type is built * based on the {@code event} instance. * @param event the event to multicast * @param eventType the type of event (can be null) * @since 4.2 */ void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType); }
4、ApplicationListener监听器注册到Spring容器
1)启动类:
public static void main(String[] args) { SpringApplication.run(CppApplication.class, args); }
2)调用run方法会先创建SpringApplication对象然后调用其run方法;SpringApplication#run 1258
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) { return new SpringApplication(primarySources).run(args); } // SpringApplication 构造方法 public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) { this.resourceLoader = resourceLoader; Assert.notNull(primarySources, "PrimarySources must not be null"); this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); this.webApplicationType = WebApplicationType.deduceFromClasspath(); setInitializers((Collection) getSpringFactoriesInstances( ApplicationContextInitializer.class)); setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); // 将监听器对应添加到 private List<ApplicationListener<?>> listeners 中 this.mainApplicationClass = deduceMainApplicationClass(); }
getSpringFactoriesInstances 方法获取到所有ApplicationListener的实现,并创建对象,然后排序。
classLoader.getResources("META-INF/spring.factories");
获取classpath路径下,META-INF/spring.factories文件,在里面配置监听器的全路径名
setListeners 方法:
public void setListeners(Collection<? extends ApplicationListener<?>> listeners) { this.listeners = new ArrayList<>(); // 先初始化SpringApplication的 private List<ApplicationListener<?>> listeners; 属性 this.listeners.addAll(listeners); // 将getSpringFactoriesInstances返回的对象集合添加进去 }
5、广播器初始化并添加监听器到广播器中
1)广播器初始化
AbstractApplicationContext#refresh#initApplicationEventMulticaster() 537行
2)添加监听器到广播器中
AbstractApplicationContext#refresh#registerListeners() 543行
6、事件触发机制
SpringApplication#run 295
301行:获取到所有实现 SpringApplicationRunListener 接口的监听器, 创建对象并添加到SpringApplicationRunListeners类的 List<SpringApplicationRunListener> listeners 属性中。
SpringApplicationRunListener接口中定义了Spring容器启动过程中各个阶段的事件,比如starting、environmentPrepared、contextPrepared、contextLoaded、started、running、failed。
所以只用调用不同的方法就可以在相应的节点触发对应的事件。
比如:SpringApplicationRunListeners#statrting() ,遍历所有的SpringApplicationRunListener类型的监听器并调用其 starting() 方法
又比如 SpringApplicationRunListener 接口的实现类 EventPublishingRunListener 的 starting() 实现:
调用广播器的 multicastEvent 方法发送一个相应的 ApplicationStartingEvent 事件。
multicastEvent() 方法的内部实现:
@Override
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
// 获取对当前事件监听的监听器列表,然后遍历
for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
Executor executor = getTaskExecutor(); // 获取线程池,注意:默认是获取不到线程池的,需要手动注入线程池
if (executor != null) {
executor.execute(() -> invokeListener(listener, event));
}
else {
invokeListener(listener, event); // 调用监听器的onApplicationEvent()方法,触发事件
}
}
}
那么,getApplicationListeners() 方法是如何获取对指定事件监听的监听器的?
大致就是判断监听器的泛型的类型,是否与发布事件的类型一致,如果一致将监听器的对象放到一个集合中。
org.springframework.context.event.AbstractApplicationEventMulticaster#retrieveApplicationListeners:
// Add programmatically registered listeners, including ones coming // from ApplicationListenerDetector (singleton beans and inner beans). for (ApplicationListener<?> listener : listeners) { if (supportsEvent(listener, eventType, sourceType)) { if (retriever != null) { filteredListeners.add(listener); // 监听当前事件的监听器 } allListeners.add(listener); } }
具体判断监听器是否监听给定事件是由supportsEvent方法来实现的。接着进入:
org.springframework.context.event.GenericApplicationListenerAdapter#resolveDeclaredEventType,resolveDeclaredEventType是Spring内部实现的一个泛型解析器,会根据类定义获得该类声明的事件类型:
@Nullable private static ResolvableType resolveDeclaredEventType(ApplicationListener<ApplicationEvent> listener) { ResolvableType declaredEventType = resolveDeclaredEventType(listener.getClass()); // 如果listener被aop代理,则获取到的declaredEventType为null,isAssignableFrom:判断参数类型是否与当前类型相同或是当前类型的子类 if (declaredEventType == null || declaredEventType.isAssignableFrom(ApplicationEvent.class)) { // 获取被aop代理的目标类(被代理listener) Class<?> targetClass = AopUtils.getTargetClass(listener); if (targetClass != listener.getClass()) { // 解析listener监听的事件类型 declaredEventType = resolveDeclaredEventType(targetClass); } } return declaredEventType; } @Nullable static ResolvableType resolveDeclaredEventType(Class<?> listenerType) { ResolvableType eventType = eventTypeCache.get(listenerType); if (eventType == null) { // 获取泛型类型 eventType = ResolvableType.forClass(listenerType).as(ApplicationListener.class).getGeneric(); eventTypeCache.put(listenerType, eventType); } return (eventType != ResolvableType.NONE ? eventType : null); }
更多请看Spring ResolvableType博客
二、自定义监听器监听Spring容器的启动
事件列表:
事件名 | 事件类 |
容器开始启动 | ApplicationStartingEvent |
在环境(environment)准备完成,容器创建之前 | ApplicationEnvironmentPreparedEvent |
容器创建完成,并且准备完成,资源加载之前 | ApplicationContextInitializedEvent |
容器加载完成,刷新之前 | ApplicationPreparedEvent |
容器启动完成 | ApplicationStartedEvent |
容器运行 | ApplicationReadyEvent |
容器启动失败 | ApplicationFailedEvent |
示例,容器启动完成之后从数据库加载数据到JVM缓存中:
/** * Spring容器启动完成监听器 */ @Component public class ApplicationContextStartedListener implements ApplicationListener<ApplicationStartedEvent> { @Resource private CategoryMapper categoryMapper; @Override public void onApplicationEvent(ApplicationStartedEvent applicationStartedEvent) { // 缓存分类告警信息 // 查询开启告警的分类 Category category = new Category(); category.setIsAlarm(CommonConstant.STR_ONE); category.setLevel(CommonConstant.CATEGORY_LEVEL_COUNT); List<Category> categoryList = categoryMapper.selectAll(category); if (CollectionUtils.isEmpty(categoryList)) { return; } for (Category c : categoryList) { CommonCache.CATEGOTY_ALARM_CACHE.put(c.getId(), c.getAlarmThreshold()); } } }
三、业务代码中使用Spring监听器
1、自定义事件,继承ApplicationEvent
// 任务执行失败事件 public class TaskExecFailedEvent extends ApplicationEvent { public TaskExecFailedEvent(String source) { super(source); } }
2、定义自定义事件的监听器(注入到Spring容器中)
@Service public class TaskExecFailedListener implements ApplicationListener<TaskExecFailedEvent> { private static final Logger LOGGER = LoggerFactory.getLogger(TaskExecFailedListener.class); @Override public void onApplicationEvent(TaskExecFailedEvent event) { LOGGER.info(event.toString()); } }
3、发布事件
@Autowired private ApplicationEventMulticaster applicationEventMulticaster; applicationEventMulticaster.multicastEvent(new TaskExecFailedEvent("task execute failed!"));
或:
@Autowired private ApplicationContext applicationContext; applicationContext.publishEvent(new TaskExecFailedEvent("task execute failed"));
或,组合ApplicationContext封装事件发布器( 建议采用这种方式):
1)封装的事件发布器(注入到Spring容器中)
@Component public class SpringEventPublisher implements ApplicationContextAware { private static ApplicationContext applicationContext; @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { SpringEventPublisher.applicationContext = applicationContext; } /** * 发布事件 * * @param event */ public static void publishEvent(ApplicationEvent event) { applicationContext.publishEvent((Object) event); } }
2)发布事件
SpringEventPublisher.publishEvent(new TaskExecFailedEvent("task execute failed"));
三、模拟Spring自实现监听器
https://www.cnblogs.com/yangyongjie/p/14337665.html
END.