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。
整个过程可以简化成以下几个步骤
- applicationContext.publishEvent(ApplicationEvent e) 调用AbstractApplicationContext中的publishEvent方法
- multicastEvent 遍历所有合适的applicationListener,执行invoke(listener,event)方法
- 触发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。