Spring源码阅读——笔记整理

IOC 控制反转

  • 是一种设计理念,用来解决层层之间的 耦合

BeanFactory

  • 是Spring的顶层接口,使用了简单工厂模式,负责生产Bean

BeanFactory与Application区别

  • 都有生产bean的能力,Application的顶级父类是BeanFactory
  • 但是BeanFactory更专注于生产Bean,Application可以扫包,可以定制化需求(相当于工厂与店家的区别,店家更能了解客户需求,进行细节化定制操作)

BeanDefinition

  • 是Spring的顶层接口,它封装了生产Bean的一切原料

Bean生命周期

1、实例化  		--反射获取内的内容
2、填充属性	 --填充对应的属性
3、初始化			
4、装进Map中

ClasspathXmlApplicataionContext

构造方法

1、得到资源路径解析器

  • 在父类AbstractApplicationContext的构造方法中,new PathMatchingResourcePatternResolver(this)
// 是资源路径匹配解析器
new PathMatchingResourcePatternResolver(this);

2、对xml文件进行占位符替换

  • 由于spring的xml文件名中,可能存在${xxxxx}的占位符,所以,对占位符进行了真实值的替换
    • 如:spring-${username}.xml 最终替换成:spring-xrq.xml
// 参数为xml配置文件名
setConfigLocations(configLocations);
  • 为考虑极端情况:可能存在spring-${${${xxx}}}.xml这种奇怪的名字,但是内部使用递归调用解析,所以不会担心会有问题


学习笔记,整理不易。你的支持,我的动力!

更多学习资料,IT系列课程,请关注vx公众号:豆萌萌 网课大咖

为您提供全网最全的学习资料

更有面试题整理,金三银四冲刺,IT电子书籍等

你需要的,我恰好有,愿意推荐给你哦!


Spring IOC容器加载过程

1、实例化BeanFactory的实现类。调用父类构造方法

实例化DefaultListableBeanFactory(用于生产Bean)

    • 调用AnnotationConfigApplicationContext的构造方法时,会默认调用父类的构造方法,父类的构造方法中,实例化DefaultListableBeanFactory

2、this(),注册创世纪PostProcessor的BeanDefinition

Ⅰ、调用容器的构造方法

  • ① 有参构造方法,可以接受多个配置类,一般情况下传入一个配置类
  • ② 所传入的配置类,传统意义的带上了@Configuration注解,也有带有@Component,@Import,@ImportResource,@Service,@CompentScan等注解的配置类,前者称为Full配置类,后者称为Lite配置类

Ⅱ、调用了此类的this()无参构造方法

  • ① 调用AnnotationConfigApplicationContext的构造方法,对reader和scanner进行实例化,分别为
    • AnnotatedBeanDefinitionReader,使用注解的Bean定义读取器
    • ClassPathBeanDefinitionScanner,当手动调用.scan()方法扫包时才会使用到

Ⅲ、实例化BeanDefinition读取器:AnnotatedBeanDefinitionReader,注册创世纪PostProcessor的BeanDefinition

①、使用了AnnotatedBeanDefinitionReader类注册Bean定义

  • 它没有继承其他Spring的类,父级是Object。构造方法中,传入了BeanDefinition的实现类AnnotationConfigApplicationContext,对创世纪的BeanPostProcessor进行了Bean定义的注册

②、将Bean定义存入DefaultListableBeanFactory容器中

  • 将创世纪的PostProcessor注册BeanDefinition
    • 使用了DefaultListableBeanFactory实现类的registerBeanDefinition方法,这里有一个HashMap属性beanDefinitionMap(存储Bean定义),Key为BeanName,Value为BeanDefinition,还有一个List属性beanDefinitionNames(存储BeanName)。由此可见DefaultListableBeanFactory实际上是一个容器,存储了所有的BeanDefinition。

3、register(),注册配置类的Bean定义

  • 1、通过AnnotatedGenericBeanDefinition的构造方法,获得配置类的BeanDefinition,这里是不是 似曾相似,在注册ConfigurationClassPostProcessor类的时候,也是通过构造方法去获得 BeanDefinition的,只不过当时是通过RootBeanDefinition去获得,现在是通过 AnnotatedGenericBeanDefinition去获得;
  • 2、判断需不需要跳过注册,Spring中有一个@Condition注解,如果不满足条件,就会跳过这个类的 注册。
  • 3. 然后是解析作用域,如果没有设置的话,默认为单例;
  • 4、获得BeanName;
  • 5、5. 解析通用注解,填充到AnnotatedGenericBeanDefinition,解析的注解为Lazy,Primary,DependsOn,Role,Description;
  • 6. 限定符处理,不是特指@Qualifier注解,也有可能是Primary,或者是Lazy,或者是其他(理论上 是任何注解,这里没有判断注解的有效性)
  • 7、把AnnotatedGenericBeanDefinition数据结构和beanName封装到一个对象中(这个不是很重 要,可以简单的理解为方便传参)
  • 8、注册,最终会调用DefaultListableBeanFactory中的registerBeanDefinition方法去注册:
    • 同样的,会装进beanDefinitionMap,以及BeanDefinitionNames中

