AnnotationConfigApplicationContext(IOC容器)的有参构造方法中,在refresh()里对这些组件进行初始化

BeanPostProcessor

  bean后置处理器,bean创建对象初始化前后进行拦截工作的

BeanFactoryPostProcessor

  refresh()里的invokeBeanFactoryPostProcessors(beanFactory)来初始化BeanFactoryPostProcessor(beanFactory后置处理器,在所有的bean定义信息保存到beanFactory并进行标准初始化之后调用,来定制和修改BeanFactory的内容)

  • String[] postProcessorNames=beanFactory.getBeanNamesForType(BeanFactoryPostProcessor.class, true, false)
    • 从beanFactory中找到所有类型是BeanFactoryPostProcessor的组件,循环遍历postProcessorNames根据组件优先级加入到不同的集合里(实现了PriorityOrdered的直接在循环中就注册了beanFactoryPostProcessor,其余的之后再一次循环遍历进行注册)
    • 优先级高的集合先执行invokeBeanFactoryPostProcessors(postProcessors,beanFactory),该方法遍历调用postProcessors集合里的对象.postProcessBeanFactory(beanFactory)

BeanDefinitionRegistryPostProcessor

  继承BeanFactoryPostProcessor接口,在invokeBeanFactoryPostProcessors(beanFactory)中,在所有的bean定义信息将要被加载、BeanFactoryPostProcessor初始化之前进行工作

  • String[] postProcessorNames=beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);

    • 从beanFactory中找到所有类型是BeanDefinitionRegistryPostProcessor的组件,也是根据优先级执行invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors,registry),之后依次触发所有的postProcessBeanDefinitionRegistry(registry)可以给容器中再额外添加一些组件

结论

  • invokeBeanFactoryPostProcessors()先从容器中获取到所有的BeanDefinitionRegistryPostProcessor组件(BeanDefinitionRegistryPostProcessor执行过的bean都会保存到一个集合里,执行到BeanFactoryPostProcessor的时候就会进行判断,如果存在说明已经执行过了)
  • 依次触发所有的postProcessBeanDefinitionRegistry()
  • 再来触发BeanFactoryPostProcessor.postProcessBeanFactory()
  • 最后再来从容器中找到BeanFactoryPostProcessor组件,然后依次触发postProcessBeanFactory()
@Component
public class MyBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor{ @Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
// TODO Auto-generated method stub
System.out.println("MyBeanDefinitionRegistryPostProcessor...bean的数量:"+beanFactory.getBeanDefinitionCount());
} //BeanDefinitionRegistry Bean定义信息的保存中心,以后BeanFactory就是按照BeanDefinitionRegistry里面保存的每一个bean定义信息创建bean实例;
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
// TODO Auto-generated method stub
System.out.println("postProcessBeanDefinitionRegistry...bean的数量:"+registry.getBeanDefinitionCount());
//RootBeanDefinition beanDefinition = new RootBeanDefinition(Blue.class);
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(Blue.class).getBeanDefinition();
registry.registerBeanDefinition("hello", beanDefinition);
} }

ApplicationListener

  监听容器中发布的事件;在refresh()里的registerListeners()里执行getBeanNamesForType(ApplicationListener.class, true, false)获取所有的监听事件,并将它们保存到ApplicationEventMulticaster的listener集合中

 public interface ApplicationListener<E extends ApplicationEvent>  //监听 ApplicationEvent 及其下面的子事件

步骤

  • 实现ApplicationListener接口来监听某个事件(ApplicationEvent及其子类)并加入到容器。只要容器中有相关事件的发布,我们就能监听到这个事件

    • ContextRefreshedEvent:容器刷新完成(所有bean都完全创建)会发布这个事件
    • ContextClosedEvent:关闭容器会发布这个事件
@Component
public class MyApplicationListener implements ApplicationListener<ApplicationEvent> { //当容器中发布此事件以后,方法触发
@Override
public void onApplicationEvent(ApplicationEvent event) {
// TODO Auto-generated method stub
System.out.println("收到事件:"+event);
} }
  • 发布事件
applicationContext.publishEvent(new ApplicationEvent(new String("我发布的时间")) {
});

原理

  • ContextRefreshedEvent

    • 在refresh()里的finishRefresh()中执行了publishEvent(new ContextRefreshedEvent(this));在容器刷新完成的最后一步方法里发布ContextRefreshedEvent事件
  • ContextClosedEvent
    • 执行applicationContext.close()关闭容器的doClose()中执行了publishEvent(new ContextClosedEvent(this))

  所有的事件发布都调用了publishEvent(),而在这方法里主要做了这几件事:

  • 获取事件的多播器(派发器):getApplicationEventMulticaster(),并通过multicastEvent()派发事件,在该方法中获取到所有的ApplicationListener进行遍历(如果有Executor,可以支持使用Executor进行异步派发;否则,同步的方式直接执行invokeListener(listener, event)),拿到listener回调onApplicationEvent()

@EventListener

  使用EventListenerMethodProcessor处理器解析方法上的@EventListener,它实现SmartInitializingSingleton的afterSingletonsInstantiated();

  在refresh()里的finishBeanFactoryInitialization(beanFactory)里创建完所有单例bean后,会获取所有的bean判断是否是SmartInitializingSingleton类型的,如果是就调用afterSingletonsInstantiated()

