ApplicationEvent和ApplicationListener源码解析

1.何为观察者设计模式

在了解源码之前,先问下自己懂了观察者设计模式没,以及为什么要用观察者设计模式。

所谓观察者设计模式,可以这么类比,某个班在教室里自习,A在看小黄书,B在玩GBA,C在睡觉。这时候班主任突然出现在教室的窗户边暗中观察自习情况。

A,B,C感觉有一道灼热的目光看着自己,抬起头来发现了老师,马上A放下了手中的小黄书拿出了数学作业开始做,B放下了手中的GBA开始看英语书,C擦掉了自己的口水望着桌子发呆。老师见无异状,就安心走开了。

同学们,这就是标准的观察者模式啊,老师作为被观察者,同学们是观察者,同学们发现了老师的出现,马上开始做应该做的事情,比如看书、写作业等。老师有一个个地去跟学生说,A你要做数学作业,B你要看英语书吗?并没有,学生只要是在自习就OK了,老师并不关心你在学习哪门课程,作为老师只用出现在学生的视野里,让学生看到他,学生做学习相关的事情即可。所以不管这个班到底是A,B,C在玩,还是D,E,F在玩,都无所谓,只要你在这个教室里,你就得看老师的脸色行事。

再来梳理下上面的过程,我们需要老师(被观察者),学生(观察者),教室(观察者集合)这几个要素,老师出现在窗口触发了事件(publish event),在教室里的所有学生(观察者)做出各自对应的响应(看书、写作业)。

对应到代码,可以如下演示

创建Student接口,A,B两位同学继承该接口

public interface Student {
    void doSomething(String name);
}
public class StudentA implements Student {
    @Override
    public void doSomething(String name) {
        System.out.println(name+"老师出现了,学生A开始做数学作业");
    }
}
public class StudentB implements Student {
    @Override
    public void doSomething(String name) {
        System.out.println(name+"老师出现了,学生B开始看英语书");
    }
}

创建老师类,老师有名字和班级两个属性,和触发事件。班级管理着所有学生,老师出现在窗口前,班级内所有学生开始做跟学习相关的事情。

public class Teacher {

    List<Student> classRoom = new ArrayList<>();

    String name;

    public void addStu(Student stu){
        classRoom.add(stu);
    }

    public void removeStu(Student stu){
        classRoom.remove(stu);
    }


    // 触发事件
    public void standByWindow(){
        classRoom.forEach(s ->{
            s.doSomething(name);
        });
    }

}
public class Test {
    public static void main(String[] args) {
        Teacher t = new Teacher();
        t.name = "苍井空";

        Student A = new StudentA();
        Student B = new StudentB();

        t.addStu(A);
        t.addStu(B);

        t.standByWindow();
    }
}

执行结果:

结论:观察者模式,同一个教室里的所有学生都在看老师有没有出现在窗口,一旦发现老师出现在窗口,大家就装模作样的学习下。老师也省心,不用管这个教室里有几个学生或者学生叫什么名字,只要露个脸,整个班级就安静自习了。

那么现在Spring自己有一套观察者模式,我们就不需要重新定义Student接口和classroom集合了,直接调用Spring的一套就好了。也就是接下来要讲的ApplicationListener和ApplicationEvent。

2.ApplicationEvent和ApplicationListener介绍

Event+Listenr是观察者observer设计模式的一种体现。

首先看ApplicationListener 监听器的源码,ApplicationListener根据不同的ApplicationEvent事件,做出相应的响应。

ApplicationListener继承EventListener,java中所有的监听者都继承EventListener,如jwt里的ActionListener。

public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {

	// 实现监听:ApplicationEvent类型事件触发时 do something
	void onApplicationEvent(E event);

}

有两种方式添加监听器,一个是原始的继承ApplicationListener且重写onApplicationEvent方法

@Component
public class RegisterListener implements ApplicationListener<UserRegisterEvent>
{
    /**
     * 实现监听
     * @param userRegisterEvent
     */
    @Override
    public void onApplicationEvent(UserRegisterEvent userRegisterEvent) {
        //获取注册用户对象
        UserBean user = userRegisterEvent.getUser();

        //../省略逻辑

        //输出注册用户信息
        System.out.println("注册信息,用户名:"+user.getName()+",密码:"+user.getPassword());

    }
}

二是用@EventListener注解的方式。

@Component
public class AnnotationRegisterListener {

