Spring中事件发布机制及流程详解

 更新时间:2023年11月09日 08:43:48   作者:爱敲代码的小楚  
这篇文章主要介绍了Spring中事件发布机制及流程详解,在分析源码的过程中,也是大量使用了事件机制,在我分析的这篇博客中,有不少地方都运用了事件发布机制,所以本文的目的是从SpringBoot中学习到事件的发布流程,需要的朋友可以参考下

一、角色

在这里插入图片描述

事件类

ApplicationEvent:定义事件类型
|------ApplicationContextEvent:ApplicationContext引发的事件的基类。
        |------ContextClosedEvent:容器关闭事件
        |------ContextRefreshedEvent:容器刷新事件

事件发布者

ApplicationEventPublisher:将应用程序事件通知此应用程序注册的所有侦听器。

事件监听者

ApplicationListener:由应用程序事件侦听器实现的接口。

事件广播器

ApplicationEventMulticaster:
|------AbstractApplicationEventMulticaster:提供了基本的侦听器注册功能
        |------SimpleApplicationEventMulticaster:实现事件通知具体方式

二、角色负责的功能

事件广播器

ApplicationEventMulticaster接口

定义规范:

  • 把监听者加入集合
  • 把监听者移出集合
  • 事件通知
	// 把事件事件监听者加入集合
 	void addApplicationListener(ApplicationListener<?> listener);
 	// 把事件事件监听者加入集合
    void removeApplicationListener(ApplicationListener<?> listener);
    /**
     *  最终推送时间消息也会经过这个接口方法来处理谁该接收事件
     *
     * @param event
     */
    void multicastEvent(ApplicationEvent event);

AbstractApplicationEventMulticaster抽象类 实现了:

  • 创建集合用于存储事件监听者(Listener)
  • 实现将事件监听者(Listener)添加和移出集合的方法
  • 实现将符合事件的监听者添加入新建的集合并返回
  • 拿出集合中所有的监听者,根据要发生的事件判断,该监听者是否感兴趣
public abstract class AbstractApplicationEventMulticaster implements ApplicationEventMulticaster, BeanFactoryAware {
	// 存放所有ApplicationListener的集合
 public final LinkedHashSet<ApplicationListener<ApplicationEvent>>
            applicationListeners =  new LinkedHashSet();

	/**
     * 方法主要是摘取符合广播事件中的监听处理器,具体过滤动作在 supportsEvent 方法中。
     * @param event
     * @return
     */
 	protected Collection<ApplicationListener> getApplicationListeners(ApplicationEvent event){
        LinkedList<ApplicationListener> allListeners = new LinkedList<>();
        for (ApplicationListener<ApplicationEvent> listener : applicationListeners){
            if(supportsEvent(listener, event)) allListeners.add(listener);
        }
        return allListeners;
    }
    /**
     * 监听器是否对该事件感兴趣
     * 主要包括对 Cglib、Simple 不同实例化需要获取目标
     * Class,Cglib 代理类需要获取父类的 Class,普通实例化的不需要。接下来就是通过
     * 提取接口和对应的 ParameterizedType 和 eventClassName,方便最后确认是否为
     * 子类和父类的关系,以此证明此事件归这个符合的类处理。
     * @param applicationListener
     * @param event
     * @return
     */
    private boolean supportsEvent(ApplicationListener<ApplicationEvent> applicationListener, ApplicationEvent event) {

        Class<? extends ApplicationListener> listenerClass = applicationListener.getClass();
        // 按照 CglibSubclassingInstantiationStrategy、
        // SimpleInstantiationStrategy 不同的实例化类型,需要判断后获取目标 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);
        }
        // 判定此 eventClassName 对象所表示的类或接口与指定的 event.getClass() 参数所
        // 示的类或接口是否相同,或是否是其超类或超接口。
        // isAssignableFrom 是用来判断子类和父类的关系的,或者接口的实现类和接口的关系的,
        // 默认所有的类的终极父类都是 Object。如果 A.isAssignableFrom(B)结果是 true,证明 B 可以转换成
        // 为 A,也就是 A 可以由 B 转换而来。
        return eventClassName.isAssignableFrom(event.getClass());
    }
	......
}

SimpleApplicationEventMulticaster类 实现了:

  • 实现了事件通知(具体细节由父类完成)
public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster {
	@Override
    public void multicastEvent(ApplicationEvent event) {
        for(final ApplicationListener listener : getApplicationListeners(event)){
            listener.onApplicationEvent(event);
        }
    }
    .......
}

事件发布者

因为这边事件都是ApplicationContextEvent,Application是对Spring应用上下的管理。所以这边充当事件发布者的是AbstractApplicationContext。 AbstractApplicationContext 间接实现ApplicationEventPublisher

