关于Java的事件监听机制
Spring事件机制是观察者模式的一种实现,但是除了发布者和监听者者两个角色之外,还有一个EventMultiCaster的角色负责把事件转发给监听者。
Spring的事件监听(也称事件驱动)是观察者模式的一种实现,比较常见的有发布-订阅模型。通常我们利用消息队列来实现不同系统之间的解耦,如用户注册完成后,可以向消息队列发布一条消息,然后订阅了此topic的子系统(如邮件服务,积分服务)收到发布的消息之后,就会做相应的处理。这样做的好处是避免了在注册服务里耦合其他服务的代码,并且,执行子系统的业务将会异步执行,互不影响。下图是一个经典的观察者模式的结构。
以下为上述观察者模式的java简单实现:
Subject
9 public abstract class Subject {
10
11 //维护一个所有观察者集合
12 private List<Observer> list = new ArrayList<>();
13
14 //新注册一个观察者
15 public void attach(Observer observer){
16 list.add(observer);
17 System.out.println("新注册一个观察者");
18 }
19
20 //删除一个已注册的观察者
21 public void detach(Observer observer){
22 list.remove(observer);
23 System.out.println("删除一个已注册的观察者");
24 }
25
26
27 //通知所有已经注册的观察者
28 public void notifyObservers(String state){
29 for (int i = 0; i < list.size(); i++) {
30 list.get(i).update(state);
31 }
32 }
33 }
Observer
6 public interface Observer {
7
8 // 抽象出的更新行为
9 public void update(String state);
10 }
ConcreteSubject
6 public class ConcreteSubject extends Subject{
7
8 //真实主题内维护一个状态
9 private String state;
10
11 public String getState() {
12 return state;
13 }
14
15 public void change(String state){
16 this.state = state;
17 System.out.println("真实主题状态变化为:"+state);
18 this.notifyObservers(state);
19 }
20 }
ConcreteObserver
6 public class ConcreteObserver implements Observer {
7
8 //具体观察者的状态
9 private String observerState;
10
11 @Override
12 public void update(String state) {
13 //这里可以根据传递过来的主题的状态作出相应的业务
14 observerState = state;
15 System.out.println("观察者的状态跟着变化为:"+observerState);
16 }
17 }
Main
6 public class Main {
7 public static void main(String[] args) {
8 //真实主题
9 ConcreteSubject concreteSubject = new ConcreteSubject();
10 //真实观察者
11 ConcreteObserver concreteObserver = new ConcreteObserver();
12 //观察者先注册
13 concreteSubject.attach(concreteObserver);
14
15 //改变真实主题状态
16 concreteSubject.change("2");
17
18 }
19 }
spring也对事件驱动模型提供了支持,该模型主要由三部分组成:
1,事件(ApplicationEvent):继承了jdk的EventObject,在spring项目中可以继承ApplicationEvent,来自定义自己的事件。
spring容器内部对ApplicationEvent有着下面几个实现
ContextRefreshEvent,当ApplicationContext容器初始化完成或者被刷新的时候,就会发布该事件
ContextStartedEvent,当ApplicationContext启动的时候发布事件,即调用ConfigurableApplicationContext接口的start方法的时候
ContextStoppedEvent,当ApplicationContext容器停止的时候发布事件,即调用ConfigurableApplicationContext的close方法的时候
ContextClosedEvent,当ApplicationContext关闭的时候发布事件,即调用ConfigurableApplicationContext的close方法的时候,关闭指的是所有的单例Bean都被销毁。
2,发布者(ApplicationEventPublisher)
@FunctionalInterface
public interface ApplicationEventPublisher {
default void publishEvent(ApplicationEvent event) {
this.publishEvent((Object)event);
}
void publishEvent(Object var1);
}
一般使用时让你的发布类继承ApplicationEventPublisherAware,得到AbstractApplicationContext它实现了ApplicationEventPublisherAware。
例如接下来的例子代码中的发布类:
@Component
public class DecidePublisher implements ApplicationEventPublisherAware {
private ApplicationEventPublisher publisher;
@Override
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
this.publisher = applicationEventPublisher;
}
public void decideTo(ApplicationEvent event) {
publisher.publishEvent(event);
}
}
3,事件订阅者(ApplicationListener):实现这个接口,就可以监听ApplicationListener发布的特定的事件。
实现ApplicationListener这个接口,重写onApplicationEvent()方法,来处理监听到的ApplicationEvent,这里可以监听特定类型的事件。
spring也为发布者和监听者提供了相应的注解支持,只需要在对应的观察者类的对应方法上加上 @EventListener。
对于发布者,可以直接在service通过@Autowired注入ApplicationEventPublisher。
两个事件吃(EatEvent)和睡(SleepEvent);吃事件的监听者(EatListener),睡事件的监听者(SleepListener),两个事件皆监听的(HolidayListener);发布者(DecidePublisher)。
EatEvent
public class EatEvent extends ApplicationEvent {
public EatEvent(String source) {
super(source);
}
}
SleepEvent
public class SleepEvent extends ApplicationEvent {
public SleepEvent(String source) {
super(source);
}
}
三个监听Listener
EatListener
@Component
public class EatListener implements ApplicationListener<EatEvent> {
@Override
public void onApplicationEvent(EatEvent eatEvent) {
System.out.println("eat event");
}
}
SleepListener
@Component
public class SleepListener implements ApplicationListener<SleepEvent> {
@Override
public void onApplicationEvent(SleepEvent sleepEvent) {
System.out.println("sleep event");
}
}
HolidayListener
@Component
public class HolidayListener implements ApplicationListener<ApplicationEvent> {
@Override
public void onApplicationEvent(ApplicationEvent applicationEvent) {
if (applicationEvent.getSource().equals("eat")) {
System.out.println("On holiday i eat.");
} else if (applicationEvent.getSource().equals("sleep")) {
System.out.println("On holiday i sleep");
}
}
}
DecidePublisher
@Component
public class DecidePublisher implements ApplicationEventPublisherAware {
private ApplicationEventPublisher publisher;
@Override
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
this.publisher = applicationEventPublisher;
}
public void decideTo(ApplicationEvent event) {
publisher.publishEvent(event);
}
}
启动类
@SpringBootApplication
public class DemoApplication implements CommandLineRunner {
@Autowired
DecidePublisher publisher;
public static void main(String[] args) {
SpringApplication application = new SpringApplication(DemoApplication.class);
application.run(args);
}
@Override
public void run(String... args) throws Exception {
EatEvent eatEvent = new EatEvent("eat");
SleepEvent sleepEvent = new SleepEvent("sleep");
System.out.println("start to publish eat event");
publisher.decideTo(eatEvent);
System.out.println("start to publish sleep event");
publisher.decideTo(sleepEvent);
}
}
public void publishEvent(Object event) {
this.publishEvent(event, (ResolvableType)null);
}
protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
Assert.notNull(event, "Event must not be null");
Object applicationEvent;
if (event instanceof ApplicationEvent) {
applicationEvent = (ApplicationEvent)event;
} else {
// 若事件不是ApplicationEvent类型,则被包裹成PayloadApplicationEvent
applicationEvent = new PayloadApplicationEvent(this, event);
if (eventType == null) {
eventType = ((PayloadApplicationEvent)applicationEvent).getResolvableType();
}
}
if (this.earlyApplicationEvents != null) { // debug中为null
this.earlyApplicationEvents.add(applicationEvent);
} else {
this.getApplicationEventMulticaster().multicastEvent((ApplicationEvent)applicationEvent, eventType);
}
if (this.parent != null) { // debug中为null
if (this.parent instanceof AbstractApplicationContext) {
((AbstractApplicationContext)this.parent).publishEvent(event, eventType);
} else {
this.parent.publishEvent(event);
}
}
}
接下来来到
获取applicationEventMulticaster,他是SimpleApplicationEventMulticaster
SimpleApplicationEventMulticaster#multicastEvent
主要是:获取所有监听该事件类型的listener,执行其onApplicationEvent方法
public void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType) {
ResolvableType type = eventType != null ? eventType : this.resolveDefaultEventType(event);
Iterator var4 = this.getApplicationListeners(event, type).iterator();
while(var4.hasNext()) {
ApplicationListener<?> listener = (ApplicationListener)var4.next();
Executor executor = this.getTaskExecutor();
if (executor != null) { // 默认为null,意味着调用者线程来处理onApplicationEvent方法
executor.execute(() -> {
this.invokeListener(listener, event);
});
} else {
this.invokeListener(listener, event);
}
}
}
在创建SimpleApplicationEventMulticaster时,executor是null,所以默认情况下所有的listener 的onApplicationEvent是直接在当前线程(事件发布者所在线程)中调用,所以如果onApplicationEvent有阻塞操作也会导致事件发布者被阻塞,后续的其他listener也会被阻塞无法调用。 那么如何解决这一问题?
这里是EatEvent,它是ApplicationEvent类型。
先获取到了DelegatingApplicationListener,
再次循环得到EatListener
再次循环得到HolidayListener
上述debug我们发现在spring的事件发布机制中,还有一个EventMultiCaster的角色负责把事件转发给监听者,它们之间的关系如下图
发布者调用applicationEventPublisher.publishEvent(msg); 是会将事件发送给了EventMultiCaster, EventMultiCaster注册着所有的Listener,然后根据事件类型决定转发给那个Listener。
ApplicationContext完成bean的装配和初始化后(非lazy-init的singleton bean会加载后就初始化),会尝试创建一个eventMultiCaster,创建代码如下:
protected void initApplicationEventMulticaster() {
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
//判断有没有一个name是“applicationEventMulticaster”且实现了“ ApplicationEventMulticaster”的bean,有的话那它就是eventMultiCaster
if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
this.applicationEventMulticaster =
beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);
if (logger.isDebugEnabled()) {
logger.debug("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");
}
}
else {
// 没有这样一个bean,那就会创建一个默认的
this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);
if (logger.isDebugEnabled()) {
logger.debug("Unable to locate ApplicationEventMulticaster with name '" +
APPLICATION_EVENT_MULTICASTER_BEAN_NAME +
"': using default [" + this.applicationEventMulticaster + "]");
}
}
}
可以看到默认就是SimpleApplicationEventMulticaster。
关于上面的问题:如何使事件发布者和监听者不用在同一个线程中调用,即开启一个新线程执行onApplicationEvent方法?
给SimpleApplicationEventMulticaster实例中注入一个线程池executor。
//使用线程池运行listener
<bean id="executorService" class="java.util.concurrent.Executors" factory-method="newCachedThreadPool">
</bean>
<bean id="applicationEventMulticaster" class="org.springframework.context.event.SimpleApplicationEventMulticaster">
<property name="taskExecutor" ref="executorService">
</property>
</bean>
spring会加载一个叫applicationEventMulticaster且实现了ApplicationEventMulticaster接口的multicaster,自定义multicaster需要实现了该接口然后将bean的名字设为applicationEventMulticaster即可。
Spring启动完成之后(已经完成bean解析,non-lazy-init的singleton实例化和初始化,完成listener的注册),默认会发布一个ContextRefreshedEvent事件,该事件包装的消息是一个ApplicationContext对象。
public class ContextRefreshedEvent extends ApplicationContextEvent {
/**
* Create a new ContextRefreshedEvent.
* @param source the {@code ApplicationContext} that has been initialized
* or refreshed (must not be {@code null})
*/
public ContextRefreshedEvent(ApplicationContext source) {
super(source);
}
}
在Dubbo中,服务暴露功能便利用到了ContextRefreshedEvent事件,服务暴露入口在ServiceBean
public class ServiceBean<T> extends ServiceConfig<T> implements InitializingBean, DisposableBean,
ApplicationContextAware, ApplicationListener<ContextRefreshedEvent>, BeanNameAware,
ApplicationEventPublisherAware {
它实现了ApplicationListener
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
if (!isExported() && !isUnexported()) {
if (logger.isInfoEnabled()) {
logger.info("The service ready on spring started. service: " + getInterface());
}
export(); // 服务暴露功能的入口
}
}
ServiceBean还实现了ApplicationEventPublisherAware,说明他还有发布功能,来看看它的setApplicationEventPublisher
@Override
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
this.applicationEventPublisher = applicationEventPublisher;
}
就是获取ApplicationEventPublisher,实现是AbstractApplicationContext。
Dubbo利用它在export方法执行完后发布一个ServiceBeanExportedEvent事件,ReferenceAnnotationBeanPostProcessor监听该事件。
Spring事件机制
Spring的事件监听机制