前面有笔记,记录过SmartInitializingSingleton这个扩展点的使用,可以参考这篇博客 spring扩展点四:SmartInitializingSingleton的应用

这篇博客是我自己写的一个demo,要验证这个扩展机制,但是最近在看spring事件监听机制的时候,看到了对这个扩展点的应用

EventListenerMethodProcessor ,就是这个bean,我们先不说这个bean是什么注入到spring容器中的,会在后面事件监听器源码的博客中,单独记录,这篇博客主要想说明的是:在spring事件监听机制中,如果我通过@EventListener来声明一个事件监听器,那他就是通过SmartInitializingSingleton这个扩展机制来完成解析的

org.springframework.context.event.EventListenerMethodProcessor#afterSingletonsInstantiated
/*** 这个方法也是spring扩展点之一*/
@Override
public void afterSingletonsInstantiated() {/*** 1.获取到所有的eventListenerFactory,理论上就是DefaultEventListenerFactory*/List<EventListenerFactory> factories = getEventListenerFactories();ConfigurableApplicationContext context = getApplicationContext();/*** 2.获取到spring容器中,所有的beanName,依次遍历*/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) {// 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(context.getBeanFactory(), 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);}}}/*** 从for循环开始,到这里,代码的细节没有看的太懂,但是这段代码是为了获取到beanName对应的type* ,以便在下面使用*/try {processBean(factories, beanName, type);}catch (Throwable ex) {throw new BeanInitializationException("Failed to process @EventListener " +"annotation on bean with name '" + beanName + "'", ex);}}}}
}

在上面的方法中,最为重要的是:调用的processBean这个方法

/*** 在这个方法中,会解析beanName对应的bean中所有的方法上,是否有添加@EventListener注解,如果有添加*     则根据beanName生成一个applicationListener,并添加到多事件派发器上* @param factories* @param beanName* @param targetType*/
protected void processBean(final List<EventListenerFactory> factories, final String beanName, final Class<?> targetType) {/*** 1.nonAnnotatedClasses* 这个set集合,可以理解为是一个缓存,全局搜索了下,只有在下面,annotatedMethods为null的时候,才会写入到这个集合中* 所以,我认为,在第一次解析的时候,如果一个bean中的method没有条件@EventListener注解,就会把这个bean* 添加到nonAnnotatedClasses集合中*/if (!this.nonAnnotatedClasses.contains(targetType)) {Map<Method, EventListener> annotatedMethods = null;try {/*** 2.解析targetType这个class,获取类中所有加了@EventListener注解的method*/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);}}/*** 3.如果当前class的方法中,没有添加@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/*** 4.如果当前targetClass中,有方法添加了@EventListener注解,那就根据beanName* 生成一个applicationListener* 方法,点进去看,会发现,就是ApplicationListenerMethodAdapter这个类** 所以,我们可以认为:* 1、对于实现ApplicationListener接口这种方式声明的事件监听器,其applicationListener就是对应的实现类* 2、对于使用@EventListener注解这种方法,其applicationListener就是ApplicationListenerMethodAdapter*/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// 是ApplicationListenerMethodAdapterApplicationListener<?> applicationListener =factory.createApplicationListener(beanName, targetType, methodToUse);if (applicationListener instanceof ApplicationListenerMethodAdapter) {((ApplicationListenerMethodAdapter) applicationListener).init(context, this.evaluator);}/*** 5.最重要得一步:将applicationListener* 添加到多事件派发器中,在后面publishEvent的时候会用到*/context.addApplicationListener(applicationListener);break;}}}if (logger.isDebugEnabled()) {logger.debug(annotatedMethods.size() + " @EventListener methods processed on bean '" +beanName + "': " + annotatedMethods);}}}
}

这里注解我写的也比较清楚,也就不再重复解释,只是有一个点,需要再次明确:

1、对于实现ApplicationListener接口这种方式声明的事件监听器,其applicationListener就是对应的实现类
2、对于使用@EventListener注解这种方法,其applicationListener就是ApplicationListenerMethodAdapter

以上,就是@EventListener注解,是如何通过SmartInitializingSingleton这个扩展点来完成解析的

context.addApplicationListener(applicationListener);

这个方法会有两个地方调用,一个是上面,@EventListener注解在被解析的时候
一个是解析通过实现接口方式来声明事件监听器的时候,会调用

