spring事件发布机制

关于Java的事件监听机制
Spring事件机制是观察者模式的一种实现,但是除了发布者和监听者者两个角色之外,还有一个EventMultiCaster的角色负责把事件转发给监听者。

观察者模式

Spring的事件监听(也称事件驱动)是观察者模式的一种实现,比较常见的有发布-订阅模型。通常我们利用消息队列来实现不同系统之间的解耦,如用户注册完成后,可以向消息队列发布一条消息,然后订阅了此topic的子系统(如邮件服务,积分服务)收到发布的消息之后,就会做相应的处理。这样做的好处是避免了在注册服务里耦合其他服务的代码,并且,执行子系统的业务将会异步执行,互不影响。下图是一个经典的观察者模式的结构。
spring事件发布机制_第1张图片
以下为上述观察者模式的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事件发布

spring也对事件驱动模型提供了支持,该模型主要由三部分组成:
1,事件(ApplicationEvent):继承了jdk的EventObject,在spring项目中可以继承ApplicationEvent,来自定义自己的事件。
spring容器内部对ApplicationEvent有着下面几个实现
spring事件发布机制_第2张图片
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);
	}
}

Debug流程

定位到AbstractApplicationContext
在这里插入图片描述

public void publishEvent(Object event) {
        this.publishEvent(event, (ResolvableType)null);
    }

接着
spring事件发布机制_第3张图片

    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
spring事件发布机制_第4张图片
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类型。
spring事件发布机制_第5张图片
先获取到了DelegatingApplicationListener
在这里插入图片描述
再次循环得到EatListener
spring事件发布机制_第6张图片

在这里插入图片描述
再次循环得到HolidayListener
spring事件发布机制_第7张图片
上述debug我们发现在spring的事件发布机制中,还有一个EventMultiCaster的角色负责把事件转发给监听者,它们之间的关系如下图
spring事件发布机制_第8张图片
发布者调用applicationEventPublisher.publishEvent(msg); 是会将事件发送给了EventMultiCaster, EventMultiCaster注册着所有的Listener,然后根据事件类型决定转发给那个Listener。

EventMultiCaster

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>

自定义multicaster

spring会加载一个叫applicationEventMulticaster且实现了ApplicationEventMulticaster接口的multicaster,自定义multicaster需要实现了该接口然后将bean的名字设为applicationEventMulticaster即可。

ContextRefreshedEvent事件

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中的应用

Dubbo中,服务暴露功能便利用到了ContextRefreshedEvent事件,服务暴露入口在ServiceBean

public class ServiceBean<T> extends ServiceConfig<T> implements InitializingBean, DisposableBean,
        ApplicationContextAware, ApplicationListener<ContextRefreshedEvent>, BeanNameAware,
        ApplicationEventPublisherAware {

它实现了ApplicationListener,可以看出他是个listener监听ContextRefreshedEvent事件,来看看它的onApplicationEvent方法

    @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的事件监听机制

你可能感兴趣的:(Java进阶)