Spring源码系列(十)——ApplicationEvent事件机制源码分析

Spring中与事件有关的接口和类主要包括ApplicationEventApplicationListener

下面来看一下Spring中事件的具体应用。

一、ApplicationEvent应用

先定义一个EventScorpiosEvent,继承ApplicationEvent

public class ScorpiosEvent extends ApplicationEvent {

	private String msg;

	public ScorpiosEvent(Object source,String message) {
		super(source);
		this.msg = message;
	}

	public String getMsg() {
		return msg;
	}

	public void setMsg(String msg) {
		this.msg = msg;
	}
}

定义两个ListenerScorpiosListenerTestListener。要加上@Component注解,让Spring容器管理。

// ScorpiosListener
@Component
public class ScorpiosListener implements ApplicationListener {

	@Override
	public void onApplicationEvent(ApplicationEvent event) {
		if(event instanceof ScorpiosEvent){
			System.out.println("ScorpiosEvent ..." + ((ScorpiosEvent) event).getMsg());
		}else{
			System.out.println("ScorpiosEvent....dododododododododo");
		}
	}
}
// TestListener
@Component
public class TestListener implements ApplicationListener {

    @Override
    public void onApplicationEvent(ApplicationEvent event) {
        if(event instanceof ScorpiosEvent){
            System.out.println("TestListener ..." + ((ScorpiosEvent) event).getMsg());
        }else{
            System.out.println("TestListener....tttttttttttttttttttttt");
        }
    }

}

入口函数

public static void main( String[] args )
{
    AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext();
    ac.register(AppConfig.class);
    ac.refresh();

    ScorpiosEvent scorpiosEvent = new ScorpiosEvent("scorpios","aaaaaaaaaa");
    ac.publishEvent(scorpiosEvent);
}

在这里插入图片描述

上面就是Event的具体的一个应用,Listener要被Spring容器管理。

从上面的代码调用和日志可以看到,只要调用一个发布事件ac.publishEvent(scorpiosEvent),所有的Listener都会被调用。注意,是所有的Listener,后面源码分析。

从上面的输出日志中可以看出,ScorpiosListenerTestListener监听器都被调用了两次,可代码里,明明就调用了一次啊,为什么System.out.println("ScorpiosEvent....dododododododododo")这行代码也会被输出呢?这是为什么呢?下面就来分析下Spring事件机制的源码吧。

二、ApplicationEvent源码分析

Spring的事件机制采用的是观察者设计模式,对于观察者设计模式,可以看下面这篇文章。

https://blog.csdn.net/zxd1435513775/article/details/88544924

Java设计模式——观察者模式【Observer Pattern】

1. Listener监听器的注册过程

下面首先来了解下Listener监听器是何时被添加到Spring容器中的,Listener被扫描后具体是存放在哪里的?下面先看一下Spring中大名鼎鼎的refresh()方法,如果对这个方法不了解,可以看下我对Spring源码解析的其他文章。

public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
        
        // 准备工作,包括设置启动时间,是否激活标识位,初始化属性源(property source)配置
        prepareRefresh();

        // 返回一个factory 为什么需要返回一个工厂? 因为要对工厂进行初始化
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

        // 准备工厂
        prepareBeanFactory(beanFactory);

        try {
            // 这个方法在当前版本的spring是没用任何代码的,可能spring期待在后面的版本中去扩展吧
            postProcessBeanFactory(beanFactory);

            // 调用BeanFactoryPostProcessors的后置处理器
            // 在Spring环境中去执行已经被注册的FactoryProcessors
            // 设置执行自定义的ProcessorBeanFactory和Spring内部自己定义的
            invokeBeanFactoryPostProcessors(beanFactory);

            //-------------------到此spring工厂完成创建工作--------------------------

            // 注册BeanPostProcessor后置处理器
            registerBeanPostProcessors(beanFactory);

            initMessageSource();

            // 初始化应用事件广播器
            initApplicationEventMulticaster();

            onRefresh();

            // 注册监听器
            registerListeners();

            // 实例化单实例非懒加载的Bean
            finishBeanFactoryInitialization(beanFactory);
            
            // 此处会发布一个事件:ContextRefreshedEvent
            finishRefresh();
        } 
    }
}

主要看下面这四个方法:

  • initApplicationEventMulticaster()
  • registerListeners()
  • finishBeanFactoryInitialization(beanFactory)
  • finishRefresh()

注意:在上面这四个方法执行之前,Spring的组件扫描工作已经结束了,但Bean实例化还没有。

2. initApplicationEventMulticaster()

此方法的作用是初始化应用事件广播器,这个广播器是干嘛的呢?说的直白点,里面存放了所有的Listener