4、 refresh()

①、prepareRefresh()

  • 此方法主要做了一些刷新前的准备工作,和主流程关系不大,主要是保存了容器的启动时间,启动标志等。

②、ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory()

  • 这个方法和主流程关系也不是很大,可以简单的认为,就是把beanFactory取出来而已。XML模式下会在这里读取BeanDefinition

③、prepareBeanFactory(beanFactory) 准备Bean工厂

1. 设置了一个类加载器 
2. 设置了bean表达式解析器 
3. 添加了属性编辑器的支持 
4. 添加了一个后置处理器:ApplicationContextAwareProcessor,此后置处理器实现了BeanPostProcessor接口 
5. 设置了一些忽略自动装配的接口 
6. 设置了一些允许自动装配的接口,并且进行了赋值操作 
7. 在容器中还没有XX的bean的时候,帮我们注册beanName为XX的singleton bean

④、postProcessBeanFactory(beanFactory)

  • 留给子类实现的扩展点,空方法

⑤、invokeBeanFactoryPostProcessors(beanFactory) 调用Bean工厂后置处理器

⑥、registerBeanPostProcessors(beanFactory);

  • 实例化和注册beanFactory中扩展了BeanPostProcessor的bean。
    • AutowiredAnnotationBeanPostProcessor(处理被@Autowired注解修饰的bean并注入)
    • RequiredAnnotationBeanPostProcessor(处理被@Required注解修饰的方法)
    • CommonAnnotationBeanPostProcessor(处理@PreDestroy、@PostConstruct、@Resource等多个注解的作用)等。

⑦、initMessageSource()

  • 初始化国际化资源处理器. 不是主线代码

⑧、initApplicationEventMulticaster()

  • 创建事件多播器

⑨、onRefresh()

  • 模板方法,在容器刷新的时候可以自定义逻辑,不同的Spring容器做不同的事情

⑩、registerListeners()

  • 注册监听器,广播early application events

⑪、finishBeanFactoryInitialization(beanFactory)

  • 实例化我们剩余的单实例bean.
  • 比如invokeBeanFactoryPostProcessors方法中根据各种注解解析出来的类,在这个时候都会被初始化
  • 实例化的过程各种BeanPostProcessor开始起作用

⑫、finishRefresh()

  • 最后容器刷新 发布刷新事件(Spring cloud也是从这里启动的)

学习笔记,整理不易。你的支持,我的动力!

更多学习资料,IT系列课程,请关注vx公众号:豆萌萌 网课大咖

为您提供全网最全的学习资料

更有面试题整理,金三银四冲刺,IT电子书籍等

你需要的,我恰好有,愿意推荐给你哦!


refresh() 重点方法整理

⑤、InvokeBeanFactoryPostProcessors,调用Bean工厂后置处理器

执行顺序:

1、如果有外部传来的PostProcessor集合,最先处理外部传入的PostProcessor

2、处理BeanDefinitionPostProcessor(子),先处理子类,再处理父类

3、处理BeanFactoryPostProcessor(父)

二、先处理子类: BeanDefinitionPostProcessor,Bean定义后置处理器接口

1、筛选出实现@PriorityOrdered注解的配置类,最先解析

  • 先筛选出所有标注了 @PriorityOrdered 注解的配置类

2、筛选出标注了@Order注解的配置类,进行解析(排除已解析过的)

3、筛选出未实现排序注解的配置类进行解析(排除已解析过的)

  • 以上三种顺序,分别都调用 invokeBeanDefinitionRegistryPostProcessors(),内部使用parse()方法进行解析配置类

解析配置类:doProcessConfigurationClass()方法

1、解析标注了 @PropertySource注解的配置类

  • 解析注解内部所包含的所有属性值,并添加属性源

2、解析标注了 @ComponentScan 扫包注解的配置类

  • 通过注解的属性值,找到扫包路径,并将包下所有符合条件的类,注册为BeanDefinition
  • 循环BeanDefinition,筛选出配置类,进行解析
  • 包含递归

