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.

posted @ 2020-09-29 16:51  杨岂  阅读(829)  评论(1编辑  收藏  举报