事件的应用在开发中也是比较灵活的一个方式,可以做到代码解耦,这个篇章就探究事件的发布订阅的原理,本篇原理比较简单,但代码结构较为复杂,所以如果只想了解事件机制原理,添加监听器的前面部分可以简单了解就行。
注解方式的监听事件
定义一个事件如下,但是需要注意的是不一定需要继承ApplicationEvent
,任何对象都可以,并且@EventListener
注解的方法可以private
修饰
public class CusEvent extends ApplicationEvent {
private String id;
public CusEvent(Object source) {
super(source);
}
public CusEvent(Object source, String id) {
this(source);
this.id = id;
}
public String getId() {
return id;
}
}
定义一个监听的方法
@Component
public class CusService {
@EventListener
public Object get(CusEvent event) {
System.out.println("eventListener 监听到事件");
return true;
}
// 或者也可以这样,和上面是一样的
@EventListener(CusEvent.class)
public Object get() {
System.out.println("eventListener 监听到事件 2");
return true;
}
}
发布一个事件
@Component
public class EventAppRunner implements ApplicationRunner {
@Autowired
private ApplicationContext applicationContext;
@Override
public void run(ApplicationArguments args) throws Exception {
applicationContext.publishEvent(new CusEvent(this, "1"));
}
}
public class CusEvent extends ApplicationEvent {
private String id;
public CusEvent(Object source) {
super(source);
}
public CusEvent(Object source, String id) {
this(source);
this.id = id;
继承ApplicationListener
,这个是spring提供的监听类;
这里有一个注意点,它是泛型的,泛型类型是我们定义的事件类,如果没有这个泛型,那么就是默认的ApplicationEvent
事件,那么发布的事件,都会被监听到
@Component
public class CusEventListener implements ApplicationListener<CusEvent> {
@Override
public void onApplicationEvent(CusEvent event) {
System.out.println("自定义事件监听器 监听到事件");
}
}
这第三种方式是支持事务的,需要引入spring-data的依赖
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-jdbcartifactId>
dependency>
使用就是注解不一样就行了
@TransactionalEventListener
public Object get2(CusEvent event) {
System.out.println("TransactionalEventListener 监听到事件");
return true;
}
除此之外,它还支持条件筛选,如下是EventListener
的定义,它有一个属性condition
它支持el表达式
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface EventListener {
@AliasFor("classes")
Class<?>[] value() default {};
@AliasFor("value")
Class<?>[] classes() default {};
String condition() default "";
}
那下面我们可以尝试以下:
// 这个是当我们发布的事件对象中的id='1'时,才会触发
@EventListener(condition = "#event.id == '1'")
public Object get3(CusEvent event) {
System.out.println("eventListener条件 监听到事件 - ID=1");
return true;
}
@EventListener(condition = "#event.id == '2'")
public Object get4(CusEvent event) {
System.out.println("eventListener条件 监听到事件 - ID=2");
return true;
}
也可以尝试枚举变量
@EventListener(condition = "T(com.liry.event.CusConst).WORD.name() == #event.id")
public Object get5(CusEvent event) {
System.out.println("eventListener条件 监听到事件 - ID=2");
return true;
}
如果你使用的是TransactionalEventListener
,它额外提供了属性phase
这个是用于事务传播机制的;
它提供了:
# 事务提交前执行
BEFORE_COMMIT
# 事务提交后执行
AFTER_COMMIT
# 事务回滚后执行
AFTER_ROLLBACK
# 事务完成后
AFTER_COMPLETION
用法如下:
@TransactionalEventListener(phase = TransactionPhase.BEFORE_COMMIT)
public Object get6(CusEvent event) {
System.out.println("TransactionalEventListener事务 监听到事件");
return true;
}
事件机制的原理也算简单,它使用了观察者模式,可以理解为在spring容器中它保存着一个监听器的列表,当我们发布事件时,就遍历这个列表,然后逐个判断是否符合条件,符合条件就执行监听器方法。
我们先来看一段代码:
protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
Assert.notNull(event, "Event must not be null");
// 这里直接看的话,就是对事件的封装
ApplicationEvent applicationEvent;
if (event instanceof ApplicationEvent) {
applicationEvent = (ApplicationEvent) event;
}
else {
applicationEvent = new PayloadApplicationEvent<>(this, event);
if (eventType == null) {
eventType = ((PayloadApplicationEvent<?>) applicationEvent).getResolvableType();
}
}
// 然后添加了事件
if (this.earlyApplicationEvents != null) {
this.earlyApplicationEvents.add(applicationEvent);
}
else {
// 或者执行
getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
}
// 以及父容器的执行
if (this.parent != null) {
if (this.parent instanceof AbstractApplicationContext) {
((AbstractApplicationContext) this.parent).publishEvent(event, eventType);
}
else {
this.parent.publishEvent(event);
}
}
}
通过这段代码,我们就已经出现了几个问题:
earlyApplicationEvents
),不直接执行?ApplicationEventMulticaster
在哪里初始化?ApplicationEventMulticaster
它怎样执行事件?位置:org.springframework.context.support.AbstractApplicationContext#refresh
这里ApplicationEventMulticaster
我这里叫做事件器,按照名字翻译叫事件广播器,我下面的都叫事件器;我们所使用的事件器是在spring初始化中做的:
protected void initApplicationEventMulticaster() {
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
// 判断spring容器中是否已经有了事件器bean
// 一般是不存在的
if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
// 若存在,则将spring容器中的事件器bean赋给当前的属性变量
this.applicationEventMulticaster =
beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);
if (logger.isTraceEnabled()) {
logger.trace("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");
}
}
else {
// 如果在spring容器中不存在事件器bean,那么这里new一个
this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
// 然后将这个new出来的事件器,注册到spring容器中
beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);
if (logger.isTraceEnabled()) {
logger.trace("No '" + APPLICATION_EVENT_MULTICASTER_BEAN_NAME + "' bean, using " +
"[" + this.applicationEventMulticaster.getClass().getSimpleName() + "]");
}
}
}
这一个步骤是在AbstractApplicationContext
中做的,所以this.applicationEventMulticaster
就是 Application Context
的属性,我们可以直接通过ApplicationContext
发布事件。
那为什么在方法中会先从bean容器中获取这个事件器呢?
这个我的理解是这样的:
这个init方法在bean容器之后、也在bean扫描之后,所以可能存在我们自定义的一个事件器,因为这个过程中,我们可以通过beanFactoryPostProcessor
,或者是beanPostProcessor
,也或者是beanRegister
进行添加,所以这个就相当于是将我们自定义的bean优先级提高。
上一步是初始化了事件器,那么在执行事件前,它还需要添加监听器,才能执行监听器的方法。
protected void registerListeners() {
// 1. 静态指定的监听器:显示声明,如add(new Listener())
for (ApplicationListener<?> listener : getApplicationListeners()) {
getApplicationEventMulticaster().addApplicationListener(listener);
}
// 2. spring扫描得到的bean,如import ApplicationListener
String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
for (String listenerBeanName : listenerBeanNames) {
// 这里添加的是bean的名称,因为这里还并没有实例化
getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
}
// 3. 执行早期的事件,也就是容器准备好之前的事件
Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents;
this.earlyApplicationEvents = null;
if (earlyEventsToProcess != null) {
for (ApplicationEvent earlyEvent : earlyEventsToProcess) {
getApplicationEventMulticaster().multicastEvent(earlyEvent);
}
}
}
注册监听器这一段中,分了两种方式添加监听器:
add(new Listener())
import ApplicationListener
这里要注意的一点是,这里添加的是bean的名称。
第三点中,它执行了监听器:
这里,他是先进行了复制,然后earlyApplicationEvents
置空,这里的这个属性是作为一个判断条件来用的,当不为空时,表示存在应用准备前期的事件,需要执行,因为现在才完成初始化,所以现在执行。
上面说过监听器的添加有两种,一种是静态指定(显示声明),另一种是spring扫描添加。
我们还是先找到注册监听器的位置:
org.springframework.context.support.AbstractApplicationContext#registerListeners
这里getApplicationListeners()
它获取的是当前实例对象(ApplicationContext
)的属性this.applicationListeners
,并且针对这个属性有add方法,也就是说,只要能拿到ApplicationContext
对象都可以调用add方法:addApplicationListener
,
这里添加步骤:
spring.factories
里的监听器,添加到listeners
applicationListeners
earlyApplicationListeners
,在close时调用主程序如下:
@SpringBootApplication
public class EventApp {
public static void main(String[] args) {
SpringApplication.run(EventApp.class);
}
}
那么它会默认读取spring.factories
文件中key为org.springframework.context.ApplicationListener
的监听器全类名,这里一般会有11个(一般简单的SpringBoot程序),这时添加的监听器是在application.listener
中
在prepareContext
中,存在很多ApplicationContextInitializer
应用初始化器,他们都会注册一个自己的监听器进行事件发布
这两个地方是应用初始化器自己添加监听器,和从spring.factories
文件读取的监听器,需要注意的是,在这里这些获取到的监听器都只是保持在一个ApplicationContext
的属性applicationListeners
中。
这里就是将启动前所有的监听器(上一步)都添加到earlyApplicationListeners
,表示这个是早期的监听器。
这里的步骤是:
添加一个bean后置处理器ApplicationListenerDetector
在bean生命周期之后,会执行后置处理器的回调方法,添加监听器的
添加后置处理器ApplicationListenerDetector
Spring在准备bean容器时,会添加默认的处理器和解析器,可以理解为这里添加的都是spring会执行,或者用到的必须的一些配置;
首先这在这里它会添加一个后置处理器·ApplicatonListenerDetector
位置:org.springframework.context.support.AbstractApplicationContext#prepareBeanFactory
后置处理器执行的位置:org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsAfterInitialization
添加监听器的位置:
org.springframework.context.support.ApplicationListenerDetector#postProcessAfterInitialization
可以看到,它这里判断是ApplicationListener
类型,也就是说只要我们的bean实现了ApplicationListener
接口,就会被添加到事件器。
相对于接口来说,注解方式的更为简便,其实就是将有注解的方法保存,然后在执行的时候进行反射执行,外部再套一个封装类就完成了,下面来看一下这个过程。
SmartInitializingSingleton
接口回调ApplicationListenerMethodAdapter
看这个位置:
org.springframework.beans.factory.support.DefaultListableBeanFactory#preInstantiateSingletons
这个地方就是添加注解方式的监听器的地方,所有实现SmartInitializingSingleton
的类都会被执行,而处理注解式监听器就是由实现了这个接口的类EventListenerMethodProcessor
完成,
简单看下的代码:
@Override
public void afterSingletonsInstantiated() {
// 获取bean容器
ConfigurableListableBeanFactory beanFactory = this.beanFactory;
Assert.state(this.beanFactory != null, "No ConfigurableListableBeanFactory set");
// 遍历所有的beanname
String[] beanNames = beanFactory.getBeanNamesForType(Object.class);
for (String beanName : beanNames) {
// 判断是否是原型bean,只有单例bean才进行此操作
if (!ScopedProxyUtils.isScopedTarget(beanName)) {
Class<?> type = null;
try {
// 获取bean的原始类型,因为存在动态代理,代理过的对象直接获取的类型并不是原始类型
type = AutoProxyUtils.determineTargetClass(beanFactory, beanName);
}
catch (Throwable ex) {
// An unresolvable bean type, probably from a lazy bean - let's ignore it.
if (logger.isDebugEnabled()) {
logger.debug("Could not resolve target class for bean with name '" + beanName + "'", ex);
}
}
if (type != null) {
// 这里再次进行了校验,ScopedObject是spring内部类,作用域对象
// 它这里应该是在ScopedObject对象中不应该有监听器类,所以这里在获取了一次
if (ScopedObject.class.isAssignableFrom(type)) {
try {
Class<?> targetClass = AutoProxyUtils.determineTargetClass(
beanFactory, ScopedProxyUtils.getTargetBeanName(beanName));
if (targetClass != null) {
type = targetClass;
}
}
catch (Throwable ex) {
// An invalid scoped proxy arrangement - let's ignore it.
if (logger.isDebugEnabled()) {
logger.debug("Could not resolve target bean for scoped proxy '" + beanName + "'", ex);
}
}
}
try {
// 这里是真正构建监听器封装类的地方
processBean(beanName, type);
}
catch (Throwable ex) {
throw new BeanInitializationException("Failed to process @EventListener " +
"annotation on bean with name '" + beanName + "'", ex);
}
}
}
}
}
private void processBean(final String beanName, final Class<?> targetType) {
// 没有解析过,含有@EventListener注解,并且是一个bean(也就是有@Component注解)
if (!this.nonAnnotatedClasses.contains(targetType) &&
AnnotationUtils.isCandidateClass(targetType, EventListener.class) &&
!isSpringContainerClass(targetType)) {
Map<Method, EventListener> annotatedMethods = null;
try {
// 从指定class中获取到被注解@EventListener标注的方法
annotatedMethods = MethodIntrospector.selectMethods(targetType,
(MethodIntrospector.MetadataLookup<EventListener>) method ->
AnnotatedElementUtils.findMergedAnnotation(method, EventListener.class));
}
catch (Throwable ex) {
// An unresolvable type in a method signature, probably from a lazy bean - let's ignore it.
if (logger.isDebugEnabled()) {
logger.debug("Could not resolve methods for bean with name '" + beanName + "'", ex);
}
}
// 如果没有获取到方法就结束
if (CollectionUtils.isEmpty(annotatedMethods)) {
this.nonAnnotatedClasses.add(targetType);
if (logger.isTraceEnabled()) {
logger.trace("No @EventListener annotations found on bean class: " + targetType.getName());
}
}
else {
// Non-empty set of methods
ConfigurableApplicationContext context = this.applicationContext;
Assert.state(context != null, "No ApplicationContext set");
// 这里事件监听器工厂,这里也是指定为EventListenerFactory的实现类,
// 可以看下面
List<EventListenerFactory> factories = this.eventListenerFactories;
Assert.state(factories != null, "EventListenerFactory List not initialized");
// 便利@EventListener注解的方法
// 每一个方法都应该创建一个监听器
for (Method method : annotatedMethods.keySet()) {
for (EventListenerFactory factory : factories) {
// 这里是工厂方法一般有的一个支持接口
// 用于判断当前这个工厂类是否支持这个方法
if (factory.supportsMethod(method)) {
// 这里类似判断
// 里面做了校验: private修饰的实例方法,并且是SpringProxy实现类,就报异常
// 这说明了什么问题?
// 说明了@EventListener它支持private修饰的方法
Method methodToUse = AopUtils.selectInvocableMethod(method, context.getType(beanName));
// 通过工厂方法创建一个监听器封装类,参数就是beanName,class,方法对象
ApplicationListener<?> applicationListener =
factory.createApplicationListener(beanName, targetType, methodToUse);
// 这里的这个判断,类似于适配扩展功能,因为我们可能会定义自己的一个工厂类
if (applicationListener instanceof ApplicationListenerMethodAdapter) {
((ApplicationListenerMethodAdapter) applicationListener).init(context, this.evaluator);
}
// 将创建的监听器封装类添加到事件器中
context.addApplicationListener(applicationListener);
break;
}
}
}
if (logger.isDebugEnabled()) {
logger.debug(annotatedMethods.size() + " @EventListener methods processed on bean '" +
beanName + "': " + annotatedMethods);
}
}
}
}
添加事件监听器工厂类,如果你没有引入spring-boot-starter-jdbc
依赖,那么只有一个DefaultEventListenerFactory
这个就是创建@EventListener
监听器的工厂类
和@EventListener
封装的地方一样,只是他们所使用工厂类不一样,工厂类TransactionalEventListenerFactory
创建ApplicationListenerMethodTransactionalAdapter
位置:org.springframework.context.event.EventListenerMethodProcessor#processBean
首先发布事件的类需要实现ApplicationEventPublisher
接口,ApplicationContext
也实现了这个一个接口,所以可以通过ApplicatoinContext
进行事件发布。
protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
Assert.notNull(event, "Event must not be null");
// 这里做发布事件的判别
// 当我们传入的事件event并非是ApplicationEvent时,它会进行一个事件对象的封装
ApplicationEvent applicationEvent;
if (event instanceof ApplicationEvent) {
applicationEvent = (ApplicationEvent) event;
}
else {
// 非applicationEvent类,进行封装
applicationEvent = new PayloadApplicationEvent<>(this, event);
if (eventType == null) {
eventType = ((PayloadApplicationEvent<?>) applicationEvent).getResolvableType();
}
}
// 那这里的判空,不难理解,在spring启动过程中,它会经过很多的步骤,每个步骤都有可能发布事件,但是在spring容器准备好之前都是不能发布事件的,
// 而这里的意思就是在准备好之前,所发布的事件都会添加到earlyApplicationEvents这个容器中,待容器准备好后,在执行这些事件
if (this.earlyApplicationEvents != null) {
this.earlyApplicationEvents.add(applicationEvent);
}
else {
// 没有暂存的事件,就执行执行事件
getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
}
// 如果父容器存在,就会进行父容器的事件发布
if (this.parent != null) {
if (this.parent instanceof AbstractApplicationContext) {
((AbstractApplicationContext) this.parent).publishEvent(event, eventType);
}
else {
this.parent.publishEvent(event);
}
}
}
这里我们讨论下这段代码:
if (this.earlyApplicationEvents != null) {
this.earlyApplicationEvents.add(applicationEvent);
}
else {
// 没有暂存的事件,就执行执行事件
getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
}
这段就是为了处理在准备前发布的事件,但是在registerListener
方法中已经清理过了,我们可以这样理解
publishEvent
进行事件发布,那么就是调用上面的方法,如果是在容器准备之前,那么就是执行this.earlyApplicationEvents.add(applicationEvent);
registerListener
然后清空前期事件getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
下面,那我们来看getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
位置:org.springframework.context.event.SimpleApplicationEventMulticaster#multicastEvent(org.springframework.context.ApplicationEvent, org.springframework.core.ResolvableType)
@Override
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
// 获取事件类型
ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
// 这里是获取一个执行器,我们可以设置一个我们自定义的一个线城池
// 这里默认是null
Executor executor = getTaskExecutor();
// getApplicationListeners 其实做了一个缓存,根据事件类型进行监听器的缓存
// 第一次是从bean容器获取的,之后都是从缓存中获取
for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
if (executor != null) {
executor.execute(() -> invokeListener(listener, event));
}
else {
invokeListener(listener, event);
}
}
}
这两个的区别在于,一个可以保证同步执行,一个保证同步执行同时,保证事务的传播;
看@TransactionalEventListener
执行的代码,位置:org.springframework.transaction.event.ApplicationListenerMethodTransactionalAdapter#onApplicationEvent
@Override
public void onApplicationEvent(ApplicationEvent event) {
if (TransactionSynchronizationManager.isSynchronizationActive() &&
TransactionSynchronizationManager.isActualTransactionActive()) {
TransactionSynchronization transactionSynchronization = createTransactionSynchronization(event);
TransactionSynchronizationManager.registerSynchronization(transactionSynchronization);
}
else if (this.annotation.fallbackExecution()) {
if (this.annotation.phase() == TransactionPhase.AFTER_ROLLBACK && logger.isWarnEnabled()) {
logger.warn("Processing " + event + " as a fallback execution on AFTER_ROLLBACK phase");
}
processEvent(event);
}
else {
// No transactional event execution at all
if (logger.isDebugEnabled()) {
logger.debug("No transaction is active - skipping " + event);
}
}
}
之前在事务原理篇中解析过,spring保证事务的传播,是有一个事务管理器,它在线程缓存中保持了会话connection
,通过dataSource
获取,并且事务的提交回滚都是由TransactionSynchronizationManager
这个类进行管理,我们看一段提交的代码:
public static void triggerBeforeCommit(boolean readOnly) {
for (TransactionSynchronization synchronization : TransactionSynchronizationManager.getSynchronizations()) {
synchronization.beforeCommit(readOnly);
}
}
这里它其实是获取了所有的TransactionSynchronization
类,在开启事务和提交事务时,都是获取到这个对象,然后通过这个对象进行开启事务和事务提交,所以这个类就类似一个事务的回调方法,在开启事务和提交事务这些操作,都会调用实现类。
呐,这里的代码就和ApplicationListenerMethodTransactionalAdapter
执行的相似。
ApplicationEvent
接口的实现类,也可以是随意的一个对象@CustomEventListener
CustomEventListenerFactory implement EventListenerFactory
ApplicationListenerMethodAdapter
监听器对象@EventListener
不一定要public
修饰,private
也是生效的ApplicationEvent
spring.factories
添加监听器类