当前位置: 首页 > 图灵资讯 > 技术篇> 探析Spring容器内部事件发布

探析Spring容器内部事件发布

来源:图灵教育
时间:2023-09-28 10:17:46

自定义模板(2).jpg其实在 JDK 提供相应的自定义事件发布功能的基本类别:

  • java.util.EventObject类 :自定义事件类型
  • java.util.EventListener接口:事件监听器

首先,了解几个概念:image.png

Spring 事件类结构

image.png

1. 事件类

事件类是定义发送的内容,例如可以继承ApplicationContextEvent定义一个特定的事件类别。image.png

1.1 ApplicationEvent

首先是继承 EventObjectApplicationEvent,事件源由source指定:

public abstract class ApplicationEvent extends EventObject {    /**     * Constructs a prototypical Event.     *     * @param source The object on which the Event initially occurred.     * @throws IllegalArgumentException if source is null.     */    public ApplicationEvent(Object source) {        super(source);    }}
1.2 ApplicationContextEvent

它是容器启动、刷新、停止和关闭各种事件的主要子类。

public class ApplicationContextEvent extends ApplicationEvent {    /**     * Constructs a prototypical Event.     *     * @param source The object on which the Event initially occurred.     * @throws IllegalArgumentException if source is null.     */    public ApplicationContextEvent(Object source) {        super(source);    }    /**     * Get the <code>ApplicationContext</code> that the event was raised for.     */    public final ApplicationContext getApplicationContext() {        return (ApplicationContext) getSource();    }}public class ContextClosedEvent extends ApplicationContextEvent{    /**     * Constructs a prototypical Event.     *     * @param source The object on which the Event initially occurred.     * @throws IllegalArgumentException if source is null.     */    public ContextClosedEvent(Object source) {        super(source);    }}public class ContextRefreshedEvent extends ApplicationContextEvent{    /**     * Constructs a prototypical Event.     *     * @param source The object on which the Event initially occurred.     * @throws IllegalArgumentException if source is null.     */    public ContextRefreshedEvent(Object source) {        super(source);    }}

我们可以通过继承这一类来实现特定事件类型的需求,比如实现邮件发送事件。只需要继承ApplicationContextEvent即可:

public class MailSendEvent extends ApplicationContextEvent {    private String msg;    public MailSendEvent(Object source, String msg) {        super(source);        this.msg = msg;    }    public String getMsg() {        return msg;    }    public void setMsg(String msg) {        this.msg = msg;    }}

同时ApplicationContextEvent还有几个特定的子类来表示容器启动、刷新、停止和关闭事件:image.png

2.事件监听器

在事件监听器接口中,只定义了一种方法:onApplicationEvent(E event)该方法接收ApplicationEvent事件对象,在此方法中编写事件响应处理逻辑。

public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {    /**     * 接收Applicationenentention 事件对象     * 在这种方法中编写事件响应处理的逻辑     * @param event     */    void onApplicationEvent(E event);}

我们还可以实现接口,以实现特定的事件监听器功能,如邮件监听器:

public class MailSenderListener implements ApplicationListener<MailSendEvent> {    @Override    public void onApplicationEvent(MailSendEvent event) {        System.out.println("邮件发送器 resource:" + event.getSource() + "邮件发送器 msg:" + event.getMsg());    }}
3.事件广播器

事件广播器负责将事件通知监听器登记表中的事件监听器,然后由事件监听器分别响应事件。Spring定义了以下接口:image.png

public interface ApplicationEventMulticaster {    /**     * 添加事件监听器     * @param listener     */    void addApplicationListener(ApplicationListener<?> listener);    /**     * 移除事件监听器     * @param listener     */    void removeApplicationListener(ApplicationListener<?> listener);    /**     * 广播事件     * @param event     */    void multicastEvent(ApplicationEvent event);}

及其简单实现类SimpleApplicationEventMulticaster

