【spring】Spring事件监听器ApplicationListener的使用与源码分析

【spring】Spring事件监听器ApplicationListener的使用与源码分析ApplicationEvent以及Listener是Spring为我们提供的一个事件监听、订阅的实现,内部实现原理是观察者设计模式,设计初衷也是为了系统业务逻辑之间的解耦,提高可扩展性以及可维护性。事件发布者并不需要考虑谁去监听,监听具体的实现内容是什么,发布者的工作只是为了发布事件而已。Spring提供的内置事件:ContextRefreshedEvent:容器刷新事件ContextStartedEvent:容器启动事件ContextStoppedEvent:容器停止事件ContextClo

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全家桶1年46,售后保障稳定

ApplicationEvent以及Listener是Spring为我们提供的一个事件监听、订阅的实现,内部实现原理是观察者设计模式,设计初衷也是为了系统业务逻辑之间的解耦,提高可扩展性以及可维护性。事件发布者并不需要考虑谁去监听,监听具体的实现内容是什么,发布者的工作只是为了发布事件而已。

Spring提供的内置事件:

  • ContextRefreshedEvent:容器刷新事件
  • ContextStartedEvent:容器启动事件
  • ContextStoppedEvent:容器停止事件
  • ContextClosedEvent:容器关闭事件

如何使用

监听容器的刷新事件

自定义一个ApplicationListener,指定监听的事件类型ContextRefreshedEvent:

package com.morris.spring.listener;

import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;

public class ContextRefreshedListener implements ApplicationListener<ContextRefreshedEvent> { 
   

	@Override
	public void onApplicationEvent(ContextRefreshedEvent event) { 
   
		System.out.println("context refresh");
	}
}

Jetbrains全家桶1年46,售后保障稳定

注入到容器中:

AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
applicationContext.register(ContextRefreshedListener.class);
applicationContext.refresh(); 

applicationContext.refresh()内部会发送容器刷新的事件。

自定义事件

自定义的事件需要继承ApplicationEvent:

package com.morris.spring.event;

import org.springframework.context.ApplicationEvent;

public class CustomEvent extends ApplicationEvent { 
   

	public CustomEvent(Object source) { 
   
		super(source);
	}
}

监听的时候使用ApplicationEvent的子类CustomEvent:

package com.morris.spring.listener;

import com.morris.spring.event.CustomEvent;
import org.springframework.context.ApplicationListener;

public class CustomEventListener implements ApplicationListener<CustomEvent> { 
   

	@Override
	public void onApplicationEvent(CustomEvent event) { 
   
		System.out.println("custom event: " + event.getSource());
	}
}

可以使用AnnotationConfigApplicationContext发布事件:

AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
applicationContext.register(CustomEventListener.class);
applicationContext.refresh();
applicationContext.publishEvent(new CustomEvent("custom event"));

可以向bean中注入一个ApplicationEventPublisher来发布事件:

package com.morris.spring.service;

import com.morris.spring.event.CustomEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;

public class CustomEventService { 
   

	@Autowired
	private ApplicationEventPublisher applicationEventPublisher;

	public void publishEvent() { 
   
		applicationEventPublisher.publishEvent(new CustomEvent("自定义事件"));
	}
}

可以通过实现ApplicationEventPublisherAware接口注入ApplicationEventPublisher来发布事件:

package com.morris.spring.service;

import com.morris.spring.event.CustomEvent;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;

public class CustomEventService2 implements ApplicationEventPublisherAware { 
   

	private ApplicationEventPublisher applicationEventPublisher;

	public void publishEvent() { 
   
		applicationEventPublisher.publishEvent(new CustomEvent("自定义事件"));
	}

	@Override
	public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) { 
   
		this.applicationEventPublisher = applicationEventPublisher;
	}
}

由于ApplicationContext实现了ApplicationEventPublisher接口,也可以直接注入ApplicationContext来发布事件。

使用@EventListener监听事件

在监听事件时,由于类需要实现ApplicationListener接口,对代码有很大的侵入性,可以使用@EventListener注解随时随地监听事件,这样一个Service中可以监听多个事件:

package com.morris.spring.listener;

import com.morris.spring.event.CustomEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.context.event.EventListener;

public class CustomEventListener2 { 
   

	@EventListener
	public void listenContextRefreshedEvent(ContextRefreshedEvent event) { 
   
		System.out.println("context refresh");
	}

	@EventListener
	public void listenCustomEvent(CustomEvent event) { 
   
		System.out.println("custom event: " + event.getSource());
	}

}

还可以在@EventListener注解上指定监听的事件类型:

package com.morris.spring.listener;

import com.morris.spring.event.CustomEvent;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.context.event.EventListener;

public class CustomEventListener3 { 
   

	@EventListener({ 
   ContextRefreshedEvent.class, CustomEvent.class})
	public void listenEvent(ApplicationEvent event) { 
   
		System.out.println(event);
	}
}