@EventListener(classes={ApplicationEvent.class})
public void listen(ApplicationEvent event){
System.out.println("监听到的事件:"+event);
}

事件的多播器

  在refresh()里的initApplicationEventMulticaster()中初始化ApplicationEventMulticaster,先去容器中找有没有id=“applicationEventMulticaster”的组件,如果没有this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);并通过beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster)注册到容器中去,在其他组件要派发事件就可以自动注入这个applicationEventMulticaster

spring注解-扩展原理的更多相关文章

  1. 重新学习Spring注解——扩展原理

    39.扩展原理-BeanFactoryPostProcessor 40.扩展原理-BeanDefinitionRegistryPostProcessor 41.扩展原理-ApplicationList ...

  2. Spring注解实现原理

    ​[Spring如何使用注解机制完成自动装配] Java实例构造时会调用默认父类无参构造方法,Spring正是利用了这一点,让"操作元素的代码"得以执行.   [两种处理策略] ( ...

  3. Spring注解驱动开发(五)-----扩展原理

    扩展原理 1.BeanPostProcessor-----bean后置处理器,bean创建对象初始化前后进行拦截工作的 2.BeanFactoryPostProcessor-----beanFacto ...

  4. 【spring 注解驱动开发】扩展原理

    尚学堂spring 注解驱动开发学习笔记之 - 扩展原理 扩展原理 1.扩展原理-BeanFactoryPostProcessor BeanFactoryPostProcessor * 扩展原理: * ...

  5. Spring注解驱动开发之扩展原理

    前言:现今SpringBoot.SpringCloud技术非常火热,作为Spring之上的框架,他们大量使用到了Spring的一些底层注解.原理,比如@Conditional.@Import.@Ena ...

  6. Spring 注解原理(三)@Qualifier @Value

    Spring 注解原理(三)@Qualifier @Value Spring 系列目录(https://www.cnblogs.com/binarylei/p/10198698.html) 一.Aut ...

  7. spring Mvc 执行原理 及 xml注解配置说明 (六)

    Spring MVC 执行原理 在 Spring Mvc 访问过程里,每个请求都首先经过 许多的过滤器,经 DispatcherServlet 处理; 一个Spring MVC工程里,可以配置多个的 ...

  8. 这一次搞懂Spring自定义标签以及注解解析原理

    前言 在上一篇文章中分析了Spring是如何解析默认标签的,并封装为BeanDefinition注册到缓存中,这一篇就来看看对于像context这种自定义标签是如何解析的.同时我们常用的注解如:@Se ...

  9. [转]Spring注解原理的详细剖析与实现

    原文地址:http://freewxy.iteye.com/blog/1149128/ 本文主要分为三部分: 一.注解的基本概念和原理及其简单实用 二.Spring中如何使用注解 三.编码剖析spri ...

随机推荐

  1. lumen、laravel问题汇总

    框架报500 1.chmod 777 -R storage 将日志目录权限设置下. 2.修改fastcgi,将代码目录包含进去. fastcgi_param PHP_ADMIN_VALUE " ...

  2. Java测试开发--MySql之C3P0连接池(八)

    连接池C3P0! 连接池技术的目的:解决建立数据库连接耗费资源和时间很多的问题,提高性能 ! 下面以案例演示下C3P0的操作流程. 1.测试准备: ①MySql数据库一枚②database名为myte ...

  3. Win10-更改c盘下的用户文件夹名

    如果你是win10家庭版,请先升级成专业版 win10家庭版升级到win10专业版 修改用户名称

  4. Maven打包web项目报错:webxml attribute is required (or pre-existing WEB-INF/web.xml if executing in update)

    问题描述 使用Maven打包项目的时候,出现错误: webxml attribute is required (or pre-existing WEB-INF/web.xml if executing ...

  5. Spring 为啥默认把bean设计成单例的?

    熟悉Spring开发的朋友都知道Spring提供了5种scope分别是singleton.prototype.request.session.global session. 如下图是官方文档上的截图, ...

  6. Python批量爬取谷歌原图,2021年最新可用版

    文章目录 前言 一.环境配置 1.安装selenium 2.使用正确的谷歌浏览器驱动 二.使用步骤 1.加载chromedriver.exe 2.设置是否开启可视化界面 3.输入关键词.下载图片数.图 ...

  7. CodeBlocks调试器缺少(gdb.exe)文件

    错误如下: Building to ensure sources are up-to-date Selecting target:  Debug ERROR: You need to specify ...

  8. [bzoj1072]排列

    考虑用状压dp枚举排列,即f[i][j]表示当前状态为i,余数为j的方案数,考虑在末尾新增一个字符来转移即可,注意最后答案要除以排列组合 1 #include<bits/stdc++.h> ...

  9. nginx安装与配置1-nginx安装

    反向代理: 客户端不需要配置就可以访问,将请求发送到反向代理服务器, 由反向代理服务器选择目标服务器获取数据,再返回客户端,对外暴露代理服务器地址,隐藏真实ip 负载均衡: 客户端请求nginx等服务 ...

  10. 【备考06组01号】第四届蓝桥杯JAVA组A组国赛题解

    1.填算式 (1)题目描述     请看下面的算式:     (ABCD - EFGH) * XY = 900     每个字母代表一个0~9的数字,不同字母代表不同数字,首位不能为0.     比如 ...