【spring】spring学习系列之十一:spring的事件监听

系列文章目录


前言

Spring框架的事件机制(ApplicationEvent)提供了一种组件间通信的方式,允许应用程序的不同部分通过发布-订阅模式进行解耦交互。

一、使用

  1. 先定义事件
public class MyCustomEvent {
    private String message;

    public MyCustomEvent(String message) {
        this.message = message;
    }

    public String getMessage() {
        return message;
    }
}
  1. 注册事件监听器
    这里是通过@EventListener的方式,也可以通过实现ApplicationListener接口的方式
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;

@Component
public class MyEventListener {

    // 监听特定类型的事件
    @EventListener
    public void handleMyEvent(MyCustomEvent event) {
        System.out.println("Received MyCustomEvent - " + event.getMessage());
    }

    // 监听多个事件类型
    @EventListener({AnotherEvent.class, YetAnotherEvent.class})
    public void handleMultipleEvents(Object event) {
        if (event instanceof AnotherEvent) {
            System.out.println("Handling AnotherEvent");
        } else if (event instanceof YetAnotherEvent) {
            System.out.println("Handling YetAnotherEvent");
        }
    }

    // 带条件的事件监听
    @EventListener(condition = "#event.message.startsWith('important')")
    public void handleImportantEvents(MyCustomEvent event) {
        System.out.println("Received IMPORTANT event - " + event.getMessage());
    }
}
  1. 发布事件
    利用ApplicationContext发布事件,因为ApplicationContext有事件广播器
@Service
public class EventPublisherService implements ApplicationContextAware{

    private ConfigurableApplicationContext applicationContext;
	@Override
	public void setApplicationContext(ApplicationContext applicationContext) {
		this.applicationContext = applicationContext;
	}
    public void publishEvent(String message) {
        MyCustomEvent event = new MyCustomEvent(message);
        applicationContext.publishEvent(event);
    }
}

二、整体流程

回顾一下spring的启动流程

  1. 在构造方法中,初始化了6个重要的BeanDefinition,其中有2个是EventListenerMethodProcessor和DefaultEventListenerFactory,之前介绍过这两个与@EventListener注解有关。
  2. 在refresh方法中,registerBeanPostProcessors这一步注册了ApplicationListenerDetector(检测bean是否为监听器类型),initApplicationEventMulticaster这一步初始化了SimpleApplicationEventMulticaster(事件广播器)
  3. 在refresh方法中,registerListeners这一步将所有的监听器注册到事件广播器中

我们知道了spring事件监听的相关组件,再来看看每个组件的具体功能

三、EventListenerMethodProcessor和DefaultEventListenerFactory

1.EventListenerMethodProcessor

EventListenerMethodProcessor实现了两个重要的接口BeanFactoryPostProcessor 和SmartInitializingSingleton

public class EventListenerMethodProcessor
		implements SmartInitializingSingleton, ApplicationContextAware, BeanFactoryPostProcessor {
		}

1)BeanFactoryPostProcessor 是在容器执行refresh方法中的invokeBeanFactoryPostProcessors方法时调用,前面文章已经介绍过。看下代码

@Override
	public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
		this.beanFactory = beanFactory;

		Map<String, EventListenerFactory> beans = beanFactory.getBeansOfType(EventListenerFactory.class, false, false);
		List<EventListenerFactory> factories = new ArrayList<>(beans.values());
		AnnotationAwareOrderComparator.sort(factories);
		this.eventListenerFactories = factories;
	}
	

就是在容器中找到类型为EventListenerFactory的bean,然后设置到eventListenerFactories 这个属性中,而容器中正好在构造阶段注册了DefaultEventListenerFactory这个BeanDefinition。注意,eventListenerFactories 是一个集合,也就是说我们也可以自己注册类型为EventListenerFactory的bean。当然我们现在只分析DefaultEventListenerFactory即可。

2)SmartInitializingSingleton是在所有的bean实例化初始化都完成后执行的,前面文章也介绍过。看下这里的实现代码:

@Override
	public void afterSingletonsInstantiated() {
		ConfigurableListableBeanFactory beanFactory = this.beanFactory;
		Assert.state(this.beanFactory != null, "No ConfigurableListableBeanFactory set");
		String[] beanNames = beanFactory.getBeanNamesForType(Object.class);
		for (String beanName : beanNames) {
			if (!ScopedProxyUtils.isScopedTarget(beanName)) {
				Class<?> type = null;
				try {
					//如果这个BeanDefinition是代理对象,使用被代理的class。因为被代理的class才有@EventListener注解
					type = AutoProxyUtils.determineTargetClass(beanFactory, beanName);
				}
				catch (Throwable ex) {
					...
				}
				if (type != null) {
					...
					try {
						processBean(beanName, type);
					}
					catch (Throwable ex) {
						...
					}
				}
			}
		}
	}