    /**
     * 注册监听实现方法
     * @param userRegisterEvent 用户注册事件
     */
    @EventListener
    public void register(UserRegisterEvent userRegisterEvent)
    {
        //获取注册用户对象
        UserBean user = userRegisterEvent.getUser();

        //../省略逻辑

        //输出注册用户信息
        System.out.println("@EventListener注册信息,用户名:"+user.getName()+",密码:"+user.getPassword());
    }
}

 

ApplicationEvent代表某个事件对象,包含属性Object source ——发生事件的对象(没感觉有啥屌用,随便传个非空的值就行了,一般传this)

public abstract class ApplicationEvent extends EventObject {

	/**
	 * 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();
	}	

}

现在有了监听器ApplicationListener和事件ApplicationEvent,由谁来触发呢?答案是

ApplicationContext中的publishEvent(ApplicationEvent event)方法。

@Autowired

Applicationcontext applicationContext;

// UserApplicationEvent继承抽象类ApplicationEvent

applicationContext.publishEvent(new UserApplicationEvent(this));

ApplicationContext管理着所有的listener,触发ApplicationEvent事件时,ApplicationContext会从所有listener中找出符合条件的监听器,然后触发其中的onApplicationEvent(e)方法。这样就完成了监听器对事件的监听和响应。

3.源码解析

ApplicationContext,如果展开来讲怕是几天几夜都讲不完,我们这里先贴个继承图。其中publishEvent()方法来源于ApplicationEventPublisher。

整个过程可以简化成以下几个步骤

  1. applicationContext.publishEvent(ApplicationEvent e)  调用AbstractApplicationContext中的publishEvent方法
  2. multicastEvent  遍历所有合适的applicationListener,执行invoke(listener,event)方法
  3. 触发listener.onApplicationEvent()方法,如果是实现了ApplicationListener接口,则直接调用其中的onApplicationEvent()方法;如果是用@EventListener注释,则调用ApplicationListenerMethodAdapter中的onApplicationEvent()方法。

按照顺序依次贴出代码来看看执行过程,首先是applicationContext.publishEvent(ApplicationEvent e) ,自动装配的applicationContext的类型是AnnotationConfigEmbeddedWebApplicationContext,它又继承了AbstractApplicationContext。

AbstractApplicationContext执行publishEvent方法,其中multicastEvent表示将event组播到合适的listener监听器。

	protected void publishEvent(Object event, ResolvableType eventType) {
		...

		// Decorate event as an ApplicationEvent if necessary
		ApplicationEvent applicationEvent;
		if (event instanceof ApplicationEvent) {
			applicationEvent = (ApplicationEvent) event;
		}
		else {
			applicationEvent = new PayloadApplicationEvent<Object>(this, event);
			if (eventType == null) {
				eventType = ((PayloadApplicationEvent)applicationEvent).getResolvableType();
			}
		}

		// Multicast right now if possible - or lazily once the multicaster is initialized
		if (this.earlyApplicationEvents != null) {
			this.earlyApplicationEvents.add(applicationEvent);
		}
		else {
                    // 遍历applicationListener执行event事件
			getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
		}

		...
	}

SimpleApplicationEventMulticaster继承了ApplicationEventMulticaster,其中multicastEvent方法如下。

invokeListener(listener,event)触发了listener.onApplicationEvent()方法。

            @Override
	public void multicastEvent(final ApplicationEvent event, ResolvableType eventType) {
		ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
            // 遍历所有合适的applicationListener
		for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
			Executor executor = getTaskExecutor();
			if (executor != null) {
				executor.execute(new Runnable() {
					@Override
					public void run() {
                                         // 触发监听器 listener.onApplicationEvent(event);
						invokeListener(listener, event);
					}
				});
			}
			else {
				invokeListener(listener, event);
			}
		}
	}

如果像这样继承ApplicationListener的类,直接执行重写的方法

          

如果是用@EventListener注释的方法,则程序启动时,spring自动创建了一个ApplicationListenerMethodAdapter类。

                                  

ApplicationListenerMethodAdapter继承关系和属性如下,执行onApplicationEvent()方法,跳转到doInvoke()方法。

                                       

doInvoke方法执行的是用@EventListener注释的方法。

4.总结

需要对观察者模式、event+listener模式有个清楚的认知,以后需要用到广播的地方,可以直接借用Spring中的ApplicationListener。

 

 

猜你喜欢

转载自blog.csdn.net/qq_30905661/article/details/81237525