您现在的位置是:亿华云 > 应用开发
Spring-Context注解源码之@EventListener
亿华云2025-10-09 03:58:26【应用开发】0人已围观
简介注解说明Annotation that marks a method as a listener for application events.以注解的方式将一个方法标记为事件监听器。如果对于spri
注解说明
Annotation that marks a method as a listener for application events.
以注解的解源方式将一个方法标记为事件监听器。如果对于spring事件监听机制还不了解的解源小伙伴点击查看一文彻底搞懂spring事件监听机制
属性说明
public @interface EventListener { /** * 同class */ @AliasFor("classes") Class<?>[] value() default { }; /** * 监听事件的类型 * 如果这个属性长度不为空,则以这个属性的解源为准 * 如果这个属性长度为空,则以被标注方法的解源参数为准 */ @AliasFor("value") Class<?>[] classes() default { }; /** * 以spring表达的方式计算事件监听是否需要触发 */ String condition() default ""; }通过上述属性,我们可以发现,解源相比起实现接口的解源方式创建事件监听器,用注解的解源方式灵活性更加大,不仅可以指定多个接受事件类型,解源还可以增加是解源否触发的条件。
使用示例
@EventListener public void customListener1(MyEvent event) { System.out.println("接受事件customListener1"); }相关源码
EventListenerMethodProcessor
/** * 检测bean里面是解源否包含 @EventListener */ private void processBean(final String beanName, final Class<?> targetType) { if (!this.nonAnnotatedClasses.contains(targetType) && !targetType.getName().startsWith("java") && !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 - lets ignore it. if (logger.isDebugEnabled()) { logger.debug("Could not resolve methods for bean with name " + beanName + "", ex); } } if (CollectionUtils.isEmpty(annotatedMethods)) { // 如果这个类一个包含 @EventListener 方法都没有则缓存到 nonAnnotatedClasses 中,减少重复计算 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"); // 可以创建自定义 EventListenerFactory,如果不创建,解源默认拥有 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)) { // 根据方法创建 applicationListener,服务器租用并将 applicationListener 添加给容器 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); } } } }在
EventListenerMethodProcessor的processBean方法中,会遍历已经注册的所有的bean,找到包含有被 @EventListener 标注的方法。这些方法会被遍历已经创建的 EventListenerFactory 找到合适的工厂来生成 applicationListener,并将 applicationListener 注册到容器的事件监听器列表。
ApplicationListenerMethodAdapter
/** * 解析时间监听器支持的事件类型 */ private static List<ResolvableType> resolveDeclaredEventTypes(Method method, @Nullable EventListener ann) { int count = method.getParameterCount(); if (count > 1) { // 如果方法本身参数超过1个,则直接抛出异常 throw new IllegalStateException( "Maximum one parameter is allowed for event listener method: " + method); } if (ann != null) { // 取出 注解中的 classes属性 Class<?>[] classes = ann.classes(); if (classes.length > 0) { // 如果classes属性不为空,则解析classes属性并返回作为事件解析类型 List<ResolvableType> types = new ArrayList<>(classes.length); for (Class<?> eventType : classes) { types.add(ResolvableType.forClass(eventType)); } return types; } } // 如果传入的classes属性为空,并且方法没有参数,也抛出异常 if (count == 0) { throw new IllegalStateException( "Event parameter is mandatory for event listener method: " + method); } return Collections.singletonList(ResolvableType.forMethodParameter(method, 0)); }ApplicationListenerMethodAdapter的resolveDeclaredEventTypes方法会解析@EventListener标签的classes属性,然后根据这个属性决定事件监听器的监听的事件类型。
如果方法参数个数超过1个,直接抛出异常。这是源码库一个事件触发以后,如果接受的方法参数个数大于1个,spring没办法给方法进行传参。
如果classes属性为空,并且方法参数个数为0个,也抛出异常。这是因为spring无法推断这个监听器需要支持什么类型的事件。
除去上面两种情况,解析都是成功,同时classes属性会优先被选择为监听的事件类型。
private boolean shouldHandle(ApplicationEvent event, @Nullable Object[] args) { if (args == null) { return false; } String condition = getCondition(); if (StringUtils.hasText(condition)) { // 如果 condition 属性不为空,则进行spring表达式计算结果并返回 Assert.notNull(this.evaluator, "EventExpressionEvaluator must not be null"); return this.evaluator.condition( condition, event, this.targetMethod, this.methodKey, args, this.applicationContext); } return true; }ApplicationListenerMethodAdapter的shouldHandle方法会根据@EventListener标签的condition属性判断是否需要推送消息。
如果condition不为空,则使用spring表达式计算condition得到结果,结果为true的时候才推送事件。如果condition为空,则不判断直接推送。
很赞哦!(99)
相关文章
- 2、根据用户基础选择访问提供程序。由于互联问题的存在,接入商的选择也非常重要,如果用户群主要在联通,尽量选择联通接入较好的接入商,如果用户群主要在电信,那么选择电信接入较好的接入商。如果用户组位于国家/地区,则选择更好的访问提供程序进行交互。
- Redis 字符串用起来简单,但是原理可是真不简单
- 面试必问:分布式锁到底用Redis好?还是Zookeeper好?
- 注册好自己的域名后如何建站?
- 在众多公司中,如果我们必须选择一家可信的公司,那当然是信得过的。
- 一不小心把MySQL密码忘记了,该怎么办?
- 用 Three.js 和 AudioContext 实现音乐频谱的 3D 可视化
- 居然可以用 JS 写 PPT?
- 3、不明先知,根据相关征兆预测可能发生的事件,以便提前做好准备,赶紧注册相关域名。;不差钱域名;buchaqian抢先注册,就是这种敏感类型。预言是最敏感的状态。其次,你应该有眼力。所谓眼力,就是善于从社会上时不时出现的各种热点事件中获取与事件相关的域名资源。眼力的前提是对域名领域的熟悉和丰富的知识。
- Next.js对手来了!Remix正式宣布开源