异步发送消息

spring消息的发送默认都是同步的,如果要异步发送消息,首先要在配置类上开启异步功能@EnableAsync:

package com.morris.spring.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;

@Configuration
@EnableAsync // 开启异步
public class EventListenerConfig { 
   
}

在监听的方法上加上@Async:

package com.morris.spring.listener;

import com.morris.spring.event.CustomEvent;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;

@Slf4j
public class AsyncCustomEventListener { 
   
	@EventListener({ 
   ContextRefreshedEvent.class, CustomEvent.class})
	@Async // 异步
	public void listenEvent(ApplicationEvent event) { 
   
		log.info("receive event: {}", event);
	}
}

也可以自定义执行异步消息的线程池(默认就是SimpleAsyncTaskExecutor):

@Bean
public TaskExecutor executor() { 
   
	return new SimpleAsyncTaskExecutor("eventListen-");
}

异步消息只是借用spring的异步执行机制,在方法上加上@Async注解,方法都会异步执行。

ApplicationListener原理分析

发布消息的入口

发布消息入口:
org.springframework.context.support.AbstractApplicationContext#publishEvent(org.springframework.context.ApplicationEvent)

public void publishEvent(ApplicationEvent event) { 
   
    publishEvent(event, null);
}

protected void publishEvent(Object event, @Nullable ResolvableType eventType) { 
   
...
        /** * @see SimpleApplicationEventMulticaster#multicastEvent(org.springframework.context.ApplicationEvent, org.springframework.core.ResolvableType) */
        // 发布消息
        getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
    }
... ...
}

然后调用SimpleApplicationEventMulticaster来进行广播消息:

// org.springframework.context.event.SimpleApplicationEventMulticaster#multicastEvent(org.springframework.context.ApplicationEvent, org.springframework.core.ResolvableType)
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);
        }
    }
}

protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) { 
   
    ErrorHandler errorHandler = getErrorHandler();
    if (errorHandler != null) { 
   
        try { 
   
            doInvokeListener(listener, event);
        }
        catch (Throwable err) { 
   
            errorHandler.handleError(err);
        }
    }
    else { 
   
        // ApplicationListener.调用onApplicationEvent
        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;
        }
    }
}

何时注入SimpleApplicationEventMulticaster

从上面的源码可以发现spring是通过SimpleApplicationEventMulticaster事件多播器来发布消息的,那么这个类是何时注入的呢?容器refresh()时。

org.springframework.context.support.AbstractApplicationContext#initApplicationEventMulticaster

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 { 
   
		// 直接new,然后放入到spring一级缓存中
		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() + "]");
		}
	}
}

何时注入ApplicationListener

spring在发布消息时,会从SimpleApplicationEventMulticaster中拿出所有的ApplicationListener,那么这些ApplicationListener何时被注入的呢?容器refresh()时。

org.springframework.context.support.AbstractApplicationContext#registerListeners

protected void registerListeners() { 
   
	// Register statically specified listeners first.
	for (ApplicationListener<?> listener : getApplicationListeners()) { 
   
		getApplicationEventMulticaster().addApplicationListener(listener);
	}

	// Do not initialize FactoryBeans here: We need to leave all regular beans
	// uninitialized to let post-processors apply to them!
	String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
	for (String listenerBeanName : listenerBeanNames) { 
   
		// 添加到SimpleApplicationEventMulticaster中
		getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
	}

	// Publish early application events now that we finally have a multicaster...
	Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents;
	this.earlyApplicationEvents = null;
	if (!CollectionUtils.isEmpty(earlyEventsToProcess)) { 
   
		for (ApplicationEvent earlyEvent : earlyEventsToProcess) { 
   
			getApplicationEventMulticaster().multicastEvent(earlyEvent);
		}
	}
}

@EventListener的原理

@EventListener注解的功能是通过EventListenerMethodProcessor来实现的,EventListenerMethodProcessor这个类在AnnotationConfigApplicationContext的构造方法中被注入。

EventListenerMethodProcessor主要实现了两个接口:SmartInitializingSingleton和BeanFactoryPostProcessor。

先来看看BeanFactoryPostProcessor的postProcessBeanFactory(),这个方法主要是保存beanFactory和eventListenerFactories,后面的方法将会使用到:

public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) { 
   
    // 保存beanFactory
    this.beanFactory = beanFactory;

    /** * EventListenerFactory[DefaultEventListenerFactory]在何处被注入? * @see AnnotationConfigUtils#registerAnnotationConfigProcessors(org.springframework.beans.factory.support.BeanDefinitionRegistry, java.lang.Object) */
    Map<String, EventListenerFactory> beans = beanFactory.getBeansOfType(EventListenerFactory.class, false, false);
    List<EventListenerFactory> factories = new ArrayList<>(beans.values());
    AnnotationAwareOrderComparator.sort(factories);
    // 保存eventListenerFactories
    this.eventListenerFactories = factories;
}

