IOC源码解析-程序员宅基地

技术标签: spring  spring boot  Spring源码  java  源码  

目录

主要从3方面进行解析

Bean与BeanDefinition

容器初始化主要做的事情(主要脉络)

BeanFactory

ApplicationContext

模板方法模式

Resource、ResourceLoader、容器之间的关系

BeanDefinitionReader

BeanDefinition的注册

小结


  • 主要从3方面进行解析

    • 解析配置
    • 定位与注册对象
    • 注入对象
  • Bean与BeanDefinition

  • Bean是Spring的一等公民
    • Bean的本质就是java对象,只是这个对象的生命周期由容器来管理
    • 不需要为了创建Bean而在原来的java类上添加任何额外的限制(低侵入)
    • 对java对象的控制方式体现在配置上
    • Bean是一个由Spring IoC容器实例化、组装和管理的对象
    • Bean并不是程序员编辑的,而是程序运行时,由Spring通过反射生成的
  • 什么是BeanDefinition
    • 根据配置,生成用来描述Bean的BeanDefinition
    • BeanDefinition 是定义 Bean 的配置元信息接口
    • 包含:
    • Bean 的类名
    • 设置父 bean 名称、是否为 primary
    • Bean 行为配置信息,作用域、自动绑定模式、生命周期回调、延迟加载、初始方法、销毁方法等
    • Bean 之间的依赖设置,dependencies
    • 构造参数、属性设置
    • 例:
    • 作用范围scope(@Scope)
    • 懒加载lazy-init(@Lazy):决定Bean实例是否延迟加载
      • true:在使用bean实例的时候才会将bean实例创建出来
    • 首选primary(@Primary) :设置为true的bean会是优先的实现类
      • 当一个接口有多个实现类的时候,加了@Primary的bean会是优先的实现类
    • factory-bean和factory-method(@Configuration和@Bean)
      • factory-bean:工厂bean的名称
      • factory-method:工厂方法的名称
  • main执行,主要是从容器里面调用getBean,传入Bean的id,来获取实例对象
  • 不同的BeanId创建的实例都是不同的
  • 不管是xml还是注解的方式,bean对象都会被容器定位读取到内存,之后解析成一个个的beanDefinition实例注册到容器,整个过程发生在容器的初始化过程中
  • 容器初始化主要做的事情(主要脉络)

  • Spring的Bean的继承关系不是通过extends和implements实现的,而是设置parent属性

  • BeanDefinition:描述某个Bean实例的配置信息(延时加载,scope...)
  • AttributeAccessor:定义了最基本的对任意对象的元数据的修改或者获取方式,主要用于获取BeanDefinition的属性,并对这些属性进行操作
  • BeanMetadataElement:用来传输可配置的元对象 ,主要用于返回BeanDefinition这个class对象本身
  • AbstractBeanDefinition:定义了共有的构造函数,子类就可以基于构造函数给属性赋值,其次定义了一些通用属性的get和set方法,方便给通用的属性赋值,还提供了公用的工具方法
  • RootBeanDefinition:不能作为其他类的子类,通常用于在运行时接受多个BeanDefinition合并起来的信息;能接受具有继承关系的两个BeanDefinition的属性,承接两者合并在一起的除了parent属性之外的属性
  • GenericBeanDefinition:是通用的BeanDefinition实现,具有parentName属性,方便程序在运行时设置parentBeanDefinition
  • BeanFactory

  • BeanFactory与FactoryBean有什么区别?
    • BeanFactory是Spring容器的根接口,定义了Bean工厂的最基本的功能特性(比如根据BeanName获取Bean实例等)
    • 使用作管理Bean的容器,Spring中生成的Bean都是由这个接口的实现类管理的
    • FactoryBean也是接口,基于接口里面的getObject方法,用户可以生成一套复杂的逻辑来生成Bean
    • 它本质也是一个Bean,但他并不是注入到某个地方例如Service之类
    • 它的作用是用来生成普通的bean的,实现这个接口之后,Spring容器在初始化时会把实现了这个接口的bean取出来,使用Bean里面的getObject方法来生成我们想要的bean
  • 例子:
  • 配置bean,并测试得到bean实例

  • 发现创建的并不是UserFactoryBean的实例,而是user实例
  • 说明直接调用UserFactoryBean的getBean方法,它会默认调用里面的getObject方法,返回创建的user实例
  • 那怎么获取UserFactoryBean的实例呢?
  • 只需要在前面添加一个&即可

  • BeanFactory的重要方法:

  • 简单容器

  • 接口主要用来描述容器具有的功能,实现类实现功能
  • ListableBeanFactory:批量列出工厂生产的实例的信息(beanName)
  • ApplicationContext

  • 术语补充
    • 组件扫描:自动发现应用容器中需要创建的Bean
    • 自动装配:自动满足Bean之间的依赖(对被注解Autowired注解标记的成员变量进行依赖注入)
  • BeanFactory面向的是Spring自身,而ApplocationContext面向的是开发者
  • 好比Spring容器是一辆汽车,BeanFactory是一个汽车的发动机,而ApplicationContext则是一辆完整的汽车

  • ApplicationContext:应用上下文,继承BeanFactory接口,它是Spring的一个更高级的容器,提供了更多的有用的功能
    • 国际化(MessageSource)
    • 访问资源,如URL和文件(ResourceLoader)
    • 载入多个(有继承关系)上下文 ,使得每一个上下文都专注于一个特定的层次,比如应用的web层
    • 消息发送、响应机制(ApplicationEventPublisher)
    • AOP(拦截器)
  • 因为他继承了多个接口所以拥有更多的功能
    • EnvironmentCapable:可以通过EnvironmentCapable里面的Environment getEnvironment();获取web.xml里面contextConfigLocation信息,根据它的值去加载Spring的所有的配置文件
    • ListableBeanFactory: 通过列表的方式管理bean
    • HierarchicalBeanFactory: 实现多层级的容器来实现对每一层bean的管理
    • ResourcePatternResolver: 加载资源文件
    • MessageSource: 管理message,进而实现国际化的功能
    • ApplicationEventPublisher:具备事件发布的能力,(容器在启动的时候会发布一些listener,用来监听发布的事件)监听机制
  • ApplicationContext常用容器
  • 传统的基于XML配置的经典容器
    • FileSystemXmlApplicationContext:从文件系统加载配置
    • ClassPathXmlApplicationContext:从classpath加载配置
    • XmIWebApplicationContext:用于Web应用程序的容器
  • 目前比较流行的容器
    • AnnotationConfigServletWebServerApplicationContext:在SpringBoot的Boot模块下
    • AnnotationConfigReactiveWebServerApplicationContext:用来满足响应式的需求
    • AnnotationConfigApplicationContext:对于普通的非web应用常用
  • ApplicationContext方法都是以get开头,都是只读的,需要使用子接口来实现ApplicationContext可配置的能力
  • 子接口ConfigurableApplicationContext:
    • 里面提供了一些方法来配置ApplicationContext,启动、刷新、关闭应用上下文的能力
    • 重新启动容器,清除缓存,重新装载类信息
  • AbstractApplicationContext:
    • 实现了ApplicationContext里面简单不易动的部分:容器工厂的处理,事件的发送广播、监听器的注册、容器初始化操作refresh方法、getBean方法
  • refresh()是Spring最核心的方法,在SpringApplication.run(args)的时候执行,是一个同步同步方法,用synchronized关键字来实现
  • refresh()大致功能
    • 容器初始化、配置解析
    • BeanFactoryPostProcessor和BeanPostProcessor的注册和激活
    • 国际化配置
  • 模板方法模式

  • 围绕抽象类,实现通用逻辑,定义模板结构,部分逻辑由子类实现
  • 基于继承的,会准备一个抽象类,将部分逻辑以具体方法和具体逻辑实现,然后定义一个模板结构,将剩下的具体内容延迟到子类去实现
  • 声明一些抽象方法迫使子类实现剩下的逻辑
  • 复用: 将相同逻辑的代码在父类中复用,将具体实现下沉到子类
  • 反向控制: 通过父类调用子类的操作,通过对子类具体的实现扩展出不同的行为,以此来实现反向控制
  • 通过子类扩展来实现定制化的行为,符合开闭原则

  • 模板方法:定义了整个方法需要实现的业务的骨架
  • 具体方法:一些确定不变的逻辑,父类直接实现
  • 钩子:不是由子类来直接调用而是在特定条件发生时由抽象类的调用方来调用,以用于对发生的事件进行响应
  • 钩子就是供子类灵活变通的钥匙
  • 例如:去KTV唱歌,服务生帮忙打开音响,结束后客户付钱是统一必有的操作,所以在父类中实现,点歌需要看用户的需求,所以设置为抽象方法交给子类实现,但是会不会额外消费子类就看情况而定,选择性实现,定义为钩子方法

  • refresh()方法就是一个模板方法,主要定义了容器启动时需要做的事情,其中方法:

  • Resource、ResourceLoader、容器之间的关系

  • java中资源会被抽象成url,解析url的protocol处理不同协议资源
  • 而Spring将物理资源抽象为Resource
  • Resource
    • 一个接口,定义了资源的基本操作

    • InputStreamSource:只有一个方法,获取资源流
    • Resource家族:

    • 针对不同的资源有不同类的实现;每个实现类代表资源的访问策略
    • EncodedResource:对资源文件的编码处理
    • AbstractResource:对Resource方法的大部分默认公共实现;若想自定义Resource可继承它,覆盖相应方法即可
    • ServletContextResource:访问web容器中的上下文资源而实现的,支持以流、url的形式访问,还可以从jar包中访问资源
    • ClassPathResource:访问类加载路径下的资源;可自动搜索WEB-INF/classes下的资源
    • FileSystemResource:访问文件系统资源;java提供的File类也可实现
    • 会根据资源地址自动选择正确的Resource
    • 强大的加载资源的方式
    • 自动识别"classpath:"、”file:" 等资源地址前缀
    • 支持自动解析Ant风格带通配符的资源地址
    • Ant
    • 路径匹配表达式,用来对URI进行匹配;类似于正则表达式,只不过正则表达式表示的范围比较广,Ant只适用于路径匹配
    • ? 匹配任何单字符
    • *匹配0或者任意数量的字符
    • **匹配0或者更多的目录
    • 例子:

  • Resourceloader
    • 实现不同的Resource加载策略,按需返回特定类型的Resource
    • 是一个接口,根据路径获取资源;可以是classPath或者file等

  • DefaultResourceLoader
    • 简单工厂模式侧重的是:返回创建出的对象,用户不了解对象本身,相当于黑盒
    • 但是策略模式要求用户了解策略本身,即针对什么样的资源使用什么样的Resource加载

    • 因为Resourceloader里面的方法只能获取一个resource实例,因此又来了一个接口ResourcePatternResolver

    • 实现类PathMatchingResourcePatternResolver去实现,同时还支持Ant路径风格模式
    • Spring提供了Resource和ResourceLoader来统一抽象整个资源及其定位,使得资源与资源的定位有了更加清晰的界限,并且有DefaultResourceLoader使得自定义实现更加清晰和方便
    • 发现ApplicationContext继承了 ResourcePatternResolver 那就间接继承了ResourceLoader
    • 所以任何的ApplicationContext的实现都可以看做 ResourcePatternResolver 或ResourceLoader的实例
    • 整个ApplicationContext 的实现类完全可以支持ResourcePatternResolver 、ResourceLoader,这也是高级容器为什么支持统一加载资源的原因
    • 在容器读取配置时,委派给了PathMatchingResourcePatternResolver以及DefaultResourceLoader来执行
  • BeanDefinitionReader

  • 它是ResourceLoader的使用者,是资源加载利器的使用者
  • 它利用ResourceLoader、ResourcePatternResolver,将配置信息解析成一个个BeanDefinition,并借助BeanDefinitionRegistry将BeanDefinition注册到容器里
  • 作用:
  • 定义了一系列加载BeanDefinition的接口,针对单个或者多个配置文件的加载,或者单个resource实例或者多个resource实例的加载,最终目的将配置文件的配置转换成一个个的BeanDefinition

  • 体系结构:

  • AbstractBeanDefinitionReader
    • 实现了BeanDefinition的公共逻辑
    • 如果是ResourcePatternResolver的实例,则代表需要加载多个资源
    • 不管是加载单个还是多个资源,最后都会调用loadBeanDefinitions方法做进一步加载
    • loadBeanDefinitions():主要根据用户提供的资源加载器的类型来判断加载单个或多个资源
    • 实现类是针对不同的资源类型做定义的
  • XmlBeanDefinitionReader
    • 传入指定xml文件的路径,XmlBeanDefinitionReader 调用它的父类ResourceLoader根据传入的路径返回resoure实例,它再去调用loadBeanDefinitions方法去执行

  • BeanDefinition的注册

  • 方法:
  • 1---向注册表中注册一个新的BeanDefinition实例

  • 2---移除注册表中已存在的实例

  • 3---从注册表中取得指定的BeanDefinition实例

  • 4---判断BeanDefinition实例是否在注册表中(是否注册)

  • DefaultListableBeanFactory实现了 BeanDefinitionRegistry接口

  • 同时里面定义的beanDefinitionMap将注册的beanName为key,注册实例为value存里面

  • 配置到读取到加载到解析
  • 主要的逻辑就是将解析的beanDefinition的实例给注册到DefaultListableBeanFactory容器(里面的beanDefinitionMap)中
  • 注解容器是先于xml文件创建出来的,为什么要提前创建出来呢?
  • 因为他会提前创建出一些系统内置的beanDefinition实例,所以就需要提前在构造函数中创建DefaultListableBeanFactory实例,以提供对系统内置的beanDefinition的注册
  • 先加载内置的beanDefinition实例,再加载entrance(被Component放到容器里的bean)
  • 再加载其他被注解(Controller、Service....)修饰的类
  • 在容器刷新的时候被注册进来,和xml配置一样都是在容器刷新的时候被注册进来
  • BeanDefinitionRegistryPostProcessor的beanDefinition会被优先执行,普通的BeanFactoryPostProcessor会被延后执行
  • 执行BeanDefinitionRegistryPostProcessor会调用invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry)
  • 执行完之后才会调用invokeBeanFactoryPostProcessors(registryProcessors, beanFactory);去执行普通的BeanFactoryPostProcessor
  • 小结

  • Spring会依据BeanDefinition创建Bean
  • DefaultListableBeanFactory:主要负责对BeanDefinition的注册
  • 容器刷新时的共性:都使用了AbstractApplicationContext里面的refresh()
  • 配置资源在Spring里面会被转换成一个个不同的Resource对象实例,这离不开ResourceLoader的支持
  • 有了ResourcePatternResolver使得Spring可以根据配置资源的ur路径选择合适的resource进行包装,体现了策略模式
  • 有了好的利器,BeanDefinitionReader进行使用
  • xml资源被XmlBeanDefinitionReader解析成Document对象,再委托给BeanDefinitionDocumentReader解析成一个个GenericBeanDefinition实例,再将其注册到DefaultListableBeanFactory内置容器中
  • 不同于xml,xml所有的beanDefinition实例都是在refresh()方法中刷新时注册的,而注解分为三类BeanDefinition的注册
  • 第一类:容器内部设置的BeanDefinition实例,在容器的构造函数一经调用就被注册到内置容器中
  • 第二类:用户自定义的带有@Configuration的类,在容器的构造函数中调用register()方法时被注册
  • 第三类:常规的BeanDefinition则是在refresh()方法里的容器级别后置处理器被调用时,进行注册的
  • AnnotatedBeanDefinitionReader:负责对上述三种类型的BeanDefinition进行解析,并将其注册到DefaultListableBeanFactory容器中
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_59624686/article/details/130938029

