spring框架抽象出了一套事件机制,通过发布,订阅方式来实现,类似观察者模式。先来搭建一个基本的spring环境,然后做个简单的案例演示事件的应用,最后在做源码分析。
引入依赖
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-beansartifactId>
<version>5.2.10.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-contextartifactId>
<version>5.2.10.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-context-supportartifactId>
<version>5.2.10.RELEASEversion>
dependency>
创建一个配置类和启动类
@Configuration
public class AppConfig {
}
public class TestSpring {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
}
}
简单的环境搭建完毕。
熟悉发布-订阅模型都清楚,整个模型肯定要包括3种角色,发布者,订阅者(也叫监听者),事件。这三个角色有什么作用呢?
发布者:触发事件的角色,触发以后,所有监听,也叫订阅该事件的监听者都会收到通知,发布者不需要知道有多少个监听者存在。
订阅者:监听某个事件的发生,一旦发生事件,执行相应处理逻辑。
事件:代表事件的实体,这个事件实体通常会包含某些信息,发布者将信息包装到事件内,订阅者可以获取该信息,达到传递信息的作用
spring本身定义了一个发布事件的方法,可以看成是发布者。
订阅者在spring容器内叫做ApplicationListener,是一个泛型接口,用户可以自己定义某一事件的订阅者,注册到容器中。
spring内部定义了一个ApplicationEvent抽象类,所有spring管理的事件都实现了ApplicationEvent类,他的一个子类ApplicationContextEvent也是一个抽象类,spring又定义了几个ApplicationContextEvent的子类,用于代表spring容器生命周期的典型事件,包括ContextStartedEvent,ContextRefreshedEvent,ContextStoppedEvent,ContextClosedEvent,发布这些容器生命周期事件的方法嵌入在容器的整个生命周期中。spring的实现原来在后面的原理分析中详细讲解。
发布者,订阅者spring已经有自己的实现机制,这里我们自定义一个实现ApplicationEvent的事件类,用于测试。
public class MyEvent extends ApplicationEvent {
public MyEvent(Object source) {
super(source);
}
}
spring的事件机制有两种使用方式,分别是ApplicationListener接口方式和基于EventListener注解方式。
ApplicationListener是一个泛型接口,我们实现该接口,定义一个监听器。
@Component
public class MyApplicationListener implements ApplicationListener<MyEvent> {
@Override
public void onApplicationEvent(MyEvent event) {
System.out.println("MyEvent事件发生");
System.out.println("收到信息:" + event.getSource());
}
}
在TestSpring启动类的main方法中加入下面两行代码
MyEvent myEvent = new MyEvent(context);
context.publishEvent(myEvent);
启动TestSpring类的main方法,显示结果
MyEvent事件发生
收到信息:org.springframework.context.annotation.AnnotationConfigApplicationContext@2e0fa5d3, started on Thu Feb 04 09:47:23 CST 2021
创建一个测试类AnnotationEventTest,类中定义一个方法,注意方法必须有形参,不然会报错,方法添加EventListener注解,这就相当于监听了MyEvent事件的处理方法。
@Component
public class AnnotationEventTest {
@EventListener
public void test(MyEvent event)
{
System.out.println("事件测试:"+event);
}
}
启动TestSpring类的main方法,显示结果:
事件测试:com.li.study.event.MyEvent[source=org.springframework.context.annotation.AnnotationConfigApplicationContext@2e0fa5d3, started on Fri Feb 05 08:22:11 CST 2021]
这种基于EventListener注解的方式,省掉了定义监听器类,用起来更方便。
很多人会有疑问,spring为什么会做这种事件通知机制?我理解主要为了解耦,事件的发布者不需要知道有多少监听者在监听该事件的发生。有人说这种功能也可以通过依赖注入的方式实现,把监听逻辑的实现类注入到发布事件的对象中,直接通过方法调用即可。我只能说,呃。。。你说的对,太郭敬明了。
其实这个实现原理大家仔细一想,也很简单,无非就是spring自动识别所有的事件监听器,不管是实现ApplicationListener还是添加EventListener注解的方法,将所有的监听器注入到容器,每个监听器绑定了一个事件类型,ApplicationListener通过泛型的参数类型绑定,EventListener注解通过方法形参绑定。当容器触发事件时,遍历所有监听器,找到类型匹配的事件监听器,执行监听方法。
下面就从源码角度来分析spring的实现方式,分为两个主要步骤:事件监听器的自动注册和发布事件
前面也提到,spring的事件监听器有两种形式,实现ApplicationListener接口和标记EventListener注解的方法。
自动注册ApplicationListener接口的实现类,实现原理是BeanPostProcessor后置处理器,后置处理器的实现原理参考BeanPostProcessor后置处理器原理与应用
限于篇幅,这里不详细说明。在上面的案例调用AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);时,会启动该并刷新容器,会执行AbstractApplicationContext类的refresh() 方法,在refresh() 方法中会执行很多复杂的逻辑,这个方法时spring的合新方法。其中有一行代码prepareBeanFactory(beanFactory);我们查看一下,在代码中有一行 beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));就是向spring容器中注册了一个发现事件监听器的BeanPostProcessor。
//省略无关代码
protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
// Register early post-processor for detecting inner beans as ApplicationListeners.
beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));
}
我们看一下ApplicationListenerDetector的postProcessMergedBeanDefinition和postProcessAfterInitialization方法。为什么只关心这两个方法,这就和BeanPostProcessor的原理有关。
postProcessMergedBeanDefinition方法在MergedBeanDefinitionPostProcessor接口中定义,MergedBeanDefinitionPostProcessor是BeanPostProcessor的一个子接口,postProcessMergedBeanDefinition方法会在bean创建之后,初始化之前调用的。
postProcessAfterInitialization方法在BeanPostProcessor接口中定义,通过也可以推断,该方法是在bean初始化之后被调用的。
所以,在ApplicationListenerDetector类中,postProcessMergedBeanDefinition方法会在postProcessAfterInitialization方法之前被调用。
@Override
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
if (ApplicationListener.class.isAssignableFrom(beanType)) {
this.singletonNames.put(beanName, beanDefinition.isSingleton());
}
}
@Override
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
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;
}
在postProcessMergedBeanDefinition方法中判断bean类型是不是ApplicationListener的实现类,如果是加入到map中。在postProcessAfterInitialization方法中,通过beanName在map中将ApplicationListener的实现类取出,注册到容器的applicationListeners集合中,准备将来被调用。
EventListener注解只能标记再方法和注解上,不允许标记在类上。原理就是通过注解指定的方法,动态代理出一个ApplicationListener的实现类,来看一下具体实现。
首先我们来看一下,spring框架是怎么识别EventListener和怎么动态代理的。还是从AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);开始分析,走下面的调用链路。
AnnotationConfigApplicationContext#AnnotationConfigApplicationContext() //无参构造函数
AnnotatedBeanDefinitionReader#AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry)
AnnotatedBeanDefinitionReader#AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment)
AnnotationConfigUtils#registerAnnotationConfigProcessors(BeanDefinitionRegistry registry)
AnnotationConfigUtils#registerAnnotationConfigProcessors(BeanDefinitionRegistry registry, @Nullable Object source)
来看一下registerAnnotationConfigProcessors(BeanDefinitionRegistry registry, @Nullable Object source)方法
//省略无关代码
public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(
BeanDefinitionRegistry registry, @Nullable Object source) {
if (!registry.containsBeanDefinition(EVENT_LISTENER_PROCESSOR_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition(EventListenerMethodProcessor.class);
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_PROCESSOR_BEAN_NAME));
}
if (!registry.containsBeanDefinition(EVENT_LISTENER_FACTORY_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition(DefaultEventListenerFactory.class);
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_FACTORY_BEAN_NAME));
}
return beanDefs;
}
这里注册了两个bean,EventListenerMethodProcessor和DefaultEventListenerFactory,就是这两个bean实现了基于EventListener注解的事件机制。
看一下EventListenerMethodProcessor,他实现了BeanFactoryPostProcessor接口,是一个bean工厂的后置处理器,原理见BeanFactoryPostProcessor与BeanDefinitionRegistryPostProcessor用法与原理
我们关注postProcessBeanFactory和afterSingletonsInstantiated两个方法
postProcessBeanFactory在BeanFactoryPostProcessor接口定义,在容器将所有bean解析成beanDefinition后,实例化之前执行
afterSingletonsInstantiated在SmartInitializingSingleton接口中定义,SmartInitializingSingleton接口其实也是一种扩展机制,类似InitializingBean接口,不同之处在于SmartInitializingSingleton中的afterSingletonsInstantiated方法在bean实例化以后调用,InitializingBean的afterPropertiesSet在bean初始化时调用。
这里postProcessBeanFactory在afterSingletonsInstantiated之前调用。
@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;
}
@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 {
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) {
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);
}
}
}
}
}
postProcessBeanFactory方法先获取所有实现EventListenerFactory接口的单例对象,排序后保存在容器中。
afterSingletonsInstantiated方法执行一系列的判断,然后真正的逻辑在processBean(beanName, type);中实现
private void processBean(final String beanName, final Class<?> targetType) {
//判断类型包含注解,并且是EventListener注解
if (!this.nonAnnotatedClasses.contains(targetType) &&
AnnotationUtils.isCandidateClass(targetType, EventListener.class) &&
!isSpringContainerClass(targetType)) {
Map<Method, EventListener> annotatedMethods = null;
try {
//提取添加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);
}
}
//没有添加找到添加EventListener注解的方法对象添加到nonAnnotatedClasses集合,这里为了上面的判断使用
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");
//这里的eventListenerFactories实际上在容器启动时添加了一个默认的监听器工厂DefaultEventListenerFactory
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)) { //判断是否支持方法的注解方式,这里是DefaultEventListenerFactory的逻辑,返回的是true
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);
}
context.addApplicationListener(applicationListener);
break;
}
}
}
if (logger.isDebugEnabled()) {
logger.debug(annotatedMethods.size() + " @EventListener methods processed on bean '" +
beanName + "': " + annotatedMethods);
}
}
}
}
至此事件监听器的注册原理就输说完了,还希望大家理解,其实这些理解清楚了,整个spring的原理也比较清楚了。
相比于事件监听器原理,事件发布原理简单得多,就是直接调用事件监听器的处理方法。
通过上面案例演示的context.publishEvent(myEvent);代码开始分析。会进入到AbstractApplicationContext类的下面方法。
protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
Assert.notNull(event, "Event must not be null");
// Decorate event as an ApplicationEvent if necessary
ApplicationEvent applicationEvent;
if (event instanceof ApplicationEvent) {
applicationEvent = (ApplicationEvent) event;
}
else {
applicationEvent = new PayloadApplicationEvent<>(this, event);
if (eventType == null) {
eventType = ((PayloadApplicationEvent<?>) applicationEvent).getResolvableType();
}
}
// Multicast right now if possible - or lazily once the multicaster is initialized
if (this.earlyApplicationEvents != null) {
this.earlyApplicationEvents.add(applicationEvent);
}
else {
getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
}
// Publish event via parent context as well...
if (this.parent != null) {
if (this.parent instanceof AbstractApplicationContext) {
((AbstractApplicationContext) this.parent).publishEvent(event, eventType);
}
else {
this.parent.publishEvent(event);
}
}
}
这里主要是一些判断逻辑,我们关注getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);这里又事件广播器的概念,事件发布就是在事件广播器实现的。获取的事件广播器是在AbstractApplicationContext类的refresh()方法中调用initApplicationEventMulticaster();方法注册的,注册的类是SimpleApplicationEventMulticaster,这个方法的逻辑很简单,就不分析了。就是让大家知道时间广播器怎么来的。我们去看一下事件广播器的multicastEvent方法。
@Override
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)) {
if (executor != null) {
executor.execute(() -> invokeListener(listener, event));
}
else {
invokeListener(listener, event);
}
}
}
具体得调用逻辑在invokeListener方法
protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
//有没有异常处理,代码逻辑不一样
ErrorHandler errorHandler = getErrorHandler();
if (errorHandler != null) {
try {
doInvokeListener(listener, event);
}
catch (Throwable err) {
errorHandler.handleError(err);
}
}
else {
doInvokeListener(listener, event);
}
}
private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
try {
listener.onApplicationEvent(event); //调用监听器得处理方法,完成事件发布
}
catch (ClassCastException ex) {
String msg = ex.getMessage();
if (msg == null || matchesClassCastMessage(msg, event.getClass())) {
// Possibly a lambda-defined listener which we could not resolve the generic event type for
// -> let's suppress the exception and just log a debug message.
Log logger = LogFactory.getLog(getClass());
if (logger.isTraceEnabled()) {
logger.trace("Non-matching event type for listener: " + listener, ex);
}
}
else {
throw ex;
}
}
}
以上就是spring事件通知机制的全部内容,实现的逻辑比较清晰,但是涉及的spring源码相关的知识比较多,这些都理解清楚了,spring框架的基本功能就了解一半以上了。本人能力有限,有错误之处希望大佬指出。