protected void initApplicationEventMulticaster() {
    ConfigurableListableBeanFactory beanFactory = getBeanFactory();
    // 工厂里是否包含 “applicationEventMulticaster” Bean
    if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
        this.applicationEventMulticaster =
            beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);
    } else {
        // 没有的话,Spring自己创建一个SimpleApplicationEventMulticaster
        this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
        // 将自己创建的SimpleApplicationEventMulticaster放到Spring容器中,
        // name 为:applicationEventMulticaster
        beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);
        }
    }
}

此处是Spring自己创建了一个SimpleApplicationEventMulticaster,放入到Spring容器中,看断点图。

在这里插入图片描述

3. registerListeners()

此方法里面是从Spring容器中,拿到实现了ApplicationListener接口的所有BeanName,然后把它们名字添加到广播器中的this.defaultRetriever.applicationListenerBeans

protected void registerListeners() {

    // 注册静态指定的监听器,没有
    for (ApplicationListener<?> listener : getApplicationListeners()) {
        getApplicationEventMulticaster().addApplicationListener(listener);
    }

    // 从Spring容器中拿到所有实现了ApplicationListener接口的类,此处能拿到我们添加的两个Listener
    String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
    for (String listenerBeanName : listenerBeanNames) {
        // 获取上面创建的ApplicationEventMulticaster广播器,把listenerBeanName放到广播器中的
        // this.defaultRetriever.applicationListenerBeans这个集合中,注意此处是放的是listenerBeanName
        getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
    }

    Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents;
    this.earlyApplicationEvents = null;
    if (earlyEventsToProcess != null) {
        for (ApplicationEvent earlyEvent : earlyEventsToProcess) {
            getApplicationEventMulticaster().multicastEvent(earlyEvent);
        }
    }
}

在这里插入图片描述

4. finishBeanFactoryInitialization()

此方法就是实例化单例非懒加载的Bean。这个方法就不具体介绍了,Spring源码分析的其他文章中有详细介绍。这地方给个断点图吧,看看执行到此方法时,Spring容器中有哪些Bean

在这里插入图片描述

从上面的断点图中可以看出,ScorpiosListenerTestListener两个Listener已经被扫描到,但还没有被实例化,所以下面会进行它们的实例化操作。那么这两个Listener是怎么被保存到广播器ApplicationEventMulticaster中的呢?答案是通过ApplicationListenerDetector这个BeanPostProcessor后置处理器。

// ApplicationListenerDetector类
public Object postProcessAfterInitialization(Object bean, String beanName) {
    // 判断当前的Bean是不是实现了ApplicationListener接口
    // 这两个ScorpiosListener、TestListener肯定是的啊
    // 此处的Bean已经实例化好了
    if (bean instanceof ApplicationListener) {
        
        Boolean flag = this.singletonNames.get(beanName);
        if (Boolean.TRUE.equals(flag)) {
            // 将这个实现了ApplicationListener接口的Bean放到广播器的
            // this.defaultRetriever.applicationListeners属性中
            this.applicationContext.addApplicationListener((ApplicationListener<?>) bean);
        } else if (Boolean.FALSE.equals(flag)) {
            this.singletonNames.remove(beanName);
        }
    }
    return bean;
}
// AbstractApplicationContext类中方法
public void addApplicationListener(ApplicationListener<?> listener) {

    if (this.applicationEventMulticaster != null) {
        this.applicationEventMulticaster.addApplicationListener(listener);
    }
    this.applicationListeners.add(listener);
}

// AbstractApplicationEventMulticaster类中方法
public void addApplicationListener(ApplicationListener<?> listener) {
    synchronized (this.retrievalMutex) {
      
        Object singletonTarget = AopProxyUtils.getSingletonTarget(listener);
        if (singletonTarget instanceof ApplicationListener) {
            this.defaultRetriever.applicationListeners.remove(singletonTarget);
        }
        // 将listener放到广播器的属性中了!!!!!
        this.defaultRetriever.applicationListeners.add(listener);
        this.retrieverCache.clear();
    }
}

在这里插入图片描述

上面是Listener实例化和最终保存在哪里的源码分析,下面要看一下,发布事件是怎么触发监听器的调用的呢?

5. finishRefresh()

在此方法中,Spring发布了一个事件:ContextRefreshedEvent

protected void finishRefresh() {

    clearResourceCaches();

    initLifecycleProcessor();

    getLifecycleProcessor().onRefresh();

    // 发布事件,就关注这一个方法!!!!
    publishEvent(new ContextRefreshedEvent(this));

    LiveBeansView.registerApplicationContext(this);
}

