# Spring源码解析:事件实现及注意事项

请添加图片描述

# 观察者模式

观察者模式:多个观察者同时监听一个主题对象,当主题对象发生改变时,它的所有观察者都会收到通知。

例如微信公众号,当作者发文时,所有的订阅者都会收到。这样观察者模式就能实现广播,同时符合开闭原则,增加新的观察者不用改原有的代码。

观察者模式的UML图如下

在这里插入图片描述

Subject(主题):定义了观察者集合,提供注册,删除,通知观察者的方法 ConcreteSubject(具体主题) Observer(观察者):定义了收到主题通知时所做的改变 ConcreteObserver(具体观察者)

# 手写观察者模式

以微信公众号为例写一个观察者模式

抽象主题

public interface MySubject {

    void registerObserver(MyObserver o);

    void removeObserver(MyObserver o);

    void notifyObserver();
}

抽象观察者

public interface MyObserver {
    void update(String authorName, String articleName);
}

具体主题

public class WeChatServer implements MySubject {

    private List<MyObserver> myObservers;
    private String authorName;
    private String articleName;

    public WeChatServer(String authorName) {
        myObservers = new ArrayList<>();
        this.authorName = authorName;
    }

    public void publishArticle(String articleName) {
        this.articleName = articleName;
        notifyObserver();
    }

    @Override
    public void registerObserver(MyObserver o) {
        myObservers.add(o);
    }

    @Override
    public void removeObserver(MyObserver o) {
        if (myObservers.contains(o)) {
            myObservers.remove(o);
        }
    }

    @Override
    public void notifyObserver() {
        myObservers.forEach(item -> {
            item.update(authorName, articleName);
        });
    }
}

具体观察者

public class WeChatClient implements MyObserver {

    private String username;

    public WeChatClient(String username) {
        this.username = username;
    }

    @Override
    public void update(String authorName, String articleName) {
        System.out.println(username + ": " + authorName + " 发了一篇文章 " + articleName);
    }
}

测试类

public class Main {

    public static void main(String[] args) {
        WeChatServer weChatServer = new WeChatServer("Java识堂");
        WeChatClient user1 = new WeChatClient("张三");
        WeChatClient user2 = new WeChatClient("李四");
        weChatServer.registerObserver(user1);
        weChatServer.registerObserver(user2);
        weChatServer.publishArticle("《五分钟学会观察者模式》");
    }
}

输出为

张三: Java识堂 发了一篇文章 《五分钟学会观察者模式》
李四: Java识堂 发了一篇文章 《五分钟学会观察者模式》

# JDK提供了观察者模式接口

Java在java.util包中有对观察者模式进行支持,它定义了2个接口

抽象观察者

public interface Observer {
    void update(Observable o, Object arg);
}

抽象主题

public class Observable {
    private boolean changed = false;
    private Vector<Observer> obs;

    public Observable() {
        obs = new Vector<>();
    }

    public synchronized void addObserver(Observer o) {
        if (o == null)
            throw new NullPointerException();
        if (!obs.contains(o)) {
            obs.addElement(o);
        }
    }

    public synchronized void deleteObserver(Observer o) {
        obs.removeElement(o);
    }

    public void notifyObservers() {
        notifyObservers(null);
    }

    public void notifyObservers(Object arg) {
	
        Object[] arrLocal;

        synchronized (this) {
            if (!changed)
                return;
            arrLocal = obs.toArray();
            clearChanged();
        }

        for (int i = arrLocal.length-1; i>=0; i--)
            ((Observer)arrLocal[i]).update(this, arg);
    }

    public synchronized void deleteObservers() {
        obs.removeAllElements();
    }

    protected synchronized void setChanged() {
        changed = true;
    }

    protected synchronized void clearChanged() {
        changed = false;
    }

    public synchronized boolean hasChanged() {
        return changed;
    }

    public synchronized int countObservers() {
        return obs.size();
    }
}

和我们之前定义的很相似哈,只是多了一个change的开关字段,并且保证了线程安全

我们来把之前的例子改写一下,定义事件对象

@Data
@AllArgsConstructor
public class NewArticleEvent {

    private String authorName;
    private String articleName;
}
public class WeChatServer extends Observable {

    private String authorName;
    private String articleName;

    public WeChatServer(String authorName) {
        this.authorName = authorName;
    }

    public void publishArticle(String articleName) {
        setChanged();
        this.articleName = articleName;
        notifyObservers(new NewArticleEvent(authorName, articleName));
    }
}
public class WeChatClient implements Observer {