可以看到就是遍历所有的beanName,调用processBean方法

private void processBean(final String beanName, final Class<?> targetType) {
		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) {
				。。。
				}
			}

			if (CollectionUtils.isEmpty(annotatedMethods)) {
				this.nonAnnotatedClasses.add(targetType);
				。。。
			}
			else {
				// Non-empty set of methods
				ConfigurableApplicationContext context = this.applicationContext;
				Assert.state(context != null, "No ApplicationContext set");
				List<EventListenerFactory> factories = this.eventListenerFactories;
				Assert.state(factories != null, "EventListenerFactory List not initialized");
				for (Method method : annotatedMethods.keySet()) {
					for (EventListenerFactory factory : factories) {
						if (factory.supportsMethod(method)) {//判断工厂类是否支持该方法
							Method methodToUse = AopUtils.selectInvocableMethod(method, context.getType(beanName));
							//利用工厂生成一个ApplicationListener实例
							ApplicationListener<?> applicationListener =
									factory.createApplicationListener(beanName, targetType, methodToUse);
							if (applicationListener instanceof ApplicationListenerMethodAdapter) {
								((ApplicationListenerMethodAdapter) applicationListener).init(context, this.evaluator);
							}
							//将生成的ApplicationListener实例添加到Spring的applicationListeners中,后续会注册到事件广播器上
							context.addApplicationListener(applicationListener);
							break;
						}
					}
				}
				。。。
			}
		}
	}

逻辑如下:
a. 查找Class上所有被@EventListener注解的方法
b. 如果有被@EventListener注解的方法,遍历所有的EventListenerFactory,找到能处理这个方法的EventListenerFactory (适配器模式),利用EventListenerFactory工厂将该方法生成一个ApplicationListener实例。

可以通过@EventListener注解的属性增加一些功能,比如前面例子中的通过condition属性判断是不是要处理这个事件,这里就不展开了

c. 将生成的ApplicationListener实例添加到Spring的applicationListeners中,后续会注册到事件广播器上

2.DefaultEventListenerFactory

上面已经介绍了,这个类就是将被@EventListener注解的方法生成一个ApplicationListener实例。不展开了

3.ApplicationListenerDetector

实现了MergedBeanDefinitionPostProcessor

class ApplicationListenerDetector implements DestructionAwareBeanPostProcessor, MergedBeanDefinitionPostProcessor {
}

MergedBeanDefinitionPostProcessor 有2个扩展点,一个是在createBeanInstance之后执行postProcessMergedBeanDefinition方法,这里就是判断是否为单例bean

public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
		if (ApplicationListener.class.isAssignableFrom(beanType)) {
			this.singletonNames.put(beanName, beanDefinition.isSingleton());
		}
	}

另一个是在初始化后执行postProcessAfterInitialization方法:就是判断如果这个bean实现了ApplicationListener接口,且是单例bean,添加到Spring的applicationListeners中,后续会注册到事件广播器上

public Object postProcessAfterInitialization(Object bean, String beanName) {
		if (bean instanceof ApplicationListener) {
			// potentially not detected as a listener by getBeanNamesForType retrieval
			Boolean flag = this.singletonNames.get(beanName);
			if (Boolean.TRUE.equals(flag)) {
				// singleton bean (top-level or inner): register on the fly
				//添加到Spring的applicationListeners中,后续会注册到事件广播器上
				this.applicationContext.addApplicationListener((ApplicationListener<?>) bean);
			}
			else if (Boolean.FALSE.equals(flag)) {
				if (logger.isWarnEnabled() && !this.applicationContext.containsBean(beanName)) {
					// inner bean with other scope - can't reliably process events
					logger.warn("Inner bean '" + beanName + "' implements ApplicationListener interface " +
							"but is not reachable for event multicasting by its containing ApplicationContext " +
							"because it does not have singleton scope. Only top-level listener beans are allowed " +
							"to be of non-singleton scope.");
				}
				this.singletonNames.remove(beanName);
			}
		}
		return bean;
	}

4.initApplicationEventMulticaster

1)从容器中找beanName为applicationEventMulticaster的bean,如果存在,将其设置为applicationEventMulticaster
2)如果不存在,创建一个默认的SimpleApplicationEventMulticaster,设置为applicationEventMulticaster

