Spring源码_BeanFactory&Application

43 阅读5分钟

10_BeanFactory 和 ApplicationContext 详解

1.BeanFactory

1.概述
    BeanFactory 是 Spring 中,提供的最低等的工厂,主要的功能就是用于创建对象。
2.最常使用的 BeanFactory 实现类 DefaultListableBeanFactory
  • 类体系结构图
  • image-20231012221341090
BeanFactory :
    工厂具有的功能,可以查找对象,创建对象(注入)
​
XXXRegistry :
    注册中心,BeanDefinition 都保存在注册中心里面,用 Map<String, BeanDefinition> beanDefinitionMap 保存了 BeanDefinition
    
3.编码常用的 ApplicationContext
ApplicationContext :
    虽然 BeanFactory 是 Spring 最底层的工厂,但是程序员在编码的时候,最常用的还是 ApplicationContext,为什么使用 ApplicationContext 而不直接使用 BeanFactory 的实现类,肯定是 ApplicationContext 提供了更方便的使用方法

2.ApplicationContext 类结构分析

  • image-20231012222353781
    可以发现 ApplicationContext 比 BeanFactory 增加了 环境感知、事件监听、资源解析、消息等功能,但是我们发现 ApplicationContext 缺少了 Register 的功能,而注册中心又是不可少的功能。
1.思考:Spring 是如何解决 ApplicationContext 缺少了 Registry 的功能的?
思路一:通过实现类(子接口)去继承/实现 Registry 的功能

image-20231012223042299

思路二:通过组合(聚合),将 Registry 实现类聚合到 ApplicationContext 中类,Spring 采用的就是这种方式

image-20231012223055050

2.重点:

在面向对象的设计中,有非常重要的一条准则就是,组合优于继承

继承&组合:

继承:
    继承需要满足 is a 的关系,不是想当然就去使用继承,另外 java 语法中,继承只能单继承,语法限制比较大,继承最大的作用就是用来实现多态的
    
组合:
    组合的最大作用就是代码的复用,两个在实际意义上没有太大关联的类,只能通过组合的方式,去使用另外一个类的功能。
ApplicationContext:
    组合了 DefaultListableBeanFactory,提供对象的创建、对象的查找、对象的存储、对象元信息(BeanDefinition)
    
ApplicationContext 是如何工作的呢?
    ApplicationContext 中包含了 BeanFactory, 而 BeanFactory 是使用 lazy 的方式,在使用的时候才会去创建对象,但是我们在使用 ApplicationContext 的时候,一上来,所有的对象都给你创建好了,那么 ApplicationContext 是怎么做的呢?
    ApplicationContext 底层肯定不会再将创建对象的功能自己再实现一遍,利用已经有的 BeanFactory,来创建对象,然后给保存起来
    ApplicationContext 底层是通过手动的创建 BeanFactory ,然后调用 getBean() 的方式去创建对象,最后将对象保存在 SingletonObjects 中
    
3.ApplicationContext 常用的实现类
3.1.ClassPathXmlApplicationContext

image-20231012224605510

3.2.AnnotationConfigApplicationContext

image-20231012224713346

4.ApplicationContext 处理流程(简版)

image-20231012225842703

3.Beandefinition 以及类体系结构

3.1.类体系结构

image-20231014215130889

  • RootBeanDefinition : 用于 xml 解析结果的封装,但是现在 Spring 建议废除,因为 不支持父子 Bean
  • GenericBeanDefinition : 通用的 BeanDefinition, 也可以用作 xml 解析结果的封装
  • AnnotatedGenericBeanDefinition : 注解 Bean 的定义信息的封装
3.2.如何将 xml 解析成 BeanDefinition

Spring 中通过 BeanDefinitionReader 将 xml 解析成 BeanDefinition

  • BeanDefinitionReader 的类体系结构
  • image-20231014220029931
1. 从类的体系图可以看出,缺少了注解类型的解析器,这里的设计是 Spring 中一个败笔,设计的不怎么好,应该是早期设计的时候,就没有将注解相关的想法给定义出来,所有类的体系中就没有关于注解的解析器,而是单独提供了注解的解析器
1.AnnotatedBeanDefinitionReader
1. 解析注解的 BeanDefinition

image-20231014220959705

比较重要的方法都在图中标出了,主要就是注册 BeanDefinitionRegistry

2.Registry 接口以及实现类 DefaultListableBeanFactory

image-20231014222020084

3.编码过程中的注册的含义是什么?
1. 在开发的过程中,注册的含义就是将 BeanDefinition 注册
    在 Spring 的底层实现里面,意味着:
        1. BeanDefinition 的创建
        2. 将 BeanDenition 注册到 Registry 中

4.Bean 创建的几种方式

1. 通过 xml 的配置文件
2. 通过 @Component|@Controller|@Service|@Repostory 注解
3. @configuration + (@Bean 注解|@Import 注解)
4.1.@Import 注解 的三种运用方式
1. 直接导入某个类 
2. @Import + ImportSelctor 接口的配合使用,可以导入多个类
3. @Import + ImportBeanDefinitionRegistrar 接口的配合使用,一般用在不能获取 Class 对象的情况中,
4.2.各种创建 Bean 的方式的使用场景
1. xml 的方式

这种方式逐渐在被取代了,现在开发一般都是推荐使用注解的方式了

2.通过 @Component|@Controller|@Service|@Repostory 注解

这种方式一般是在程序员开发的过程中,类都是程序员自己创建的

3. @configuration + @Bean

这种方式一般使用在别的框架中类需要加入到 Spring 容器中,可以自己灵活的创建对象

4. @Import 直接导入某个类

不推荐使用,能使用这种方式来实现的,通过 @bean 的方式也可以实现,@Import 一般用在对底层框架的封装,不向用户暴露过多的细节,例如 SpringBoot 的场景启动器的原理

5. @Import + ImportSelctor

存在实际的类型: .class + SPI 机制 , SpringBoot 里面是使用这种方式, 可以批量导入

6.@Import + ImportBeanDefinitionRegistrar

这种一般使用在 class 没有办法获取 : 动态代理的场景,或者动态字节码的场景中,我们动态在 内存中生成 字节码 ,需要配置 FactoryBean 使用,

在 FactoryBean 中,通过动态字节码去创建对象

4.3.@Import + ImportBeanDefinitionRegistrar 场景实战 (Spring + Mybatis 整合)
1. Spring 整合 Mybatis 的关键在于代理对象的创建:
    DataSource + SqlSessionFactory 可以通过 @Bean 的方式来创建,但是根据 XXXDao --> XXXDAOImplProxy 的实现,就很麻烦,我们要将代理对象放入到工厂,但是代理对象 class 我们又不知道,创建对象完成了我们才知道,可是创建完成了,就可以直接放入到 BeanFactory 中了呀,我们可以以一个中间对象来转换一下,将中间对象放入到 BeanDefinition 中,然后通过中间对象来创建我们代理对象,在 Spring 中,通过中间对象来创建实际对象,提供了 FactoryBean 接口

5.@ComponentScan 实现原理

1. @ComponentScan 实现原理  和 new AnnotationConfigApplicationContext("com.jiajia"); 都会扫描给定路径下的 Bean
2. 底层是通过 : ClassPathBeanDefinitionScanner 包扫描器来进行扫描,然后将扫描到的 Bean 注册到 Retistry 中去

6.ApplicationContext 流程详解