    private String username;

    public WeChatClient(String username) {
        this.username = username;
    }

    @Override
    public void update(Observable o, Object arg) {
        NewArticleEvent event = (NewArticleEvent) arg;
        System.out.println(username + ": " + event.getAuthorName() + " 发了一篇文章 " + event.getAuthorName());
    }
}
public class Main {

    public static void main(String[] args) {
        WeChatServer weChatServer = new WeChatServer("Java识堂");
        WeChatClient user1 = new WeChatClient("张三");
        WeChatClient user2 = new WeChatClient("李四");
        weChatServer.addObserver(user1);
        weChatServer.addObserver(user2);
        weChatServer.publishArticle("《五分钟学会观察者模式》");
    }
}

输出和上面一样

# 在spring中自定义事件

Spring用观察者模式来实现事件监听,在spring中实现事件监听比较简单,还是把上面的例子改造一下

事件类型需要继承ApplicationEvent类

@Data
public class NewArticleEvent extends ApplicationEvent {

    private String authorName;
    private String articleName;

    public NewArticleEvent(Object source, String authorName, String articleName) {
        super(source);
        this.authorName = authorName;
        this.articleName = articleName;
    }

}

我们可以通过实现ApplicationListener接口或者使用@EventListener注解来实现事件监听

@Component
public class NewArticleEventListener implements ApplicationListener<NewArticleEvent> {

    @Override
    public void onApplicationEvent(NewArticleEvent event) {
        System.out.println(event.getAuthorName() + " 发了一篇文章 " + event.getArticleName());
    }
}
@Component
public class MyEventListener {

    @EventListener
    public void newArticleEventListener(NewArticleEvent event) {
        System.out.println(event.getAuthorName() + " 发了一篇文章 " + event.getArticleName());
    }
}

上面的例子用两种方式写了2个观察者,下面开始写测试类。上面的类都在com.javashitang.part6包下哈

@Configuration
@ComponentScan("com.javashitang.part6")
public class AppConfig {
}
public class Main {

	public static void main(String[] args) {
		AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
		context.publishEvent(new NewArticleEvent(context, "Jva识堂", "《五分钟学会观察者模式》"));
		context.close();
	}
}

此时可以看到控制台输出

Java识堂 发了一篇文章 《五分钟学会观察者模式》
Java识堂 发了一篇文章 《五分钟学会观察者模式》

好了,到现在你已经会在spring中自定义事件了。是不是很简单

# spring事件实现

和spring事件相关的接口有如下4个

ApplicationListener:事件监听者,观察者 ApplicationEvent:事件 ApplicationEventPublisher和ApplicationEventMulticaster:发布事件 在这里插入图片描述 spring为什么要用ApplicationEventPublisher和ApplicationEventMulticaster来发布事件?

我们来看两者的接口定义

在这里插入图片描述 在这里插入图片描述 ApplicationEventPublisher只提供了发布事件的方法,而ApplicationEventMulticaster提供了注册监听者和发布事件的方法。

其实用ApplicationEventMulticaster就完全可以了,之所以再增加一个ApplicationEventPublisher接口,主要是为了方便别的组件使用,只想发布事件,而不用维护监听者,当然最终的发布操作还是要由ApplicationEventMulticaster来完成

从上面的继承关系图可以看到事件的发布都会调用到如下方法

SimpleApplicationEventMulticaster#multicastEvent 在这里插入图片描述 首先解析出事件的类型,然后根据事件类型获取对应的监听者,回调ApplicationListener#onApplicationEvent方法

当设置了线程池时,事件通知是通过线程池异步调用的。没有设置线程池时,依次同步调用

默认情况下没有设置线程池,spring处理事件是同步的。因此观察者执行的事件不要太长,以免影响主流程。如果事件的处理耗费的事件比较长时,可以以异步的方式进行处理,例如使用@Async注解

@Async
@EventListener
public void onEvent(MySpringEvent event) {
}

当给SimpleApplicationEventMulticaster设置线程池时,所有的事件都是异步调用的。 而@Async仅会让使用了该注解的方法异步调用

spring容器内置的事件有如下4个,我们可以监听这些事件来做相关操作

容器事件 解释
ContextStartedEvent ApplicationContext启动事件
ContextRefreshedEvent ApplicationContext 刷新事件
ContextStoppedEvent ApplicationContext停止事件
ContextClosedEvent ApplicationContext关闭事件