下面来看一下这个publishEvent()方法

protected void publishEvent(Object event, @Nullable ResolvableType eventType) {

    ApplicationEvent applicationEvent;
    // 判断这个event是不是ApplicationEvent实例
    if (event instanceof ApplicationEvent) {
        applicationEvent = (ApplicationEvent) event;
    } else {
        applicationEvent = new PayloadApplicationEvent<>(this, event);
        if (eventType == null) {
            eventType = ((PayloadApplicationEvent<?>) applicationEvent).getResolvableType();
        }
    }

    if (this.earlyApplicationEvents != null) {
        this.earlyApplicationEvents.add(applicationEvent);
    } else {
        // 拿到广播器,然后调用广播器的multicastEvent()方法
        getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
    }

    if (this.parent != null) {
        if (this.parent instanceof AbstractApplicationContext) {
            ((AbstractApplicationContext) this.parent).publishEvent(event, eventType);
        } else {
            this.parent.publishEvent(event);
        }
    }
}

AbstractApplicationContext类中的getApplicationEventMulticaster()方法

// 此方法拿到之前创建的广播器applicationEventMulticaster
// 还记得是什么类型的嘛?对,是它:SimpleApplicationEventMulticaster
ApplicationEventMulticaster getApplicationEventMulticaster() throws IllegalStateException {
    if (this.applicationEventMulticaster == null) {
		// 抛异常代码略
    }
    return this.applicationEventMulticaster;
}

SimpleApplicationEventMulticaster广播器中的multicastEvent()方法

public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
    ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
    Executor executor = getTaskExecutor();
    // 此getApplicationListeners(event, type)方法就是拿到广播器里面的所有Listener
    for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
        if (executor != null) {
            executor.execute(() -> invokeListener(listener, event));
        } else {
            // 调用监听器的方法!!!!
            invokeListener(listener, event);
        }
    }
}

protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
    ErrorHandler errorHandler = getErrorHandler();
    if (errorHandler != null) {
        doInvokeListener(listener, event);     
    } else {
        // 参数传入的是listener,终于开始调用了!!!!
        doInvokeListener(listener, event);
    }
}

private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
    try {
        // 调用Listener中仅有的onApplicationEvent()方法!!!
        listener.onApplicationEvent(event);
    } catch (ClassCastException ex) {
     	// 抛异常代码略  
    }
}

终于分析完了。这里是Spring自己发布的一个Event,所以会走到ScorpiosListenerTestListener两个监听器里面的else代码,打印System.out.println("ScorpiosEvent....dododododododododo")System.out.println("TestListener....tttttttttttttttttttttt")这两行代码。

在这里插入图片描述

当执行到ac.publishEvent(scorpiosEvent)这行代码发布Event事件时,广播器又会去调用所有的Listener,所以会有这两行代System.out.println("ScorpiosEvent ..." + ((ScorpiosEvent) event).getMsg())码的输出!!!

在这里插入图片描述

三、 小结

Spring中事件机制使用的是观察者设计模式,其中对应观察者的四个角色分别为:

  • 事件EventApplicationEvent是所有事件对象的父类。ApplicationEvent继承自 JDK中的 EventObject,所有的事件都需要继承 ApplicationEvent,并且通过 source得到事件源

    Spring也为我们提供了很多内置事件,ContextRefreshedEventContextStartedEventContextStoppedEventContextClosedEventRequestHandledEvent

  • 事件监听器ApplicationListener,也就是观察者,继承自 JDKEventListener,该类中只有一个方法 onApplicationEvent(),当监听的事件发生后该方法会被执行

  • 事件源ApplicationContextApplicationContextSpring中的核心容器,在事件监听中 ApplicationContext可以作为事件的发布者,也就是事件源。因为 ApplicationContext继承自 ApplicationEventPublisher。在 ApplicationEventPublisher中定义了事件发布的方法:publishEvent(Object event)

  • 事件管理ApplicationEventMulticaster,用于事件监听器的注册和事件的广播。监听器的注册就是通过它来实现的,它的作用是把 Applicationcontext发布的 Event广播给它的监听器列表。 因为它里面手握所有监听器。

Spring中的事件模型是一种简单的、粗粒度的监听模型,当有一个事件到达时,所有的监听器都会接收到,并且作出响应,如果希望只针对某些类型进行监听,需要在代码中进行控制。

ApplicationContext接收到事件后,事件的广播是Spring内部给我们做的,其实在Spring读包扫描之后,将所有实现ApplicationListenerBean找出来,注册为容器的事件监听器。当接收到事件的 时候,Spring会逐个调用事件监听器。

  • 4
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

止步前行

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值