再来看看SmartInitializingSingleton的afterSingletonsInstantiated()方法,这个方法会在所有的bean初始化完后执行。

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);
				}
			}
		}
	}
}
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 { 
   
			// 获得类中所有的带有@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");
			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));
						// 使用factory创建一个ApplicationListener
						ApplicationListener<?> applicationListener =
								factory.createApplicationListener(beanName, targetType, methodToUse);
						if (applicationListener instanceof ApplicationListenerMethodAdapter) { 
   
							((ApplicationListenerMethodAdapter) applicationListener).init(context, this.evaluator);
						}
						// 将ApplicationListener添加到Spring容器中
						context.addApplicationListener(applicationListener);
						break;
					}
				}
			}
			if (logger.isDebugEnabled()) { 
   
				logger.debug(annotatedMethods.size() + " @EventListener methods processed on bean '" +
						beanName + "': " + annotatedMethods);
			}
		}
	}
}

SpringBoot中的事件

SpringBoot在启动时会按以下顺序发送消息:

  1. ApplicationStartingEvent:在运行开始时发送 ,但在进行任何处理之前(侦听器和初始化程序的注册除外)发送
  2. ApplicationEnvironmentPreparedEvent:当被发送Environment到中已知的上下文中使用,但是在创建上下文之前
  3. ApplicationContextInitializedEvent:在ApplicationContext准备好且已调用ApplicationContextInitializers之后但任何bean定义未加载之前发送
  4. ApplicationPreparedEvent:在刷新开始之前但在加载bean定义之后发送
  5. ApplicationStartedEvent:上下文已被刷新后发送,但是任何应用程序和命令行都被调用前
  6. ApplicationReadyEvent:在所有的命令行应用启动后发送此事件,可以处理请求
  7. ApplicationFailedEvent:在启动时异常发送
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/223017.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • LabVIEW图像灰度分析与变换(基础篇—4)

    LabVIEW图像灰度分析与变换(基础篇—4)图像分析是将图像的像素灰度统计和测量技术结合,使机器可以理解图像内容,并提取特征信息以实现智能检测目的的学科。图像分析更侧重于对图像内容的分析、解释和识别。

  • linux删除软连接命令_linux删除链接文件夹

    linux删除软连接命令_linux删除链接文件夹linux删除软链接的正确做法

  • Java中List的详细用法

    Java中List的详细用法目录:list中添加,获取,删除元素;list中是否包含某个元素;list中根据索引将元素数值改变(替换);list中查看(判断)元素的索引;根据元素索引位置进行的判断;利用list中索引位置重新生成一个新的list(截取集合);对比两个list中的所有元素;判断list是否为空;返回Iterator集合对象;将集合转换为字符串;将集合转换为数组;集…

  • tf卡锁定怎么解锁_tf卡写保护解除办法

    tf卡锁定怎么解锁_tf卡写保护解除办法最近这段时间,开始使用TFS2010进行项目的源码管理,在使用过程中,发现了不些问题,由于开发人员的操作失误,经常导致了源码中的文件被锁定,而在开发人员的PC机上又不能把锁定的文件签入到TFS中,刚开始遇到这个问题时,想通过管理员帐号来“取消锁定”,但在“源代码管理器”中,管理员帐号也没有权限操作“取消锁定”。遇到这种文件被锁定的事,是相当的郁闷。郁闷归郁闷,问题还是得要解决,…

  • 40个容易上瘾的HTML5网页游戏,总有一款适合你[通俗易懂]

    40个容易上瘾的HTML5网页游戏,总有一款适合你[通俗易懂]我记得姐姐家的孩子在刚刚才学会走路,说话还不能完整的时候就已经能自己用小手点出小游戏的网站来一个人自娱自乐。我一直在想这一代跟着计算机一起茁壮成长的孩子会不会也和美国那一代人一样,出现9岁的黑客和计算机天才。但是并不是信息的成长就能让教育同步。很多时候我们会发现教育在发展的大环境中并没有什么创新的思考。不管怎么说,我们还是需要小盆友们能有足够的想象力。不要被束缚!今天分享的是40个HTML5的网页

  • mongodb条件查询语句_linux基本命令的使用

    mongodb条件查询语句_linux基本命令的使用1、字段匹配举例:查询“_id”字段值为5980690eceab061b1613e594的数据。命令:{‘_id’:ObjectId(‘5980690eceab061b1613e594’)}  2、显示指定字段举例:显示“_id”“tag”两个字段。命令:{_id:”,tag:”}查询结果:  3、字段包含举例:查询tag(数组)字段包含“家”的数据。命令:{‘tag’:{$regex:’…

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号