3、解析标注了 @Import 导入注解的配置类(SpringBoot的自动装配,从这里导入并解析自动配置选择器)

4、解析配置类中标注了 @ImportResource 注解的配置类

5、解析配置类中 标注了 @Bean 注解的方法

  • 这里只是解析出来,该方法外部,再进行注册BeanDefinition

  • parse()外部,进行loadBeanDefinition

⑩、finishBeanFactoryInitialization();

  • 实例化Bean
  • 调用了AbstractBeanDefinition类的doGetBean()方法

doGetBean();

1、去缓存中获取Bean

  • 第一次进入该方法,还未创建Bean,所以缓存中取不到Bean

2、是否存在子父容器

3、是否有dependsOn(依赖)的Bean

  • 如果存在,则优先创建依赖的Bean

4、创建单例Bean

5、进入getSingleton()方法

  • ①、标记Bean即将开始创建
  • ②、getObject(),实际上返回了 getSingleton() 的 Lambda 表达式中

6、进入 createBean() 方法

  • ①、该方法中第一次调用到了Bean的后置处理器
    • 如果使用了该后置处理器的情况下,直接返回由该扩展点创建好的Bean,return Bean

    • 如果没有使用该后置处理器,则继续往下,由Spring帮我们创建Bean

7、doCreateBean() ,Spring开始真正的创建bean

doCreateBean()方法中,真正开始创建Bean
1、实例化
2、填充属性
3、初始化

  • createBeanInstance(),此方法用于实例化Bean

    • 使用反射实例化Bean
      • 通过无参构造函数实例化Bean
        • BeanDefinition中有该Bean的Class文件,从Bean定义中读取Class文件,实例化Bean
        • 自动装配可以使用有参构造函数的方式
    • 使用工厂模式实例化Bean
      • 自定义工厂,实现Bean的实例化
  • 加入三级缓存中,以防出现循环依赖问题

  • populateBean(),进行属性赋值

  • initializeBean(),进行初始化

Bean创建:
	1、@preDestroy		   InitDestroyAnnotationBeanPostProcessor
	2、@PostConstruct      InitDestroyAnnotationBeanPostProcessor
	3、@Resource		   CommonAnnotationBeanPostProcessor
	4、三级缓存概念,设计思路梳理,推导过程
	
Bean创建:
	1、预先设置三级缓存的Lambda表达式
	2、populateBean,属性赋值
		思路:基本类型,引用类型 赋值方式不一样。
			基本类型:直接完成赋值操作(四类八种基本数据类型)
				集合中也可能存在引用数据类型
			引用数据类型:从容器中获取对象,有则赋值(存在多个时的选择问题),没有则创建
				注入类型:不注入、构造器注入、byName、byType
			数组、集合、properties等处理(这些里面既存在基本也存在引用类型)
		并且还需要考虑类型转换(如配置文件中有个 1,那么是注入String还是Int)
		
		Ⅰ、byName方式,使用getBean()
			①、筛选出非简单属性:unsatisfiedNonSimpleProperties
			②、属性挨个循环遍历时,使用getBean()获取对象,有则获取,无则创建
			③、得到bean后,填充进属性值里
			④、注册依赖的bean(dependsOn)
		Ⅱ、byType方式,使用resolveDependency()方式
			①、首先支持四种注入,Optional注入、延迟注入、懒加载注入,如果以上都没有注入成功,那么进入正常注入情况doResolveDependency注入
			②、利用缓存、@Value,集合、数组、Set、Map、单个依赖等方式进行分类型注入
		Ⅲ、使用@Autowired注解进行属性赋值
		
		
		
		
getBean —————— doGetBean —————— createBean —————— doCreateBean

doCreateBean:
	createBeanInstance	实例化对象
	设置用于三级缓存的回调
	注解预处理的解析
	populateBean,属性赋值
	

	
2、populateBean方法步骤
	①、调用实现了InstantiationAwareBeanPostProcessor接口的后置处理器,进行属性赋值,返回值设置为false时,直接不处理后续操作直接返回,如果为true,则允许后续覆盖(PostProcessorAfterInstantiation方法处理)
	②、根据配置文件上的autowired属性,来进行byName、byType的属性赋值(byName、byType方法处理)
	③、将对象中定义了@Autowired注解的属性进行解析,并完成对象或属性注入(postProcessProperties方法处理———— autowiredAnnotationAfterBeanPostProcessor)
	④、根据property标签定义属性后,完成相应的属性赋值操作
	
	