spring扩展点四:SmartInitializingSingleton 补充相关推荐

  1. 非常有必要了解的Springboot启动扩展点

    点击上方蓝色"方志朋",选择"设为星标" 回复"666"获取独家整理的学习资料! 来源:r6d.cn/r4P7 背景 Spring的核心思想 ...

  2. 详解spring生命周期的扩展点

    详解spring生命周期的扩展点,加速你追赶高手的脚步 详解spring生命周期的扩展点

  3. 三万字盘点Spring/Boot的那些常用扩展点

    Spring对于每个Java后端程序员来说肯定不陌生,日常开发和面试必备的.本文就来盘点Spring/SpringBoot常见的扩展点,同时也来看看常见的开源框架是如何基于这些扩展点跟Spring/S ...

  4. 盘点Spring/Boot的那些常用扩展点

    Spring对于每个Java后端程序员来说肯定不陌生,日常开发和面试必备的.本文就来盘点Spring/SpringBoot常见的扩展点,同时也来看看常见的开源框架是如何基于这些扩展点跟Spring/S ...

  5. beanfactorypostprocessor_Spring源码分析(六)容器的扩展点(BeanFactoryPostProcessor)

    之前的文章我写了BeanDefinition的基本概念和合并,其中很对次提到了容器的扩展点,这篇文章就写这方面的知识.这部分的内容主要涉及到官网的1.8小节.按照官网介绍来说,容器的扩展点可以分为三类 ...

  6. Spring Cloud(四):Spring Cloud Alibaba Feign Dubbo

    扩展点 RequestInterceptor#apply 扩展点 feign.Client#execute spring cloud dubbo 调用 RPC RPC 全称是 Remote Proce ...

  7. MyBatis的扩展点(plugins)

    2019独角兽企业重金招聘Python工程师标准>>> 1.mybatis扩展点plugins mybatis的扩展是通过拦截器Interceptor来实现的,本质上就是JDK的动态 ...

  8. Struts2工作原理和框架扩展点

    http://www.cnblogs.com/winstonyan/archive/2011/11/13/struts2_flow_and_extends.html 框架主要涉及技术:Spring + ...

  9. 实现自定义扩展点_spring扩展API接口介绍

    对spring进行定制化功能扩展时,可以选择如下一些扩展点: BeanFactoryPostProcessor 是beanFactory后置处理器,支持在bean factory标准初始化完成后,对b ...

  10. MVC 的八个扩展点

    Asp.net MVC中常用的八个扩展点并举例说明. 一.ActionResult ActionResult代表了每个Action的返回结果.asp.net mvc提供了众多内置的ActionResu ...

最新文章

  1. webpack基础+webpack配置文件常用配置项介绍+webpack-dev-server
  2. C语言嵌入式系统编程修炼之道——屏幕操作篇
  3. 牛客 - 双流机场(思维)
  4. commons-httpclient 和 httpclient 区别
  5. DOM 事件深入浅出(二)
  6. 《Android进阶之光》--View体系与自定义View
  7. raid 物理盘缓存状态_使用MegaCli工具查看Raid磁盘阵列状态
  8. 子类重载父类函数_Python面向对象之继承、重写与重载
  9. 嵌入式开发之视频压缩比---h264、mjpeg、mpeg4
  10. Google Play In-app Billing API version is less than 3.
  11. 与孤独世界的博弈——诺贝尔奖得主约翰·纳什的传奇一生
  12. python爬取整个网页,教师节不知道给老师送什么?
  13. 基于Java web的论坛BBS系统设计与实现
  14. WIN10 时间同步
  15. 此spoolsv.exe(木马程序)非彼spoolsv.exe(系统进程)
  16. Excel散点图 如何用平滑线 连接 不连续的点
  17. 主流图数据库对比,Neo4j、ArangoDB、OrientDB、JanusGraph、HugeGraph
  18. java通信:远程画板
  19. 搭了我半年顺风车的同事,把我拉黑了:和任何人走太近,都是一场灾难
  20. 阿里云学生机搭建FTP实战(日后完善)

热门文章

  1. JAVA rs 是否要关闭_关闭结果集rs和statement以后,是否还要关闭数据库连接呢?...
  2. First Bad Version
  3. 318.最大单词长度乘积
  4. ImportError: libgfortran.so.4: cannot open shared object file: No such file or directory
  5. python从键盘输入一个字符串、将小写字母_# 每日一道面试题 # 从键盘输入一个字符串,将小写字母全部转换成大写字母,然后输出到一个磁盘文件test中保存。...
  6. 语音识别系统原理介绍----gmm-hmm
  7. 2018_08_10_生活记录_关于我和这个博客的说明
  8. vofuria的开发(3)将vuforia引入新建立的工程
  9. 153.寻找旋转排序数组中的最小值(力扣leetcode) 博主可答疑该问题
  10. 《四 spring源码》spring的事务注解@Transactional 原理分析