【Spring源码篇】事件监听机制(@EventListener实现方式)

前言

项目里多出用到了spring的事件监听机制,然后今天无聊就翻了翻源码,看看spring底层是如何实现的。

先梳理一下,首先Ioc容器启动的时候,ApplicationContextrefresh模板方法中,initApplicationEventMulticaster()方法中那个初始化了SimpleApplicationEventMulticaster

发送事件还是使用 applicationContext.publishEvent(或者applicationEventPublisher.publishEvent),并且底层还是使用SimpleApplicationEventMulticaster发送。只是原来使用的是固定方法名称onApllicationEvent进行调用,那拿到监听的类则可以使用父类调用子类的方法就可以了。但是现在是自己写了一个随意定的名称那么怎么进行调用呢?其实自己去写框架的时候也可以思考一下,当然知道方法上有固定注解(@EventListener)则还是可以找到该方法的。

在看@EventListener之前需要先知道 继承EventListener方式在底层是怎么实现了,可以参见前一篇博客Spring源码-事件监听机制(实现EventListener接口)


一、@EventListener方式的实现

定义事件类型,这里的SysUser对象省略

/**
 * @author ZhuZiKai
 * @Description 发送消息事件
 * @date 2021-05-28 16:40
 */
public class SendMobileMsgEvent extends ApplicationEvent {
    private SysUser sysUser;

    public SendMobileMsgEvent(Object source, SysUser sysUser) {
        super(source);
        this.sysUser= sysUser;
    }

    public SysUser getSysUser () {
        return sysUser;
    }

    public void setSysUser (SysUser sysUser) {
        this.sysUser= sysUser;
    }
}

两种发送事件的方式:

@Service("eventUserService")
public class UserService implements ApplicationContextAware, ApplicationEventPublisherAware {
 
    private ApplicationContext applicationContext;
    private ApplicationEventPublisher applicationEventPublisher;
 
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
 
    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
        this.applicationEventPublisher = applicationEventPublisher;
    }
 
    public BaseResp addUser(SysUser sysUser) {
        // 新增用户
        {
        .....
        }
        
        // 发生事件(发邮件、发短信、、、)
        applicationContext.publishEvent(new SendMobileMsgEvent(sysUser));
        // 两种发生方式一致
        applicationEventPublisher.publishEvent(new SendMobileMsgEvent(sysUser));
        
        return new BaseResp<>(ResultStatus.SUCCESS);;
    }
}

@EvnetListener监听实现

@Component
public class UserListener {
 
 	@Async
    @EventListener
    public void getUserEvent(UserEvent userEvent) {
        System.out.println("getUserEvent-接受到事件:" + userEvent);
    }
   
    @Async
    @EventListener
    public void getUserEvent2(UserEvent userEvent) {
 
        System.out.println("getUserEvent2-接受到事件:" + userEvent);
    }
}

测试

@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = KevinToolApplication.class )
public class AnnotationEventListenerTest {
 
    @Autowired
    private UserService userService;
 
    @Test
    public void annotationEventTest() {
        userService.addUser(new SysUser());
    }
}



二、@EventListener方式的源码分析

@EventListener做什么了

@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 "";
}

该注解可以定义在方法或者上,可以定义监听的Class,可以定义监听的条件(Spring EL表达式)。那么问题来了,定义了Class当然可以找到是谁发送事件过来,没有定义呢(可能是通过方法发入参,因为事件可以定义ApplicationEvent或者Object类型)。

如果idea导入了source和document(个人比较喜欢),则在注解中可以看见@see EventListenerMethodProcessor,结构如下:

public class EventListenerMethodProcessor
		implements SmartInitializingSingleton, ApplicationContextAware, BeanFactoryPostProcessor {
 
	protected final Log logger = LogFactory.getLog(getClass());
 
	@Nullable
	private ConfigurableApplicationContext applicationContext;
 
	@Nullable
	private ConfigurableListableBeanFactory beanFactory;
 
	@Nullable
	private List<EventListenerFactory> eventListenerFactories;
 
	private final EventExpressionEvaluator evaluator = new EventExpressionEvaluator();
 
	private final Set<Class<?>> nonAnnotatedClasses = Collections.newSetFromMap(new ConcurrentHashMap<>(64));
 
 
	@Override
	public void setApplicationContext(ApplicationContext applicationContext) {
		Assert.isTrue(applicationContext instanceof ConfigurableApplicationContext,
				"ApplicationContext does not implement ConfigurableApplicationContext");
		this.applicationContext = (ConfigurableApplicationContext) applicationContext;
	}
 
	@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);
					}
				}
			}
		}
	}
 
	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 {
				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));
							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);
				}
			}
		}
	}
 
	/**
	 * Determine whether the given class is an {@code org.springframework}
	 * bean class that is not annotated as a user or test {@link Component}...
	 * which indicates that there is no {@link EventListener} to be found there.
	 * @since 5.1
	 */
	private static boolean isSpringContainerClass(Class<?> clazz) {
		return (clazz.getName().startsWith("org.springframework.") &&
				!AnnotatedElementUtils.isAnnotated(ClassUtils.getUserClass(clazz), Component.class));
	}
 
}

实现了三个接口:

1)、实现了 ApplicationContextAware 接口将其注入进来

2)、实现了BeanFactoryPostProcessor接口,实现方法如下(只是没想通有ApplicationContext则beanFactory的功能都有了,为什么对实现一个接口,可能是执行时机也可能是觉得工厂干工厂的事好理解):