public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster{    public SimpleApplicationEventMulticaster(BeanFactory beanFactory) {        setBeanFactory(beanFactory);    }    /**unchecked 告诉编译器忽略指定的警告,无需编译后出现警告信息*/    @SuppressWarnings("unchecked")    @Override    public void multicastEvent(ApplicationEvent event) {        for (ApplicationListener applicationListener : getApplicationListeners(event)) {            applicationListener.onApplicationEvent(event);        }    }}
4.事件发布者

作为事件源,它将在适当的时间向相应的事件监听器发布相应的事件:

public interface ApplicationEventPublisher {    /**     * 通知监听者并发布事件     * @param event     */    void publishEvent(ApplicationEvent event);}

在Spring容器事件中,ApplicationContext界面定义继承了ApplicationEventPublisher接口,所以实际上AbstractApplicationContext在事件中扮演事件发布者的角色。但事实上,在事件发布和事件监听器注册的具体实现中,将功能转移到ApplicationEventMulticaster接口,最终具体实现放在那里AbstractApplicationEventMulticaster实现类:image.png

Spring 事件类的应用

那么事件类在Spring中是如何工作的呢?首先,我们将在xml配置文件中配置相应的ApplicationListener因此,在容器启动后,这些类型的bean将被使用ApplicationContext对于容器识别,它们负责监控容器内相应的发布ApplicationEvent类型事件。

<bean class="cn.ethan.springframework.test.event.ContextRefreshedEventListener"/><bean class="cn.ethan.springframework.test.event.MailSenderListener"/><bean class="cn.ethan.springframework.test.event.ContextClosedEventListener"/>

AbstractApplicationContextrefresh()自动注册的内容可以在方法中看到:

public void refresh() throws BeansException {        // 6. 初始化事件发布者        initApplicationEventMulticaster();        // 7. 注册事件监听器        registerListeners();        // 9. 发布容器刷新并完成事件        finishRefresh();}private void initApplicationEventMulticaster() {    ConfigurableListableBeanFactory beanFactory = getBeanFactory();    applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);    beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, applicationEventMulticaster);}private void registerListeners() {    Collection<ApplicationListener> applicationListeners = getBeansOfType(ApplicationListener.class).values();    for (ApplicationListener listener : applicationListeners) {        applicationEventMulticaster.addApplicationListener(listener);    }}private void finishRefresh() {    publishEvent(new ContextRefreshedEvent(this));}public void publishEvent(ApplicationEvent event) {    applicationEventMulticaster.multicastEvent(event);}

所以在ApplicationContext当容器启动时,它将自动注册EventListener类型的 Bean,一旦检测到ApplicationContextEvent发布类型的事件将通知这些注册在容器中的事件EventListener

应用实例

以下是发送邮件的Spring事件实例:

1. 邮件发送事件MailSendEvent
public class MailSendEvent extends ApplicationContextEvent {    private String msg;    public MailSendEvent(Object source, String msg) {        super(source);        this.msg = msg;    }    public String getMsg() {        return msg;    }}
2.邮件发送事件监听器MailSendListener(邮件发送事件)、ContextRefreshedEventListener(容器刷新事件) 和 ContextClosedEventListener(容器关闭事件)
public class MailSenderListener implements ApplicationListener<MailSendEvent> {    @Override    public void onApplicationEvent(MailSendEvent event) {        System.out.println("邮件发送器 resource:" + event.getSource() + "邮件发送器 msg:" + event.getMsg());    }}
public class ContextClosedEventListener implements ApplicationListener<ContextClosedEvent> {    @Override    public void onApplicationEvent(ContextClosedEvent event) {        System.out.println("关闭事件:" + this.getClass().getName());    }}
public class ContextRefreshedEventListener implements ApplicationListener<ContextRefreshedEvent> {    @Override    public void onApplicationEvent(ContextRefreshedEvent event) {        System.out.println("刷新/打开事件:" + this.getClass().getName());    }}

此时,将监听器注入xml文件:

<bean class="cn.ethan.springframework.test.event.ContextRefreshedEventListener"/><bean class="cn.ethan.springframework.test.event.MailSenderListener"/><bean class="cn.ethan.springframework.test.event.ContextClosedEventListener"/>
3.邮件发送事件发布者

事件发布者ApplicationEventPublisher,因为前面提到的,applicationContext继承了ApplicationEventPublisher,而applicationContext委托事件发布功能。ApplicationEventMulticaster,启动容器时,检查是否有名称applicationEventMulticasterApplicationEventMulticaster对象实例,如果有实现提供的实现,如果没有,默认初始化SimpleApplicationEventMulticaster作为将要使用的东西ApplicationEventMulticaster

/** * @description: 事件监听器管理功能的实现 * @author: wjw * @date: 2022/7/9 */public abstract class AbstractApplicationEventMulticaster implements ApplicationEventMulticaster, BeanFactoryAware  {    public final Set<ApplicationListener<ApplicationEvent>> applicationListeners = new LinkedHashSet<>();    private BeanFactory beanFactory;    @Override    public void addApplicationListener(ApplicationListener<?> listener) {        applicationListeners.add((ApplicationListener<ApplicationEvent>) listener);    }    @Override    public void removeApplicationListener(ApplicationListener<?> listener) {        applicationListeners.remove(listener);    }    @Override    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {        this.beanFactory = beanFactory;    }    /**     * 获得监听器     * @param event     * @return     */    protected Collection<ApplicationListener> getApplicationListeners(ApplicationEvent event) {        LinkedList<ApplicationListener> allListeners = new LinkedList<>();        for (ApplicationListener<ApplicationEvent> listener : allListeners) {            if (supportsEvent(listener, event)) {                allListeners.add(listener);            }        }        return allListeners;    }    protected boolean supportsEvent(ApplicationListener<ApplicationEvent> applicationListener, ApplicationEvent event) {        Class<? extends ApplicationListener> listenerClass = applicationListener.getClass();        /**根据不同的实例类型,判断后获得相应的目标 class*/        Class<?> targetClass = ClassUtils.isCglibProxyClass(listenerClass) ? listenerClass.getSuperclass() : listenerClass;        Type genericInterface = targetClass.getGenericInterfaces()[0];        Type actualTypeArgument = ((ParameterizedType) genericInterface).getActualTypeArguments()[0];        String className = actualTypeArgument.getTypeName();        Class<?> eventClassName;        try {            eventClassName = Class.forName(className);        } catch (ClassNotFoundException e) {            throw new BeansException("wrong event class name: " + className);        }        return eventClassName.isAssignableFrom(event.getClass());    }}
public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster{    public SimpleApplicationEventMulticaster(BeanFactory beanFactory) {        setBeanFactory(beanFactory);    }    /**unchecked 告诉编译器忽略指定的警告,无需编译后出现警告信息*/    @SuppressWarnings("unchecked")    @Override    public void multicastEvent(ApplicationEvent event) {        for (ApplicationListener applicationListener : getApplicationListeners(event)) {            applicationListener.onApplicationEvent(event);        }    }}
4.测试验证
public void test_event() {    ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:spring.xml");    applicationContext.publishEvent(new CustomEvent(applicationContext, 110L, "test!"));    System.out.println("-----------------------------------------------------------------");    applicationContext.publishEvent(new MailSendEvent(applicationContext, "邮件发送测试"));    applicationContext.registerShutdownHook();}
刷新/打开事件:cn.ethan.springframework.test.event.ContextRefreshedEventListener$$EnhancerByCGLIB$$邮件发送器2e5c458- resource:cn.ethan.springframework.context.support.ClassPathXmlApplicationContext@5f2050f6邮件发送器 msg:邮件发送测试关闭事件:cn.ethan.springframework.test.event.ContextClosedEventListener$$EnhancerByCGLIB$$fbc2c978