智能推荐

Docker 快速上手学习入门教程_docker菜鸟教程-程序员宅基地

文章浏览阅读2.5w次,点赞6次,收藏50次。官方解释是,docker 容器是机器上的沙盒进程,它与主机上的所有其他进程隔离。所以容器只是操作系统中被隔离开来的一个进程,所谓的容器化,其实也只是对操作系统进行欺骗的一种语法糖。_docker菜鸟教程

电脑技巧:Windows系统原版纯净软件必备的两个网站_msdn我告诉你-程序员宅基地

文章浏览阅读5.7k次,点赞3次,收藏14次。该如何避免的,今天小编给大家推荐两个下载Windows系统官方软件的资源网站,可以杜绝软件捆绑等行为。该站提供了丰富的Windows官方技术资源,比较重要的有MSDN技术资源文档库、官方工具和资源、应用程序、开发人员工具(Visual Studio 、SQLServer等等)、系统镜像、设计人员工具等。总的来说,这两个都是非常优秀的Windows系统镜像资源站,提供了丰富的Windows系统镜像资源,并且保证了资源的纯净和安全性,有需要的朋友可以去了解一下。这个非常实用的资源网站的创建者是国内的一个网友。_msdn我告诉你

vue2封装对话框el-dialog组件_<el-dialog 封装成组件 vue2-程序员宅基地