3、InitializeBean初始化方法步骤:
	①、执行Aware接口的后置处理器方法
	②、调用执行init-method方法
		实现了inititalizingBean接口之后调用afterPropertieset方法
		调用执行用户自定义的初始化方法
	③、执行after的初始化方法:AOP动态代理的功能

			

五、InvokeBeanFactoryPostProcessors(beanFacorty, 自定义PostProcessor集合)

	执行顺序:
		1、如果有外部传来的PostProcessor集合,最先处理外部传入的PostProcessor
		2、处理BeanDefinitionPostProcessor(子),先处理子类,再处理父类
		3、处理BeanFactoryPostProcessor(父)
	
	执行BeanDefinitionPostProcessor
		执行前已将BeanFactory强转为BeanDefinitionRegistry(DefaultListableBeanFactory是该类的子类)
		从BeanDefinition中取得所有属于BeanDefinitionPostProcessor的,按照以下顺序:
			1、包含 @PriorityOrdered 注解的
			2、包含 @Order 注解的
			3、和没有实现排序注解的
		
		进入ProcessConfigBeanDefinitions,筛选和排序,确定需要解析的配置类
		调用parse()方法,开始执行解析配置类(asSourceClass()方法进行了递归处理)
		调用parse()方法,开始执行解析配置类(asSourceClass()方法进行了递归处理)
		真正开始解析配置类:doProcessConfigurationClass
			处理注解:
				@PropertySources,读取配置文件属性的注解
					处理name、encoding、factory、valued等一系列属性
				@ComponentScan,组件扫描的注解(将扫包的所有配置类进行解析)
					通过属性获取basePackage等属性,扫描对应的包,通过doScan()方法,扫描BeanDefinition,符合的BeanDefinition,进行注册BeanDefinition,加入Map中,
					扫描结束后,将扫描出并且注册的所有BeanDefinition,进行递归,解析配置类
				@Import,导入注解
				@ImportResource,导入资源注解
				@Bean
	


ConfigurationClassPostProcessor类
用于解析配置类

	入口:InvokeBeanFactoryPostProcessors中,筛选配置类,调用PostProcessorRegister

	配置类的选择范围:
		full级别:标注了@Configuration注解的配置类
		lite级别:标注了@Import、@ImportResource、@Component、@ComponentScan 四个注解的配置类
		但排除了有@Bean注解的类
		
	这里面有特别多的递归,因为注解可能包含注解,类可能包含子类
	最终层层递归,找到所有符合配置类的BeanDefinition,进行解析
	只会初始化BeanDefinition,不会实例化Bean
		
	
	
SpringBoot自动配置原理

    通过InvokeBeanFactoryPostProcessors,进入配置类解析
	解析到@Import注解
	Set<SourceClass> imports = new LinkedHashSet<>();
	Set<SourceClass> visited = new LinkedHashSet<>();
	collectImports(sourceClass, imports, visited);
	
	
	
	
	
	
	
ConversionService	转换器服务
Converter:类型一对一转换
GenericConvers:类型一对多转换
ConvertFactory:类型多对多转换


学习笔记,整理不易。你的支持,我的动力!

更多学习资料,IT系列课程,请关注vx公众号:豆萌萌 网课大咖

为您提供全网最全的学习资料

更有面试题整理,金三银四冲刺,IT电子书籍等

你需要的,我恰好有,愿意推荐给你哦!


IOC加载流程:XML配置文件的解析流程

1、初始化ClassPathXmlApplicationContext对象

  • new ClasspathXmlApplicationContext([可变参数:配置文件名])

1、构造方法

  • 调用父类构造方法,创建了一个xml路径的解析器
  • 获取系统环境变量后,解析xml文件名,并替换

    • 如存在${},则使用系统变量值对相应内容进行替换,未找到可替换的值则会报错
    • 不存在则不做处理
注:考虑到配置文件可能存在名为spring.${${}}.xml嵌套的情况,该位置存在递归处理操作

2、配置文件的解析以及Bean定义的加载流程

  • refresh方法的第二个方法,在ClassPathXmlApplicationContext的实现中,起到了重要的作用
    • 1、解析了xml配置文件
    • 2、加载了Bean定义

1、添加dtd和xsd格式的解析器

  • 添加xml实体处理器(dtd格式和xsd格式)
    • 在 new ResourceEntityResolver()的父类构造方法中,添加了格式解析器

