SpringBoot源码阅读

一、Spring容器中Bean的创建(Spring的IOC控制反转特性)

1.学习目标

Spring容器中的Bean是何时创建的?

为什么可以从容器获取到对象实例?

 

 

 

2.探究过程

 

 

 

3.结论

①Spring容器中的Bean是何时创建的?

非懒加载的单实例bean是在Spring容器创建的时候,通过beanFactory的doGetBean()方法来创建的对象;

——doGetBean()方法内部是通过反射获取运行时类的构造器,然后调用其构造器创建运行时类的对象;

 

 

②为什么可以从容器内获取到对象实例?

容器在创建对象之后,还会将这个bean放入一个单例bean的map集合(singletonObjects)中存储,key值就是我们配置bean时的名称,

在调用容器的getBean()方法获取对象实例时,也是通过调用doGetBean()方法,从该map集合中根据key值取出对应的对象;

 

 

③多次调用getBean()方法获取对象实例,不会重复创建新的对象,而是直接返回最初创建的那个对象,因为这个对象限定单例;

 

 

 

 

 


 

二、Spring中BeanDefinition的创建

1.学习目标

BeanDefintion是什么?是什么时候创建的?

 

 

 

2.探究过程

 

 

 

 

3.结论

①BeanDefintion是什么?

BeanDefinition就是一个用于存储bean标签中各属性值的对象,容器的beanFactory会根据BeanDefinition中的内容来创建bean;

 

 

②BeanDefintion是什么时候创建的?

在容器创建时会先创建一个beanFactory,然后使用XmlBeanFactoryReader去读取xml配置文件,对其中的各个标签进行解析,

然后创建BeanDefinition对象来存储bean标签中的各个属性的值;

 

 

③BeanDefintion是如何存入BeanFactory中的?

BeanDefinition对象在创建之后会存入BeanFactory中的一个存放BeanDefinition对象的map集合beanDefinitionMap中,key值为bean的名称,

bean的名称还会存入一个List集合BeanDefinitionNames中,这个List集合存储了当前beanFactory中全部bean的名称;

 

 

 

 

 


 

三、SpringBoot中Spring容器的创建与获取

 

1.学习目标

SpringBoot项目中Spring容器是在什么时候创建和刷新的?

 

 

 

 

2.探究过程

 

 

 

 

3.结论

①SpringBoot项目中Spring容器是在什么时候创建和刷新的?

在启动类中调用SpringApplication的run()方法时,会根据容器的类型创建对应的容器对象,并调用容器的refresh()方法对容器进行刷新;

 

 

②SpringBoot中@ComponentScan注解是如何起作用的?为什么启动类即使不加@Component注解,容器也会自动扫描启动类所在package中有无加了@Component注解的类?

容器在刷新时会调用BeanFactory后置处理器(BeanFactoryPostProcessor)进行处理,

其中的配置类处理器(ConfigurationClassPostProcessor)会调用配置类解析器(ConfigurationClassParser)中的parse()方法去解析处理配置类,

在此方法中会进一步调用ComponentScan注解解析器(ComponentScanAnnotationParser)的parse()方法对@ComponentScan注解进行解析处理,

解析时如果发现没有配置扫描范围basePackage,就会默认将配置类所在的包作为basePackage进行组件扫描;

(简而言之,容器刷新时会调用配置类解析器的parse()方法对配置类中的@ComponentScan注解进行解析处理,对注解指定的包进行扫描,如果发现没有声明扫描范围basePackage,则默认对配置类所在的包进行扫描)

 

 

补充:

配置类解析器(ConfigurationClassParser)除了对组件扫描@Componentscan进行处理,还会对@PropertySource、@Import、@ImportSource、@Bean等注解进行解析处理;

 

 

 

 

 

 


 

四、@Import注解

 

1.@Import注解的作用

①可以用于引入不在启动类所在包下的配置类

示例:

@Configuration
@Import(CustomConfig.class)
public class UserConfig {
}

 

 

②引入ImportSelector

ImportSelector的作用:可以根据字符串数组(数组元素为类的全类名)来批量地加载指定的bean(通常也是配置类);

 

配置类,包含要加载的类的全类名:

创建ImportSelector的子类,重写其中的selectImports()方法:

使用@Import注解引入该子类:

 

 

③引入ImportBeanDefinitionRegistrar

ImportBeanDefinitionRegistrar的作用:实现动态Bean的装载,尤其是装载动态代理对象,

例如在使用MyBatis时,只要创建Mapper接口就可以实现操作数据库的功能,这个过程中并没有手动创建接口的实现类,这就是因为MyBatis的启动器通过使用ImportBeanDefinitionRegistrar实现了Mapper接口的动态代理对象的装载;

Ⅰ.简单使用:通过ImportBeanDefinitionRegistrar将单个对象注入容器中;

Ⅱ.进阶使用:通过ClassPathBeanDefinitionScanner扫描包并找出添加了指定注解的类,再将这些类的BeanDefinition对象注册到容器中;

Ⅲ.终极使用:通过ClassPathBeanDefinitionScanner扫描包并找出添加了指定注解的接口,将这些接口的BeanDefinition对象转换成FactoryBean的BeanDefinition对象,进而创建FactoryBean,让Spring容器通过FactoryBean来创建指定接口的动态代理对象,并将这些对象注入到容器中;

 

 

 

 

2.学习目标

@Import注解为什么能引入配置类?

@Import引入ImportSelector是如何生效的?

 

 

 

3.探究过程

 

 

 

 

4.结论

配置类解析器(ConfigurationClassParser)中的doProcessConfigurationClass()方法在处理启动类时会先递归扫描其上所有的@Import注解,获取其中的value属性值并封装成SourceClass对象添加到集合中,

然后遍历这个集合,并进行判断:

若是ImportSelector的子类,则会通过反射创建其对象,调用其方法获取存储全类名的字符串数组,将数组元素封装成SourceClass对象,再递归地对每个对象继续进行判断;

若是ImportBeanDefinitionRegistrar的子类,

若是加了@Configuration的配置类,则会继续调用processConfigurationClass()方法来将这些SourceClass对象添加到一个配置类集合中,再将集合元素加载成BeanDefinition对象,最终创建bean;

 

 

 

 

5.前置知识补充

①FactoryBean的作用与应用场景

一些对象的创建过程比较复杂,可以让Spring容器调用FctoryBean来实现这些对象的创建,尤其是一些动态代理对象的创建;

 

 

②FactoryBean的使用

Ⅰ.基本使用:让Spring容器通过FactoryBean来创建普通对象;

Ⅱ.进阶使用:让Spring容器通过FactoryBean来创建指定接口的动态代理对象;

 

 

 

 

 


 

posted @ 2023-11-14 11:34  Avava_Ava  阅读(13)  评论(0编辑  收藏  举报