文章浏览阅读1.2k次。vue2封装对话框el-dialog组件_

MFC 文本框换行_c++ mfc同一框内输入二行怎么换行-程序员宅基地

文章浏览阅读4.7k次,点赞5次,收藏6次。MFC 文本框换行 标签: it mfc 文本框1.将Multiline属性设置为True2.换行是使用"\r\n" (宽字符串为L"\r\n")3.如果需要编辑并且按Enter键换行,还要将 Want Return 设置为 True4.如果需要垂直滚动条的话将Vertical Scroll属性设置为True,需要水平滚动条的话将Horizontal Scroll属性设_c++ mfc同一框内输入二行怎么换行

redis-desktop-manager无法连接redis-server的解决方法_redis-server doesn't support auth command or ismis-程序员宅基地

文章浏览阅读832次。检查Linux是否是否开启所需端口,默认为6379,若未打开,将其开启:以root用户执行iptables -I INPUT -p tcp --dport 6379 -j ACCEPT如果还是未能解决,修改redis.conf,修改主机地址:bind 192.168.85.**;然后使用该配置文件,重新启动Redis服务./redis-server redis.conf..._redis-server doesn't support auth command or ismisconfigured. try

实验四 数据选择器及其应用-程序员宅基地

文章浏览阅读4.9k次。济大数电实验报告_数据选择器及其应用