注:格式解析器中,分别通过了
dtd:spring-beans.dtd
xsd: META-INF/spring.schemas
两个文件加载出解析规则的类,用于后续的解析工作

2、dtd和xsd解析规则的指定文件位置

  • dtd文件解析规则:在spring-beans包下的spring-beans.dtd文件

  • xsd文件解析规则:在spring-context包下的spring.schemas文件

3、将xml文件转换成Resource

4、通过Resource的输入流(相当于将xml读入),进行遍历整个输入流,加载Bean定义

5、Resource转换成Docment文档格式,遍历文档中的元素,加载Bean定义

  • 注册Bean定义方法的参数中,有个createReaderContext方法,创建了默认的命名空间解析器,后续会用到

6、从根元素开始遍历文档

7、根据是否为默认命名空间,来进行不同方式的解析

  • 是默认命名空间

  • 非默认命名空间

3、默认命名空间

1、默认命名空间:处理命名空间中的bean元素

  • bean元素的解析工作

2、默认命名空间:详细解析bean标签

3、默认命名空间:成功返回BeanDefinitionHolder后,真正地将每个Bean定义进行注册

4、非默认命名空间

1、命名空间地址

  • 除了spring默认的几个命名空间外,还有很多其余的命名空间。想要使用非默认的标签,则需要引用相应的命名空间地址
    • 如:使用了context标签,则引入了context的命名空间地址

  • xml文件里,beans标签里面的内容,就是命名空间i地址

2、命名空间的获取及处理类的映射

  • 获取到命名空间的URL

  • 取得处理器的时候,用到了this.readerContext类,默认的命名空间处理器类,在上方createReaderContext()方法中已经创建完毕
  • 通过 META-INF下的spring.handlers文件,映射出实际处理context命名空间的处理类全路径

  • 加载处理器类

    • 举例,context的处理器类中的init()方法如下:

    • 将每个标签对应的标签解析类,准备好,以便解析时调用
比如:当前处理的标签时context
1、那么,在生成Docment文档时,就将xml文件中的命名空间地址:http//:www.springframework.org/schema/context 存入到了NamespaceURL中
2、在方法第一步,就可以获取到此Url
3、获取到此URL后,去寻找实际的处理器类,在本地的META—INF/spring-handlers文件中,实际找到了
org.springframework.context.config.ContextNamespaceHandler类
4、获取到处理类后,通过该处理类,进行实际的解析处理工作

3、获得标签的处理器

  • 寻找处理器类,找到后返回

4、开始解析

1、开始解析前的准备工作,基本属性的设置

  • doParse,会进入上面实际寻找到的处理器类的doParse方法中执行,比如如果是context标签的property-placeholder属性,则会进入PropertyPlaceholderBeanDefinitionParser()类的doParse方法进行解析

2、开始解析

  • PropertyPlaceholderBeanDefinitionParser()

  • 如果存在属性,则设置对应属性的值

下方图片是另一个解析器,想表达的意思是,使用了的属性,全部会有对应的解析器进行解析

3、解析完成

  • 会调用注册Bean定义的方法进行注册

5、分析完自定义标签解析流程,总结出如果自己想要加自定义标签的步骤

同理,如果想要自己定义标签,则需要:
1、创建一个命名空间处理器类
2、创建一个spring.handlers文件,并指定该处理器类的映射
3、创建对应标签的解析类
5、标签的解析器类中,动议doParse方法,进行标签解析的逻辑编写
6、定义命名空间处理器类时,定义init()方法,添加各个标签对应的解析类

5、注册BeanDefinition

  • 注册bean定义的位置,在解析元素时,解析到bean标签后,根据标签属性组装好一个BeanDefinitionHolder后进行的

1、进行Bean定义注册以及别名注册

2、检查是否有同名Bean定义,并确定是否可以进行覆盖

3、注册Bean定义

  • 注册Bean定义的操作实际上就是将bean定义加入两个集合中
    • BeanDefinitionMap(key:beanName,value:BeanDefinition)
      • 后续创建时通过名字,取得对应bean定义信息来开始创建
    • BeanDefinitionNames(list集合,装的时beanName)
      • 该list,在后续创建bean时,循环此集合,通过拿到每一个bean的名字,去BeanDefinitionMap中取bean定义信息,进行Bean的创建


学习笔记,整理不易。你的支持,我的动力!

更多学习资料,IT系列课程,请关注vx公众号:豆萌萌 网课大咖

为您提供全网最全的学习资料

更有面试题整理,金三银四冲刺,IT电子书籍等

你需要的,我恰好有,愿意推荐给你哦!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值