spring扩展点四:SmartInitializingSingleton 补充
前面有笔记,记录过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 补充相关推荐
- 非常有必要了解的Springboot启动扩展点
点击上方蓝色"方志朋",选择"设为星标" 回复"666"获取独家整理的学习资料! 来源:r6d.cn/r4P7 背景 Spring的核心思想 ...
- 详解spring生命周期的扩展点
详解spring生命周期的扩展点,加速你追赶高手的脚步 详解spring生命周期的扩展点
- 三万字盘点Spring/Boot的那些常用扩展点
Spring对于每个Java后端程序员来说肯定不陌生,日常开发和面试必备的.本文就来盘点Spring/SpringBoot常见的扩展点,同时也来看看常见的开源框架是如何基于这些扩展点跟Spring/S ...
- 盘点Spring/Boot的那些常用扩展点
Spring对于每个Java后端程序员来说肯定不陌生,日常开发和面试必备的.本文就来盘点Spring/SpringBoot常见的扩展点,同时也来看看常见的开源框架是如何基于这些扩展点跟Spring/S ...
- beanfactorypostprocessor_Spring源码分析(六)容器的扩展点(BeanFactoryPostProcessor)
之前的文章我写了BeanDefinition的基本概念和合并,其中很对次提到了容器的扩展点,这篇文章就写这方面的知识.这部分的内容主要涉及到官网的1.8小节.按照官网介绍来说,容器的扩展点可以分为三类 ...
- Spring Cloud(四):Spring Cloud Alibaba Feign Dubbo
扩展点 RequestInterceptor#apply 扩展点 feign.Client#execute spring cloud dubbo 调用 RPC RPC 全称是 Remote Proce ...
- MyBatis的扩展点(plugins)
2019独角兽企业重金招聘Python工程师标准>>> 1.mybatis扩展点plugins mybatis的扩展是通过拦截器Interceptor来实现的,本质上就是JDK的动态 ...
- Struts2工作原理和框架扩展点
http://www.cnblogs.com/winstonyan/archive/2011/11/13/struts2_flow_and_extends.html 框架主要涉及技术:Spring + ...
- 实现自定义扩展点_spring扩展API接口介绍
对spring进行定制化功能扩展时,可以选择如下一些扩展点: BeanFactoryPostProcessor 是beanFactory后置处理器,支持在bean factory标准初始化完成后,对b ...
- MVC 的八个扩展点
Asp.net MVC中常用的八个扩展点并举例说明. 一.ActionResult ActionResult代表了每个Action的返回结果.asp.net mvc提供了众多内置的ActionResu ...
最新文章
- webpack基础+webpack配置文件常用配置项介绍+webpack-dev-server
- C语言嵌入式系统编程修炼之道——屏幕操作篇
- 牛客 - 双流机场(思维)
- commons-httpclient 和 httpclient 区别
- DOM 事件深入浅出(二)
- 《Android进阶之光》--View体系与自定义View
- raid 物理盘缓存状态_使用MegaCli工具查看Raid磁盘阵列状态
- 子类重载父类函数_Python面向对象之继承、重写与重载
- 嵌入式开发之视频压缩比---h264、mjpeg、mpeg4
- Google Play In-app Billing API version is less than 3.
- 与孤独世界的博弈——诺贝尔奖得主约翰·纳什的传奇一生
- python爬取整个网页,教师节不知道给老师送什么?
- 基于Java web的论坛BBS系统设计与实现
- WIN10 时间同步
- 此spoolsv.exe(木马程序)非彼spoolsv.exe(系统进程)
- Excel散点图 如何用平滑线 连接 不连续的点
- 主流图数据库对比,Neo4j、ArangoDB、OrientDB、JanusGraph、HugeGraph
- java通信:远程画板
- 搭了我半年顺风车的同事,把我拉黑了:和任何人走太近,都是一场灾难
- 阿里云学生机搭建FTP实战(日后完善)
热门文章
- JAVA rs 是否要关闭_关闭结果集rs和statement以后,是否还要关闭数据库连接呢?...
- First Bad Version
- 318.最大单词长度乘积
- ImportError: libgfortran.so.4: cannot open shared object file: No such file or directory
- python从键盘输入一个字符串、将小写字母_# 每日一道面试题 # 从键盘输入一个字符串,将小写字母全部转换成大写字母,然后输出到一个磁盘文件test中保存。...
- 语音识别系统原理介绍----gmm-hmm
- 2018_08_10_生活记录_关于我和这个博客的说明
- vofuria的开发(3)将vuforia引入新建立的工程
- 153.寻找旋转排序数组中的最小值(力扣leetcode) 博主可答疑该问题
- 《四 spring源码》spring的事务注解@Transactional 原理分析