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
- 自定义工厂,实现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的创建
- BeanDefinitionMap(key:beanName,value:BeanDefinition)
学习笔记,整理不易。你的支持,我的动力!
更多学习资料,IT系列课程,请关注vx公众号:豆萌萌 网课大咖
为您提供全网最全的学习资料
更有面试题整理,金三银四冲刺,IT电子书籍等
你需要的,我恰好有,愿意推荐给你哦!