随便推点

灰色预测模型matlab_MATLAB实战|基于灰色预测河南省社会消费品零售总额预测-程序员宅基地

文章浏览阅读236次。1研究内容消费在生产中占据十分重要的地位,是生产的最终目的和动力,是保持省内经济稳定快速发展的核心要素。预测河南省社会消费品零售总额,是进行宏观经济调控和消费体制改变创新的基础,是河南省内人民对美好的全面和谐社会的追求的要求,保持河南省经济稳定和可持续发展具有重要意义。本文建立灰色预测模型,利用MATLAB软件,预测出2019年~2023年河南省社会消费品零售总额预测值分别为21881...._灰色预测模型用什么软件

log4qt-程序员宅基地

文章浏览阅读1.2k次。12.4-在Qt中使用Log4Qt输出Log文件,看这一篇就足够了一、为啥要使用第三方Log库,而不用平台自带的Log库二、Log4j系列库的功能介绍与基本概念三、Log4Qt库的基本介绍四、将Log4qt组装成为一个单独模块五、使用配置文件的方式配置Log4Qt六、使用代码的方式配置Log4Qt七、在Qt工程中引入Log4Qt库模块的方法八、获取示例中的源代码一、为啥要使用第三方Log库,而不用平台自带的Log库首先要说明的是,在平时开发和调试中开发平台自带的“打印输出”已经足够了。但_log4qt

