Spring源码-监听事件ApplicationListener和ApplicationEvent源码分析

扫码关注公众号:Java 技术驿站

发送:vip
将链接复制到本浏览器,永久解锁本站全部文章

【公众号:Java 技术驿站】 【加作者微信交流技术,拉技术群】
免费领取10G资料包与项目实战视频资料

Spring源码-监听事件ApplicationListener和ApplicationEvent源码分析

Spring中ApplicationListener和ApplicationEvent是典型的事件驱动模型,也就是我们常说的发布-订阅模型 。其实我们在开发中是经常用到这种发布-订阅模型模型的,发布订阅模型一般用在一对多的对象关系上,比如如下案例中,我们就能用到这种发布-订阅模型。

案例:在用户注册成功后,往往还需要做其他事。

1、加积分

2、发确认邮件

3、如果是游戏帐户,可能赠送游戏大礼包

4、索引用户数据

在如上案例中,如果我们业务量不大的时候,其实可以直接用到Spring的发布-订阅模式就能解决,分别注册四个监听器,分别监听四个步骤,每一个监听器去独立的做一件事;使用这种模式还能将代码解耦,还能结合多线程的优势来提供性能。

1. 首先,我们来初步使用以下Spring 的发布-订阅模式,如下是测试代码。

先在配置文件中配置MyListen和MyListen2二个监听器的Bean

    <bean class="cn.edu.his.pay.listen.MyListen" />
    <bean class="cn.edu.his.pay.listen.MyListen2" />

MyListen.java

    package cn.edu.his.pay.listen;

    import org.springframework.context.ApplicationEvent;
    import org.springframework.context.ApplicationListener;

    public class MyListen implements ApplicationListener {

        public void onApplicationEvent(ApplicationEvent arg0) {

            if(arg0 instanceof MyEvent) {
                MyEvent event = (MyEvent)arg0;

                System.out.println(this.getClass().getName() + event.getParam1());
                System.out.println(this.getClass().getName() + event.getParam2());
                System.out.println(this.getClass().getName() + event.getSource());

            }
        }

    }

MyListen2.java

    package cn.edu.his.pay.listen;

    import org.springframework.context.ApplicationEvent;
    import org.springframework.context.ApplicationListener;

    public class MyListen2 implements ApplicationListener {

        public void onApplicationEvent(ApplicationEvent arg0) {

            if(arg0 instanceof MyEvent) {
                MyEvent event = (MyEvent)arg0;

                System.out.println(this.getClass().getName() + event.getParam1());
                System.out.println(this.getClass().getName() + event.getParam2());
                System.out.println(this.getClass().getName() + event.getSource());

            }
        }

    }

MyEvent.java

    package cn.edu.his.pay.listen;

    import org.springframework.context.ApplicationEvent;

    public class MyEvent extends ApplicationEvent {

        public String param1;

        public String param2;

        public MyEvent(Object source,String param1,String param2) {
            super(source);
            this.param1 = param1;
            this.param2 = param2;
        }

        public Object getSource() {
            return super.getSource();
        }

        public String getParam1() {
            return param1;
        }

        public void setParam1(String param1) {
            this.param1 = param1;
        }

        public String getParam2() {
            return param2;
        }

        public void setParam2(String param2) {
            this.param2 = param2;
        }

    }

Test.java

    @Test
    public void test() {
        // 往上下文context中发布事件
        MyEvent event = new MyEvent("source","param1","param2");
        context.publishEvent(event);
    }

输出结果

    cn.edu.his.pay.listen.MyListenparam1
    cn.edu.his.pay.listen.MyListenparam2
    cn.edu.his.pay.listen.MyListensource
    cn.edu.his.pay.listen.MyListen2param1
    cn.edu.his.pay.listen.MyListen2param2
    cn.edu.his.pay.listen.MyListen2source

如上测试代码结果可以看出,通过自定义了一个MyEvent(主题)并发布到上下文中,这个时候只要有监听器订阅(观察者)就能拿到主题信息去完成自己的业务。

2. 然后我们就需要思考,Spring是怎么做到的发布订阅了?现在我们就结合Spring源码来分析。

先看看Spring在初始化的时候都做了什么? 入口:AbstractApplicationContext#registerListeners。

    protected void registerListeners() {
        // Register statically specified listeners first.
        for (ApplicationListener<?> listener : getApplicationListeners()) {
            getApplicationEventMulticaster().addApplicationListener(listener);
        }
        // Do not initialize FactoryBeans here: We need to leave all regular beans
        // uninitialized to let post-processors apply to them!
        String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
        for (String lisName : listenerBeanNames) {
            getApplicationEventMulticaster().addApplicationListenerBean(lisName);
        }
    }

通过上面代码可以看出,在容器初始化的时候会将所有实现了ApplicationListener接口类beanName都先注册applicationListenerBeans集合中,相当于注册了所有的监听器。

其次,我们再看看发布主题的时候context.publishEvent(event) 都做了什么?入口:AbstractApplicationContext#publishEvent(ApplicationEvent event)。

    @Override
    public void publishEvent(ApplicationEvent event) {
        Assert.notNull(event, "Event must not be null");
        if (logger.isTraceEnabled()) {
            logger.trace("Publishing event in " + getDisplayName() + ": " + event);
        }
        getApplicationEventMulticaster().multicastEvent(event);
        if (this.parent != null) {
            this.parent.publishEvent(event);
        }
    }
    @Override
    @SuppressWarnings({ "unchecked", "rawtypes" })
    public void multicastEvent(final ApplicationEvent event) {
        for (final ApplicationListener listener : getApplicationListeners(event)) {
            Executor executor = getTaskExecutor();
            if (executor != null) {
                executor.execute(new Runnable() {
                    @Override
                    public void run() {
                        listener.onApplicationEvent(event);
                    }
                });
            }
            else {
                listener.onApplicationEvent(event);
            }
        }
    }

通过如上代码其实可以看出,在发布事件(主题)的时候,先通过getApplicationListeners(event)获取了所有监听器(观察者),其实就是从之前Spring启动时候注册到applicationListenerBeans集合中取就行。取出所有的监听器(观察者),循环遍历去调用监听器(观察者)onApplicationEvent方法。


来源:[]()

赞(0) 打赏
版权归原创作者所有,任何形式的转载请联系博主:daming_90:Java 技术驿站 » Spring源码-监听事件ApplicationListener和ApplicationEvent源码分析

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