protected void initApplicationEventMulticaster() {
		ConfigurableListableBeanFactory beanFactory = getBeanFactory();
		if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
			this.applicationEventMulticaster =
					beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);
			if (logger.isTraceEnabled()) {
				logger.trace("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");
			}
		}
		else {
			this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
			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() + "]");
			}
		}
	}

5.registerListeners

遍历applicationListeners中的ApplicationListener,注册到ApplicationEventMulticaster中,这里才算是真正注册上了

for (ApplicationListener<?> listener : getApplicationListeners()) {
			getApplicationEventMulticaster().addApplicationListener(listener);
		}

6.SimpleApplicationEventMulticaster

注册ApplicationListener,就是将ApplicationListener放到内部类defaultRetriever的applicationListeners属性中

public void addApplicationListener(ApplicationListener<?> listener) {
		synchronized (this.defaultRetriever) {
			// Explicitly remove target for a proxy, if registered already,
			// in order to avoid double invocations of the same listener.
			Object singletonTarget = AopProxyUtils.getSingletonTarget(listener);
			if (singletonTarget instanceof ApplicationListener) {
				this.defaultRetriever.applicationListeners.remove(singletonTarget);
			}
			this.defaultRetriever.applicationListeners.add(listener);
			this.retrieverCache.clear();
		}
	}

四、事件发布过程

上面已经介绍了监听器的注册,接下来看看事件发布过程

前面的例子中,我们是通过ApplicationContext的publishEvent方法来发布事件的

有2个方法,分别用来发布ApplicationEvent类型的事件和非ApplicationEvent 类型的事件

//发布ApplicationEvent类型的事件
public void publishEvent(ApplicationEvent event) {
		publishEvent(event, null);
	}
//发布非ApplicationEvent 类型的事件
public void publishEvent(Object event) {
		publishEvent(event, null);
	}

我们主要看发布ApplicationEvent类型的事件,通过getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);调用SimpleApplicationEventMulticaster的multicastEvent方法

public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
		ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
		Executor executor = getTaskExecutor();
		//找到能处理该事件的监听器,遍历
		for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
			//如果执行器不为空,用执行器调用监听器的onApplicationEvent方法
			if (executor != null) {
				executor.execute(() -> invokeListener(listener, event));
			}
			else {
			//如果执行器为空,直接调用监听器的onApplicationEvent方法
				invokeListener(listener, event);
			}
		}
	}

1)找到能处理该事件的监听器,遍历监听器
2)如果执行器不为空,用执行器调用监听器的onApplicationEvent方法。
这里是为了使用线程池来异步执行,而SimpleApplicationEventMulticaster的executor是为空的,那怎么异步呢?前面讲initApplicationEventMulticaster时,如果容器没有广播器才创建默认的SimpleApplicationEventMulticaster,那我们自己定义一个广播器继承SimpleApplicationEventMulticaster,并且设置一个线程池,那不就可以异步了吗
3)如果执行器为空,直接调用监听器的onApplicationEvent方法
这种情况就同步执行

getApplicationListeners方法有点复杂,因为为了支持非ApplicationEvent 类型的事件,做了一些包装

这里只简单介绍一下非ApplicationEvent 类型的事件的用法

@Component
public class MyListeners {
    
    // 类型1:直接声明payload类型
    @EventListener
    public void handleString(String payload) {
        // 能接收 publishEvent("hello")
    }
    
    // 类型2:声明PayloadApplicationEvent泛型
    @EventListener
    public void handlePayloadEvent(PayloadApplicationEvent<String> event) {
        String payload = event.getPayload();
        // 能接收 publishEvent("hello")
    }
}

总结

  1. 监听器需要注册到ApplicationEventMulticaster后才能监听事件,有2中注册方式
    1)在所有的bean实例化初始化都完成后,通过EventListenerMethodProcessor查找@EventListener注解的方法,将其生成ApplicationLister对象,然后注册到ApplicationEventMulticaster中
    2)通过ApplicationListenerDetector这个BPP,在bean的生命周期中执行初始化后方法postProcessAfterInitialization,如果这个bean实现了ApplicationListener接口,且是单例bean,注册到ApplicationEventMulticaster中
  2. 发布事件时,找到能处理该事件的所有监听器,遍历监听器,逐个执行监听器的onApplicationEvent方法。如果要异步执行,必须给广播器设置线程池。
  3. 事件分为ApplicationEvent类型的事件和非ApplicationEvent 类型的事件,都需要对应的监听器才能处理
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值