100种思维模型之全局观思维模型-67_计算机中对于全局观的-程序员宅基地

文章浏览阅读786次。全局观思维模型,一个教我们由点到线,由线到面,再由面到体,不断的放大格局去思考问题的思维模型。_计算机中对于全局观的

线程间控制之CountDownLatch和CyclicBarrier使用介绍_countdownluach于cyclicbarrier的用法-程序员宅基地

文章浏览阅读330次。一、CountDownLatch介绍CountDownLatch采用减法计算;是一个同步辅助工具类和CyclicBarrier类功能类似,允许一个或多个线程等待,直到在其他线程中执行的一组操作完成。二、CountDownLatch俩种应用场景: 场景一:所有线程在等待开始信号(startSignal.await()),主流程发出开始信号通知,既执行startSignal.countDown()方法后;所有线程才开始执行;每个线程执行完发出做完信号,既执行do..._countdownluach于cyclicbarrier的用法

自动化监控系统Prometheus&Grafana_-自动化监控系统prometheus&grafana实战-程序员宅基地

文章浏览阅读508次。Prometheus 算是一个全能型选手,原生支持容器监控,当然监控传统应用也不是吃干饭的,所以就是容器和非容器他都支持,所有的监控系统都具备这个流程,_-自动化监控系统prometheus&grafana实战

React 组件封装之 Search 搜索_react search-程序员宅基地

文章浏览阅读4.7k次。输入关键字,可以通过键盘的搜索按钮完成搜索功能。_react search