@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;
}

获取容器中所有EventBeanFactory或子类的bean,进行排序后存放到eventListenerFactories,这里拿到了DefaultEventListenerFactory这个非常的关键,在哪里注入的后续梳理。当然如果我们还添加了注解@TransactionalEventListener肯定还会有TransactionalEventListenerFactory

3)、实现了SmartInitializingSingleton接口,则在所以非抽象、非懒加载的单利都getBean完成后,才会调用afterSingletonsInstantiated方法,这也算是SmartInitializingSingleton的使用场景分析(容器级别的处理)。主要逻辑也在这里。

@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);
                }
            }
        }
    }
}

String[] beanNames = beanFactory.getBeanNamesForType(Object.class);

很暴力的获取容器中所以的bean,并且进行遍历(总会找到我想要的)

AutoProxyUtils.determineTargetClass

根据bean的名称获取bean的Class<?>,当然还考虑代理对象和继承等情况,最好获取当然的Class,调processBean(beanName, type)方法。

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 {
			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));
						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);
			}
		}
	}
}

1、进来先判断,在nonAnnotatedClasses中没出现过,后面会往里注入值。并且类上或者方法上有EventListener注解。

2、获取注解的方法map,key就是我们写的两个方法,value就是EventListener和上面的参数信息

annotatedMethods = MethodIntrospector.selectMethods(targetType,
   (MethodIntrospector.MetadataLookup<EventListener>) method ->
	AnnotatedElementUtils.findMergedAnnotation(method, EventListener.class));

3、有可能获取到没有标注注解的方法,则在这里加到上面判断的nonAnnotatedClasses中,提高效率,因为拿了所有的bean。 比如spring boot的启动类就被加进去了。

4、下面就比较清楚了,遍历标注EventListener注解的方法,遍历工厂,最主要的是:

ApplicationListener<?> applicationListener =
	factory.createApplicationListener(beanName, targetType, methodToUse);

有不同的工厂创建不同的适配器对象(这里有简单工厂模式和适配器模式不知道理解对不),调用到DefaultEventListenerFactory的方法,这个地方非常关键:

@Override
public ApplicationListener<?> createApplicationListener(String beanName, 
    Class<?> type, Method method) {
		return new ApplicationListenerMethodAdapter(beanName, type, method);
}

这里返回了一个ApplicationListenerMethodAdapter对象(基础自EventListener),内部的method属性就是我自己写的添加了@EventListener的方法。并且将该listener放入Spring容器中。调用的是AbstractApplicationContext的方法,如下:

@Override
public void addApplicationListener(ApplicationListener<?> listener) {
    Assert.notNull(listener, "ApplicationListener must not be null");
    if (this.applicationEventMulticaster != null) {
        this.applicationEventMulticaster.addApplicationListener(listener);
    }
    this.applicationListeners.add(listener);
}

这样就将使用@EventListener注解的方法使用包装的方式放入了SimpleApplicationEventMulticasterdefaultRetriever.applicationListeners中,在后续发送事件时 获取监听器列表就能获取到了。


三、总结(与上面相同和不同之处)

相同:

1、ApplicationContext的refresh方法还是初始化了SimpleApplicationEventMulticaster

2、发送事件式还是先获取ResolvableType类型,再获取发送监听列表

不同:

1、获取监听列表返回的已经是处理过的列表。

2、添加了@EventListener注解的自定义名称的方法,会在EventListenerMethodProcessor中的afterSingletonsInstantiated()方法中遍历所有 ApplicationContext容器的单利bean。将所有添加了@EventListener的方法注入到ApplicationContextapplicationListeners和初始化的SimpleApplicationEventMulticasterdefaultRetriever.applicationListeners中,在发送事件时候获取监听列表时用。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
@EventListener 和 ApplicationListener 都是用于监听 Spring 应用程序中的事件的机制,但它们有一些不同之处。 1. 监听的事件类型 @EventListener 注解可以用于监听任何类型的事件,包括 Spring 框架的事件和自定义事件。而 ApplicationListener 接口只能监听 Spring 框架提供的事件。因此,如果我们需要监听自定义事件,@EventListener 是更好的选择。 2. 监听方法的参数 @EventListener 注解的监听方法可以接收事件实例作为参数,而 ApplicationListener 接口的 onApplicationEvent 方法只能接收 ApplicationEvent 的子类实例作为参数。因此,如果我们需要获取更具体的事件信息,@EventListener 是更好的选择。 3. 监听方法的声明方式 @EventListener 注解的监听方法可以在任何 Spring Bean 中声明,而 ApplicationListener 接口需要实现 ApplicationListener 接口并将其注册为 Spring Bean。因此,如果我们需要在多个 Bean 中声明监听方法,@EventListener 是更好的选择。 4. 执行顺序 使用 @EventListener 注解声明的监听方法的执行顺序是不确定的,而 ApplicationListener 是按照注册顺序依次执行的。因此,在需要按照特定顺序执行监听方法时,ApplicationListener 是更好的选择。 总的来说,@EventListener 和 ApplicationListener 都是很好的监听事件的机制,具体使用哪一个取决于我们的具体需求。如果需要监听 Spring 框架提供的事件或希望按照注册顺序执行监听方法,则选择 ApplicationListener。如果需要监听自定义事件或获取更具体的事件信息,则选择 @EventListener

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_Romeo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值