一、简介
@EventListener是一种事件驱动编程在spring4.2的时候开始有的,早期可以实现ApplicationListener接口, 想了解下ApplicationListener的可以参考下这篇文章https://blog.csdn.net/baidu_19473529/article/details/86683365 Spring为我们提供的一个事件监听、订阅的实现,内部实现原理是观察者设计模式;为的就是业务系统逻辑的解耦,提高可扩展性以及可维护性。事件发布者并不需要考虑谁去监听,监听具体的实现内容是什么,发布者的工作只是为了发布事件而已。 比如我们做一个电商系统,用户下单支付成功后,我们一般要发短信或者邮箱给用户提示什么的,这时候就可以把这个通知业务做成一个单独事件监听,等待通知就可以了;把它解耦处理。
二、使用@EventListener注解
建立事件对象,当调用publishEvent方法是会通过这个bean对象找对应事件的监听。AddDataEvent.java
package com.rw .article .pay .event .bean ;
import org.springframework .context .ApplicationEvent ;
public class AddDataEvent extends ApplicationEvent {
public AddDataEvent (Object source) {
super (source);
}
public AddDataEvent (Object source, Class clz, Object data) {
super (source);
this .clz = clz;
this .data = data;
}
public AddDataEvent (Object source, Class clz, Object data, String modelName, String userAgent) {
super (source);
this .clz = clz;
this .data = data;
this .modelName = modelName;
this .userAgent = userAgent;
}
private Class clz;
private Object data;
private String modelName;
private String userAgent;
public Class getClz ( ) {
return clz;
}
public void setClz (Class clz ) {
this .clz = clz;
}
public Object getData ( ) {
return data;
}
public void setData (Object data ) {
this .data = data;
}
public String getModelName ( ) {
return modelName;
}
public void setModelName (String modelName ) {
this .modelName = modelName;
}
public String getUserAgent ( ) {
return userAgent;
}
public void setUserAgent (String userAgent ) {
this .userAgent = userAgent;
}
}
对应的监听AddDataEventListener .java
package com.rw.article.pay.event.listener;
import com.alibaba.fastjson.JSON;
import com.rw.article.pay.event.bean.AddDataEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
@Component
public class AddDataEventListener {
private static Logger log = LoggerFactory.getLogger(AddDataEventListener.class);
@EventListener
public void handleAddEvent (AddDataEvent event) {
log.info("发布的data为:{} " , JSON.toJSONString(event));
}
}
package com.rw.article.pay.action;
import com.rw.article.pay.event.bean.AddDataEvent;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation .RequestMapping;
import org.springframework.web.bind.annotation .ResponseBody;
import javax.annotation .Resource;
@Controller
@RequestMapping("/test" )
public class TestController {
@Resource
private ApplicationContext applicationContext;
@ResponseBody
@RequestMapping("/testListener" )
public String testListener(){
applicationContext.publishEvent(new AddDataEvent(this ,TestController.class ,"test" ));
return "success" ;
}
}
结果是能够监听到的 如果要使用异步加上@EnableAsync
注解,方法上加@Async
注解,如下spring boot项目配置
@SpringBootApplication
@EnableAsync
public class XApplication{
public static void main(String[] args) {
ConfigurableApplicationContext run = new SpringApplicationBuilder(XApplication.class).web(true ).run(args);
run.publishEvent("test" );
}
}
@Async
@EventListener
public void test (String wrapped){
System.out.println("当前线程 " +Thread.currentThread().getName());
System.out.println(wrapped.getMchId());
}
@Configuration
public class GenericConfiguration {
@Bean
public Executor taskExecutor () {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
//核心线程数:线程池创建时候初始化的线程数
//最大线程数:线程池最大的线程数,只有在缓冲队列满了之后才会申请超过核心线程数的线程
//缓冲队列:用来缓冲执行任务的队列
//允许线程的空闲时间60秒:当超过了核心线程出之外的线程在空闲时间到达之后会被销毁
//线程池名的前缀:设置好了之后可以方便我们定位处理任务所在的线程池
//线程池对拒绝任务的处理策略:这里采用了CallerRunsPolicy策略,当线程池没有处理能力的时候,该策略会直接在 execute 方法的调用线程中运行被拒绝的任务;如果执行程序已关闭,则会丢弃该任务
executor.setCorePoolSize(5);
executor.setMaxPoolSize(10);
executor.setQueueCapacity(20);
executor.setKeepAliveSeconds(60);
executor.setThreadNamePrefix("taskExecutor-" );
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
return executor;
}
}
三、源码解析
原理还得从org.springframework.context.event.internalEventListenerProcessor 说起。 在AnnotationConfigUtils#registerAnnotationConfigProcessors注册了BeanDefinition 对应的是EventListenerMethodProcessor对象 , 而AnnotationConfigUtils是在AnnotationConfigServletWebServerApplicationContext构造方法里被加载。这里要提一下AnnotationConfigServletWebServerApplicationContext,他是spring boot启动入口的重要类(我这里用的是spring boot所以是这个类),可以相当于以前用xml的ClassPathXmlApplicationContext。
public static final String EVENT_LISTENER_PROCESSOR_BEAN_NAME =
"org.springframework.context.event.internalEventListenerProcessor" ;
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 ));
}
...........................
return beanDefs;
}
注册的EventListenerMethodProcessor对象会在初始化非懒加载对象的时候运行它的afterSingletonsInstantiated方法。 AbstractApplicationContext#finishBeanFactoryInitialization
protected void finishBeanFactoryInitialization (ConfigurableListableBeanFactory beanFactory ) {
.............
beanFactory.preInstantiateSingletons ();
}
DefaultListableBeanFactory#preInstantiateSingletons
@Override
public void preInstantiateSingletons () throws BeansException {
..................
for (String beanName : beanNames) {
Object singletonInstance = getSingleton(beanName);
if (singletonInstance instanceof SmartInitializingSingleton) {
final SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;
if (System.getSecurityManager() != null ) {
AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
smartSingleton.afterSingletonsInstantiated();
return null ;
}, getAccessControlContext());
}
else {
smartSingleton.afterSingletonsInstantiated();
}
}
}
}
EventListenerMethodProcessor#afterSingletonsInstantiated
@Override
public void afterSingletonsInstantiated ( ) {
List <EventListenerFactory > factories = getEventListenerFactories ();
ConfigurableApplicationContext context = getApplicationContext ();
String [] beanNames = context.getBeanNamesForType (Object .class );
for (String beanName : beanNames) {
if (!ScopedProxyUtils .isScopedTarget (beanName)) {
Class <?> type = null ;
try {
type = AutoProxyUtils .determineTargetClass (context.getBeanFactory (), beanName);
}
catch (Throwable ex) {
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 (
context.getBeanFactory (), ScopedProxyUtils .getTargetBeanName (beanName));
if (targetClass != null ) {
type = targetClass;
}
}
catch (Throwable ex) {
if (logger.isDebugEnabled ()) {
logger.debug ("Could not resolve target bean for scoped proxy '" + beanName + "'" , ex);
}
}
}
try {
processBean (factories, beanName, type );
}
catch (Throwable ex) {
throw new BeanInitializationException ("Failed to process @EventListener " +
"annotation on bean with name '" + beanName + "'" , ex);
}
}
}
}
}
EventListenerMethodProcessor#processBean;这里有一个重要的类就是ApplicationListenerMethodAdapter,spring把加入了@EventListener注解的方法封装进ApplicationListenerMethodAdapter对象里,然后我们publishEvent方法是,其实是调用的对应的ApplicationListenerMethodAdapter,然后里面是执行这个方法,这里可以看下ApplicationListenerMethodAdapter类的属性。
public class ApplicationListenerMethodAdapter implements GenericApplicationListener {
protected final Log logger = LogFactory.getLog(getClass());
private final String beanName;
private final Method method;
private final Method targetMethod;
private final AnnotatedElementKey methodKey;
private final List<ResolvableType> declaredEventTypes;
@Nullable
private final String condition;
private final int order;
@Nullable
private ApplicationContext applicationContext;
@Nullable
private EventExpressionEvaluator evaluator;
..................................
}
protected void processBean(
final List<EventListenerFactory> factories, final String beanName, final Class<?> targetType) {
if (!this .nonAnnotatedClasses.contains(targetType)) {
Map<Method, EventListener> annotatedMethods = null ;
try {
annotatedMethods = MethodIntrospector.selectMethods(targetType,
(MethodIntrospector.MetadataLookup<EventListener>) method ->
AnnotatedElementUtils.findMergedAnnotation(method, EventListener.class ));
}
catch (Throwable ex) {
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 {
ConfigurableApplicationContext context = getApplicationContext();
for (Method method : annotatedMethods.keySet()) {
for (EventListenerFactory factory : factories) {
if (factory.supportsMethod(method)) {
Method methodToUse = AopUtils.selectInvocableMethod(method, context.getType(beanName));
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);
}
}
}
}
后面就是触发事件监听了AbstractApplicationContext#publishEvent
@Override
public void publishEvent (ApplicationEvent event ) {
publishEvent(event , null );
}
protected void publishEvent (Object event , @Nullable ResolvableType eventType ) {
..............................
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 );
}
}
}
SimpleApplicationEventMulticaster#multicastEvent->invokeListener->doInvokeListener
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().getName())) {
Log logger = LogFactory.getLog(getClass());
if (logger.isDebugEnabled()) {
logger.debug("Non-matching event type for listener: " + listener, ex);
}
}
else {
throw ex;
}
}
}
ApplicationListenerMethodAdapter#onApplicationEvent
@Override
public void onApplicationEvent (ApplicationEvent event ) {
processEvent(event );
}
ApplicationListenerMethodAdapter#processEvent
public void processEvent (ApplicationEvent event ) {
Object[] args = resolveArguments(event );
if (shouldHandle(event , args)) {
Object result = doInvoke(args);
if (result != null ) {
handleResult(result);
}
else {
logger.trace("No result object given - no result to handle" );
}
}
}
ApplicationListenerMethodAdapter#doInvoke
protected Object doInvoke (Object ... args ) {
Object bean = getTargetBean ();
ReflectionUtils .makeAccessible (this .method );
try {
return this .method .invoke (bean, args);
}
catch (IllegalArgumentException ex) {
assertTargetBean (this .method , bean, args);
throw new IllegalStateException (getInvocationErrorMessage (bean, ex.getMessage (), args), ex);
}
catch (IllegalAccessException ex) {
throw new IllegalStateException (getInvocationErrorMessage (bean, ex.getMessage (), args), ex);
}
catch (InvocationTargetException ex) {
Throwable targetException = ex.getTargetException ();
if (targetException instanceof RuntimeException ) {
throw (RuntimeException ) targetException;
}
else {
String msg = getInvocationErrorMessage (bean, "Failed to invoke event listener method" , args);
throw new UndeclaredThrowableException (targetException, msg);
}
}
}
ApplicationListenerMethodAdapter#getTargetBean
protected Object getTargetBean() {
Assert.notNull(this .applicationContext, "ApplicationContext must no be null" );
return this .applicationContext.getBean(this .beanName);
}
至此执行这个事件监听的方法执行完毕。如果文字有误的地方,希望批评指正,感谢您的观看。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现