public abstract class AbstractApplicationContext extends DefaultResourceLoader implements ConfigurableApplicationContext {
	private ApplicationEventMulticaster applicationEventMulticaster;
	@Override
    public void refresh() throws BeansException {
    	.....
    	// 一开始就创建了事件广播器
    	// 6. 初始化事件发布者
        initApplicationEventMulticaster();
	    .......
	    // 9. 发布容器刷新完成事件(发布是容器刷新事件)
        finishRefresh();
    }
    // 创建事件广播器
    private void initApplicationEventMulticaster() {
        ConfigurableListableBeanFactory beanFactory = getBeanFactory();
        this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
        beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);
    }
    private void finishRefresh() {
        publishEvent(new ContextRefreshedEvent(this));
    }

    @Override
    public void publishEvent(ApplicationEvent event) {
        applicationEventMulticaster.multicastEvent(event);
    }
	.......
}

事件监听者

当我们关心spring容器什么时候刷新,或者想在spring容器刷新的时候做一些事情。 监听关心的事件,主要就是在ApplicationListener中写对应的事件。 spring容器在刷新完容器,就会调用该方法。

public class ContextRefreshedEventListener implements ApplicationListener<ContextRefreshedEvent> {
    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        System.out.println("刷新事件(容器刷新完成):" + this.getClass().getName());
    }
}

三、使用spring中的事件机制

1.写一个事件类(暂定我们关心的是application上下文的事件)

public class CustomEvent extends ApplicationContextEvent {

    private Long id;
    private String message;

    /**
     * Constructs a prototypical Event.
     *
     * @param source The object on which the Event initially occurred.
     * @throws IllegalArgumentException if source is null.
     */
    public CustomEvent(Object source,Long id, String message) {
        super(source);
        this.id = id;
        this.message = message;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }
}

2.事件发布者

@Test
    public void test_event(){
        ClassPathXmlApplicationContext applicationContext =
                new ClassPathXmlApplicationContext("classpath:spring.xml");

        applicationContext.publishEvent(new CustomEvent(applicationContext, 101912455552221L, "事件发布成功!"));
        //applicationContext.registerShutdownHook();
    }

3.写对应的监听者实现就可以了

public class CustomEventListener implements ApplicationListener<CustomEvent> {
    @Override
    public void onApplicationEvent(CustomEvent event) {
        System.out.println("收到:" + event.getSource() + "消息;时间:" + new Date());
        System.out.println("消息:" + event.getId() + ":" + event.getMessage());
    }
}

四、整体流程

在这里插入图片描述

到此这篇关于Spring中事件发布机制及流程详解的文章就介绍到这了,更多相关Spring事件发布机制内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • java并发编程中实现可见性的四种可行方案解析

    java并发编程中实现可见性的四种可行方案解析

    这篇文章主要介绍了java并发编程中实现可见性的四种可行方案解析,使用关键字volatile和使用锁(如synchronized关键字或者java.util.concurrent包中的锁)来确保对共享变量的修改在多线程环境中能够正确地被其他线程所观察到,需要的朋友可以参考下
    2023-08-08
  • springboot数据访问和数据视图的使用方式详解

    springboot数据访问和数据视图的使用方式详解

    这篇文章主要为大家介绍了springboot数据访问和数据视图的使用方式详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-06-06
  • Java将对象保存到文件中/从文件中读取对象的方法

    Java将对象保存到文件中/从文件中读取对象的方法

    下面小编就为大家带来一篇Java将对象保存到文件中/从文件中读取对象的方法。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2016-12-12
  • Spring加载加密的配置文件详解

    Spring加载加密的配置文件详解

    这篇文章主要为大家详细介绍了Spring加载加密的配置文件,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-07-07
  • 基于IDEA2018卡死不动的解决方式(好用)

    基于IDEA2018卡死不动的解决方式(好用)

    这篇文章主要介绍了基于IDEA2018卡死不动的解决方式,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2021-02-02
  • java实现动态代理方法浅析

    java实现动态代理方法浅析

    这篇文章主要介绍了java实现动态代理方法浅析,很实用的功能,需要的朋友可以参考下
    2014-08-08
  • Java的编译时错误和运行时错误问题

    Java的编译时错误和运行时错误问题

    这篇文章主要介绍了Java的编译时错误和运行时错误问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-04-04
  • 使用Java生成JWT(JSON Web Token)的方法示例

    使用Java生成JWT(JSON Web Token)的方法示例

    在现代应用程序中,身份验证和授权是至关重要的,JWT是一种简单而强大的身份验证和授权机制,可以在Web应用程序中安全地传输用户信息,本文主要介绍了使用Java生成JWT的方法示例,感兴趣的可以了解一下
    2024-03-03
  • 解决使用@ResponseBody后返回500错误的问题

    解决使用@ResponseBody后返回500错误的问题

    这篇文章主要介绍了解决使用@ResponseBody后返回500错误的问题,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-09-09
  • SpringMVC @RequestBody的使用解析

    SpringMVC @RequestBody的使用解析

    这篇文章主要介绍了SpringMVC @RequestBody的使用解析,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-11-11

最新评论