springboot 启动过程定义过程
2021-12-03 15:58:58 34 举报
AI智能生成
springboot 启动过程
作者其他创作
大纲/内容
1.如果有 \"org.springframework.web.reactive.DispatcherHandler\" 类,则是 WebApplicationType.REACTIVE 类型
2.如果有 { \"javax.servlet.Servlet\
如果是返回这个,后面会创建 AnnotationConfigApplicationContext 容器对象,但基本上都是2
3.如果都没有,则返回 WebApplicationType.NONE
1.判断容器类型,赋值给 webApplicationType 属性(留着后面去创建IOC容器对象)
META-INF/spring.factories
2.它会读取当前工程下的 META-INF/spring.factories 文件,还会读取jar包下的这个文件
key 是 META-INF/spring.factories 文件中的key
value 是 META-INF/spring.factories 文件中的value ,是一个集合
value 还是一个Map
3.全部读取出来后,会放到Map缓存中
1.读取 META-INF/spring.factories 文件中配置的所有数据
利用反射Class.forName() 创建初始化器对象,value 存放的都是全类名
context.initializer.classes=com.qiuguan.springboot.initializer.MyThreeApplicationInitializer
它可以读取springboot 配置文件中指定的初始化器
然后同样通过反射创建对象
将所有的 初始化器排序,可以实现Order接口,值越小越先执行,其中有一个初始化器:DelegatingApplicationContextInitializer
它实际上是由 DelegatingApplicationContextInitializer 这个初始化器加载的
1.还可以从 springboot 的配置yaml(或者properties)文件中加载:
2.还可以通过 new SpringApplication() 对象,手动添加
初始化器除了从 META-INF/spring.factories 文件中加载以外,还可以从以下两个位置加载
推荐使用从 META-INF/spring.factories 中加载
2.上面都加载完成后,根据 key = org.springframework.context.ApplicationContextInitializer 获取所有的初始化器
3.保存到 SpringApplication 对象的 initializers 属性中
4.作用:它其实就是spring容器的一个回调接口,在容器刷新之前执行,主要作用是给可以自定义一些属性值
初始化器
2.设置初始化器
1.读取 META-INF/spring.factories 文件中配置的所有数据,因为步骤2已经读取过,所以会放到缓存中
利用反射创建对象
注意:它创建对象时,必须声明一个有参构造器才可以
2.直接从缓存中根据 key=org.springframework.context.ApplicationListener 读取监听器
3.保存到 SpringApplication 对象的 listeners 属性中
4.作用:
监听器
3.设置监听器
1.先创建 SpringApplication 对象
1.声明IOC容器对象为 ConfigurableApplicationContext, 此时还没有创建对象,留着后面根据 webApplicationType 属性去创建
根据 key= org.springframework.boot.SpringApplicationRunListener 获取所有的监听器
1.它和步骤1.3中是一样的,由于1.2中加载并保存到缓存中,所以这里直接从缓存中获取
事件的发布最终都是它进行发布的,比如spring容器刷新的最后一步
创建这个对象时其内部也创建好了事件派发器 SimpleApplicationEventMulticaster
EventPublishingRunListener
其中有一个还算重要的监听器是 事件发布监听器
2.这个监听器提供了容器启动各个阶段的方法,以供回调,主要做一些监控
事件发布监听器 EventPublishingRunListener 发布 ApplicationStartingEvent 事件
3.首先调用 SpringApplicationRunListener#starting()
2.获取所有的 SpringApplicationRunListener
1.根据 webApplicationType 属性,创建 new StandardServletEnvironment() 对象
事件发布监听器 EventPublishingRunListener 发布 ApplicationEnvironmentPreparedEvent事件
2.调用 SpringApplicationRunListener#environmentPrepared()
3.环境准备对象 ConfigurableEnvironment
1.和步骤1相呼应
创建这个对象时,会先调用父类构造器, 创建 DefaultListableBeanFactory 对象
beanName=org.springframework.context.annotation.internalConfigurationAnnotationProcessor
implements BeanDefinitionRegistryPostProcessor
ConfigurationClassPostProcessor
这个处理器非常重要,用来解析配置类,给容器注册bean定义组件的
注册了 ConfigurationClassPostProcessor 处理器
beanName=org.springframework.context.annotation.internalAutowiredAnnotationProcessor
implements BeanPostProcessor
AutowiredAnnotationBeanPostProcessor
用来处理 @Autowired 注解注入组件的
还可以处理 @Value 注解
还可以处理 JSR-330 的 @Inject 注解注入组件
功能
注册了 AutowiredAnnotationBeanPostProcessor 处理器
beanName=org.springframework.context.annotation.internalCommonAnnotationProcessor
implements BeanPostProcessor
CommonAnnotationBeanPostProcessor
用来处理 @Resource 注解注入组件的
还可以处理初始化注解 @PostConstruct
还可以处理销毁注解 @PreDestroy
注册了 CommonAnnotationBeanPostProcessor 处理器
还有一些其他的处理器
new 这个对象时 BeanDefinitionRegistry 注册了很多 bean 定义信息
AnnotatedBeanDefinitionReader
这个类在做包扫描中是一个非常重要的对象
ClassPathBeanDefinitionScanner
创建这个对象时,其内部还会创建两个对象
这个类实现了 BeanDefinitionRegistry 接口,可以注册bean定义信息
2.创建的IOC容器对象是 AnnotationConfigServletWebServerApplicationContext
4.创建 IOC 容器对象赋值给 ConfigurableApplicationContext
1.前面提到的都只是获取以及保存初始化器,在这里开始调用初始化器的方法
他可以读取springboot 配置文件中制定的初始化器
2.获取后利用反射创建对象,然后调用初始化方法
2.所有初始化器中有一个优先级最高的(order=0) DelegatingApplicationContextInitializer 初始化器
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {}
3.还有一些初始化器执行的时候会给容器对象 AnnotationConfigServletWebServerApplicationContext 添加 ApplicationListener
1.调用所有的初始化器
事件发布监听器 EventPublishingRunListener 发布 ApplicationContextInitializedEvent 事件
2.调用 SpringApplicationRunListener#contextPrepared()
3.获取 DefaultListableBeanFactory 对象,注册一些必要的组件
1.内部会创建 AnnotatedBeanDefinitionReader 对象(都是new的)
2.内部会创建 ClassPathBeanDefinitionScanner 对象(都是new的)
1. 创建 BeanDefinitionLoader 对象
主配置类在这里就添加到了 bean 定义中
将主配置类添加到 BeanDefinitionRegistry 中
进而由内部的 AnnotatedBeanDefinitionReader#register(Class<?> source)
2.调用 BeanDefinitionLoader#load()
4.获取“主配置类”,然后调用 load() 方法
1.遍历所有的 SpringApplicationRunListener 监听器执行 contextLoaded() 方法,其中有一个spring 提供的 EventPublishingRunListener
1.获取 SpringApplication 对象中的所有监听器,就是创建 SpringApplication 时从 META-INF/spring.factories 文件中加载的
2.遍历所有的监听器,设置到当前容器对象 AnnotationConfigServletWebServerApplicationContext 中
事件发布监听器 EventPublishingRunListener 发布 ApplicationPreparedEvent 事件
2.EventPublishingRunListener#contextLoaded()
2.调用 SpringApplicationRunListener#contextLoaded()
5.准备容器
DefaultListableBeanFactory
1.获取 BeanFactory 【obtainFreshBeanFactory()】
设置BeanFactory的类加载器、设置表达式解析器
添加部分BeanPostProcessor【ApplicationContextAwareProcessor】
设置他们的目的是 不能自动注入这些接口类型的实现。
设置忽略的自动装配的接口
注册可以解析的装配,可以直接在任何组件中直接注入(@Autowired)
以后如果想用,则直接注入即可。
给BeanFactory中注册一些能用的组件
2.准备 BeanFactory【prepareBeanFactory(beanFactory)】
AnnotationConfigServletWebServerApplicationContext 容器类进行了重写,但是我看方法内部没有调用实用逻辑
留给子类重写
3.后置处理 BeanFactory【postProcessBeanFactory(beanFactory)】
implements BeanDefinitionRegistry
我们的工厂对象是 DefaultListableBeanFactory
1.由于我们的 beanFactory 对象是 BeanDefinitionRegistry 类型的
1.作用:从单例池中或者bean定义容器中获取 指定类型Bean的所有子类名字
1.如果没有实现,则跳过
所以一些非普通的bean对象在这里就进行了创建
beanFactory.getBean() 方法如果不能从单例池中获取对象,那么就会去创建对象,然后保存到单例池中
2.前面创建容器对象AnnotationConfigServletWebServerApplicationContext时,其内部的 AnnotatedBeanDefinitionReader 对象会注册bean定义信息,其中就有 ConfigurationClassPostProcessor,切好它实现了 BeanDefinitionRegistryPostProcessor 处理器接口
BeanFactoryPostProcessor
BeanPostProcessor
AopInfrastructureBean
EventListenerFactory
根据 beanName 获取 BeanDefinition 对象,如果实现这些接口,则不满足成为候选配置类
根据 beanName 获取 BeanDefinition 对象,看是否有 @Configuration 注解,如果有,则添加到Set集合中
2.判断是否为候选的配置类
1.这个方法非常非常复杂,但是这里也会去创建BeanDefiniton 对象,但是只会去创建 @ComponentScan 注解扫描到的,其他的像 @Import 注解导入的,@Bean 导入的,@ImportSource 导入的等等 都不会在这里创建BeanDefinition 对象,而是要下放到步骤2中
2.
1.ConfigurationClassParser#parse
2.ConfigurationClassBeanDefinitionReader#loadBeanDefinitions
1.解析主要有两个方法
0.请先看下 1- 9的总体过程,然后在看每一步骤的细节
说明,在看下面的内容时,请先看 1-9 的总体内容,细节先不要看,然后1-9 看完了,在从头一点一点看细节
刚开始进来,第一次执行,这个配置类就是主配置类,所以先看下主配置类执行1-9的过程
由谁导入的
1.图1
imortedBy
2.图2
3.图3
当前配置类是由谁导入的
Set<ConfigurationClass> importedBy
将 @ImportResource 注解导入的xml文件信息 保存到当前配置类中
将 @Import 注解导入的实现了 ImportBeanDefinitionRegistrar 接口的类 保存到当前配置类中
将 @Bean 注解标注的方法,保存到当前配置类中
Set<BeanMethod> beanMethods
ConfigurationClass 几个重要属性
parse
1.将当前配置类封装成 ConfigurationClass类(new ConfigurationClass() ),然后递归地处理配置类及其超类层次结构, do-while 结构
@SpringBootApplication —> @SpringBootConfiguration —> @Configuration —> @Component 注解
1.遍历所有的内部类
1.其作用就是继续执行1-9的步骤
@Configuration 注解上也有 @Component
不难发现 @Component 注解标注的内部类都会成为配置类
标注了 @Component 注解
这个不做说明,一般内部类不会标注这个注解
标注了 @ComponentScan 注解
标注了 @Import 注解
标注了 @ImportResource 注解
或者是类中有标注了@Bean 注解的方法
2.成为候选类的条件
3.一旦成为候选配置类,则将其添加到List集合中
2.判断内部类是否为候选配置类
注意:假如内部类标注了@Component 注解,那么在这里也当作是配置类,然后创建 ConfigurationClass 对象进行封装
3.如果这里的候选配置类集合不为空,则遍历每一个候选配置类,然后执行 1-9 中的过程(每次都是 new 新的 ConfigurationClass 对象)
4.说明:这里如果执行了,说明刚开始的主配置类解析就会停下来,让这些普通的候选配置类先去执行,当这个都执行完了(执行完1-9过程),则继续执行主配置类的第3步骤的解析
1.如果有内部成员类
2.如果没有内部成员类,则执行接下来的步骤3
很明显,主配置类是满足条件的,所以要 递归的处理其内部成员类
2.判断配置类是否有 @Component 注解
1.如果有,则解析这个注解,这个注解是引入 自定义的 xxx.properties 是属性文件,添加到环境变量中
2.如果没有,则执行接下来的步骤4
3.判断是否有 @PropertySource 注解
1.如果有,则解析这个注解
在new这个对象时,其构造器内部做了一件很重要的事情:就是指定了扫描规则,就是去扫描@Component 注解
1.其内部 会创建 ClassPathBeanDefinitionScanner 对象,这个是spring扫描工作中一个非常重要的对象(mybatis 扫描继承了这个)
2.解析 @ComponentScan 注解的属性,其中就有,如果basePackages 属性为空,则取当前配置类所在的包为基准包路径
1.扫描这个包下的所有的class文件
1.遍历排除的组件过滤器:TypeFilter tf : this.excludeFilters
1.前面在这里设置了 @Component 注解类
然后创建 ScannedGenericBeanDefinition 对象,然后来到第3步
2.如何类上有 @Component 注解,表示可以标注为:候选组件
2.遍历包含的组件过滤器:TypeFilter tf : this.includeFilters
2.判断是否为候选的组件
如果符合,则将第2步骤创建的 ScannedGenericBeanDefinition 对象保存到 Set 集合中返回
2.判断是否为候选组件,如果标注了@Component 注解,或者是font color=\"#fbc02d\
3.继续判断是否为候选的组件
只要 @ComponentScan 注解扫描到类,才会在这里就保存到 BeanDefinitionRegistry 对象中,其余的都要放到解析过程2中的 ConfigurationClassBeanDefinitionReader#loadBeanDefinitions
4.遍历 3中返回的 Set<BeanDefinition> 集合,将每个 bean 定义对象保存到 font color=\"#f44336\
1.检查是否为候选配置类
1.标注了 @Configuration 注解
2.标有基本注解
3.标有 @Bean 注解的方法
2.候选配置类的条件
3.如果满足条件,则将当前配置类(这里就这么理解吧,毕竟@Component 注解标注的类在这里都算是配置类)封装到配置类ConfigurationClass 中,然后执行大步骤1中的 do-while 循环,然后遍历每一个类,然后执行1-9步骤
5.遍历 4中返回的 Set<BeanDefinitionHolder> 集合
3.调用 ClassPathBeanDefinitionScanner 对象的 doScan(basePackages) 方法开始扫描
2.通过 ComponentScanAnnotationParser 对象开始解析
2.这个其实是一定有的,因为 @SpringBootApplication 注解上标注了,解析的时候,判断basePackages 为空,则使用主配置类所在的包
3.解析完成后,则执行接下来的步骤5
4. 判断是否有 @ComponentScan 注解
1.递归获取当前配置配置类上的@Import 注解,如果没有,则返回,执行步骤6
这个没有太细看,我用的不是很多
1.如果是 ImportSelector 类型的,则处理导入
1.注意:它是保存到当前配置属性中,并没有去扫描去创建 BeanDefinition 对象
然后将 MapperScannerRegistrar 类放到当前配置类的 importBeanDefinitionRegistrars 属性中
MapperScan
2.比如我一般是在普通配置类上添加了 @MapperScan 注解,将会导入 MapperScannerRegistrar 类
2.如果是 ImportBeanDefinitionRegistrar 类型的,则保存到当前配置类 ConfigurationClass (他就是对当前配置类的进一步封装),放到 importBeanDefinitionRegistrars 这个Map 属性中, 每一个配置类都会有对应的配置封装类 ConfigurationClass
3.如果1,2都不符合,则将其当作配置类来处理;然后将这个导入的类当作配置类,在去重复1-9 过程
2.如果有,则遍历所有 @Import 导入的类
3.上面的步骤2执行完,开始下面的步骤6
5.处理 @Import 注解
1.这个注解,就是导入以前spring 的xml文件
2.如果有,则将配置文件信息把保存到配置类ConfigurationClass (他就是对当前配置类的进一步封装)的 importedResources 这个Map属性中
3.注意:它是保存到当前配置属性中,并没有去扫描去创建 BeanDefinition 对象
4.接下来执行步骤7
6.处理 @ImportResource 注解
1.如果当前配置类中有 @Bean 注解标注的方法,则遍历所有的@Bean方法,然后将其放到当前配置类ConfigurationClass (他就是对当前配置类的进一步封装)的 beanMethods 这个 Set 属性中,并且会将方法封装成 BeanMethod 对象进行保存
2.注意:他也是保存,并没有去扫描创建 BeanDefinition 对象
3.接下来执行步骤8
7.解析 @Bean 注解
1.看当前配置类是否实现了接口,接口中是否有 @Bean 注解的默认方法,如果有,则解析 @Bean注解,将其放到当前配置类ConfigurationClass 的 beanMethods 这个Set属性中
3.接下来执行步骤9
8.解析 当前配置类实现的接口
1.如果有,则直接返回,其实这里的返回就是继续执行 开始的 do-while 循环,在将上面标注的 1-9 执行一遍
3.接下来执行步骤10,不可忽略!!!!
9.解析当前类的父类
1.一旦当前配置类解析过程结束,注意:这个结束表示当前配置类已经执行完了1-9的过程,最后返回了 null(这个结束,只是当前配置类解析结束了,以上1-9的过程实际上是非常复杂的,每一步骤的细节可能仍然谁重复1-9的过程,这样很不好理解,所以我把 1-9 的过程单独拿出来说明,这样比较好理解
1.font color=\"#9c27b0\
这个ConfigurationClass 类的 importBeanDefinitionRegistrars 属性 中,保存了 @Import 注解导入的 MapperScannerRegistrar 类
AppConfig
请参考图片
font color=\"#f44336\
这个就是@Componet 注解标注的(@Controller 本质也是@Component)
这个类是 @Import 注解导入的,但是这个类没有实现 ImportSelector, ImportBeanDefinitionRegistrar 这两个接口
说明:index = 0 和 1 是没有确定顺序的,先解析谁,谁就在第一个位置,但是主配置类,一定是在用户自定义配置类中的最后一个
1.首先LinkedHashMap 首先存放的是 @ComponentScan 注解扫描到的我们自定义的业务配置类(比如 @Configuration ,@Component 等),以及是 @Import 注解导入的类但是这个类没有实现 ImportSelector, ImportBeanDefinitionRegistrar 这两个接口。这里有点意思,可能扫描到的一个普通@Component 组件,在这里也会当作配置类保存起来
........
这个ConfigurationClass 类的 importBeanDefinitionRegistrars 属性 中,保存了 @Import 注解导入的 AutoConfiguredMapperScannerRegistrar 类
.........
2.其次存放的就是spring 自动配置导入一些配置类
3.所以说,我们用户导入或写的配置类,要比spring自动配置导入的要靠前,越靠前在 this.reader.loadBeanDefinitions 中就先去解析,请看后面的解析过程第2大步骤
2.它具体存储了什么?
2.然后将配置类放到 ConfigurationClassParser 类中的 configurationClasses 这个Map属性中,这个Map的key和value 都是前面一直提到的 ConfigurationClass 类,它是每一个配置类的封装,然后这里面保存着1-9中保存进来的内容(绿色部分),然后留着接下来的第二大步骤去进一步解析 (this.reader.loadBeanDefinitions)
10.整个解析过程结束
1.只会将 @ComponentScan 注解扫描到类以及 @Import 注解导入的且没有实现 ImportBeanDefinitionRegistrar 和 ImportSelector 接口的类 保存到 BeanDefinitionRegistry 中,其他的不会去创建 BeanDefinition 对象
2.这里的配置类,不是在我们习惯意义上的 @Configuration 标注的类,而是 @ComponentScan 注解扫描到的,比如 font color=\"#fbc02d\
配置类解析
2.将每个配置类保存到 ConfigurationClassParser 类中的 configurationClasses 这个Map属性中
1.上面的 parse 方法执行完
1.将 font color=\"#9c27b0\
1.如果符合跳过条件,则直接返回,进行下一次的遍历
2.如果不符合,则执行步骤2
1.判断是否需要跳过,就是标注了 @Condition 条件
1.就是前面开始我说的 ConfigurationClass 几个重要属性中的 importedBy 属性;请回看
@Import 导入的随便一个类也是要去创建BeanDefinition 对象
AppConfig 导入 SingletonImport
2.我通过 @Import 注解导入的自定义 SingletonImport 类(这个类没有继承,没有实现,没有属性和方法),这个类是由AppConfig 导入的,所以它符合条件,于是创建 SingletonImport 类的 BeanDefinition 对象,然后保存到 BeanDefinitionRegistry 类中
2.解析 importedBy 属性;判断是否是导入的
1.就是前面开始我说的 ConfigurationClass 几个重要属性中的 beanMethods 属性
2.解析@Bean注解的方法,然后根据返回值创建 BeanDefinition 对象,然后保存到 BeanDefinitionRegistry 中
3.解析 beanMethods 属性;获取所有的 @Bean 注解的方法进行遍历
1.就是前面开始我说的 ConfigurationClass 几个重要属性中的 importedResources 属性
2.以前没有注解的时候,都是通过spring的xml去创建对象,通过这个注解,就是将xml导入进来,去解析其中的<bean> 标签,然后创建BeanDefinition 对象,然后保存到 BeanDefinitionRegistry 类中
4.解析 importedResources 属性;获取 @ImportSource 导入的 xml文件
1.就是前面开始我说的 ConfigurationClass 几个重要属性中的 importBeanDefinitionRegistrars 属性
比如可以看下 @MapperScan 导入 MapperScannerRegistrar 类的这个方法,很容易懂
2.遍历所有实现了ImportBeanDefinitionRegistrar 接口的类,然后调用 registerBeanDefinitions 方法,这个方法内部就是注册组件的逻辑
5.解析 importBeanDefinitionRegistrars 属性;获取 @Import 导入的实现了 ImportBeanDefinitionRegistrar 接口的类
6.解析结束,那么到此,BeanDefiniton 对象的创建过程就结束了,也就是说,spring容器刷新的 invokeBeanFactoryPostProcessors(beanFactory) 方法结束了
2.遍历所有的 ConfigurationClass
解析配置类
2.开始解析 configurationClasses 属性
2.this.reader.loadBeanDefinitions
2.解析过程
3.创建 ConfigurationClassParser 对象(new出来的)用来解析每一个配置类(实际上这里就一个主配置类)
2.详细解析这个方法
ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry 开始处理bean定义
3.调用 BeanDefinitionRegistryPostProcessor 接口的方法
2.如果实现了
2.判断是否实现了PriorityOrdered 接口
1.如果没有,则跳过
2.如果实现了,同上面一样,但是一旦解析过了,就不会再次去解析了,有判断条件的
3.判断是否实现了 Ordered 接口
4.最后处理没有实现任何排序接口的
调用 font color=\"#388e3c\
2.先判断和 BeanDefinitionRegistryPostProcessor 处理器相关的(它主要管bean定义)
2.判断是否实现了PriorityOrdered 接口
3.判断是否实现了 Ordered 接口
3.在判断 和 BeanFactoryPostProcessor 处理器相关的
4.这一步会创建 BeanDefinitionRegistryPostProcessor 和 BeanFactoryPostProcessor 类型的对象,前提是从bean定义容器(就是 DefaultListableBeanFactory 类的 beanDefinitionMap 属性)中可以获取到
4.调用BeanFactory 的后置处理【invokeBeanFactoryPostProcessors(beanFactory)】
1.其作用就是将bean定义容器(就是 DefaultListableBeanFactory 类的 beanDefinitionMap 属性)中的后置处理器查询出来,通过 beanFactory.getBean() 去创建对象,然后保存到BeanFactory 对象中
前面创建容器对象AnnotationConfigServletWebServerApplicationContext时,其内部的 AnnotatedBeanDefinitionReader 对象会注册bean定义信息
从bean 定义容器中(就是 DefaultListableBeanFactory 类的 beanDefinitionMap 属性)获取已经定义的 BeanPostProcesor 数据
3.获取出来后,调用 beanFactoy.getBean() 方法去获取对象,由于此时还没有,所以就会去创建对象
4.这一步叫注册bean的后置处理器,实际上就是去创建 BeanPostProcessor 类型的对象,将其保存到 BeanFactory 的 beanPostProcessors 属性中
根接口,提供了两个方法,分别在初始化前后执行
用于实例化前的回调
以及实例化后但在显式设置属性或自动装配发生之前的回调。
处理 @Autowired 注解的 AutowiredAnnotationBeanPostProcessor 处理器此时会工作
处理 @Resource 注解的 CommonAnnotationBeanPostProcessor 处理器此时会工作
自动注入属性时回调
BeanPostProcessor的子接口
InstantiationAwareBeanPostProcessor
这个接口是一个特殊用途的接口,主要用于框架内部使用
扩展了InstantiationAwareBeanPostProcessor接口,添加了一个回调函数,用于预测已处理bean的最终类型以及解析构造器
SmartInstantiationAwareBeanPostProcessor
处理 @Autowired 注解的 AutowiredAnnotationBeanPostProcessor 处理器实现了这个接口
处理 @Resource 注解的 CommonAnnotationBeanPostProcessor 处理器实现了这个接口
BeanPostProcessor 的子接口,用于 运行时合并bean定义的后处理回调接口,不明白具体意思
MergedBeanDefinitionPostProcessor
BeanPostProcessor的子接口, 销毁前进行回调
DestructionAwareBeanPostProcessor
5.BeanPostProcessor 的类型
6.这一步会创建 BeanPostProcessor 类型的对象,前提是从bean定义容器(就是 DefaultListableBeanFactory 类的 beanDefinitionMap 属性)中可以获取到
5.注册Bean的后置处理器【registerBeanPostProcessors(beanFactory)】
1. MessageSource组件主要用来 做国际化功能,消息绑定,消息解析等
2. 看容器中是否有id为messageSource的,类型是MessageSource的组件;如果有赋值给messageSource,如果没有自己创建一个DelegatingMessageSource,并将其注册到 BeanFactory 的单例池中
6.初始化 MessageSource 组件【initMessageSource()】
1.从BeanFactory 中获取 beanName=applicationEventMulticaster 的 ApplicationEventMulticaster
2.如果没有则创建一个 SimpleApplicationEventMulticaster
监听器的监听方法的调用时通过事件派发器去广播的
3.后面注册监听器要使用
7.初始化事件派发器【initApplicationEventMulticaster()】
容器对象 AnnotationConfigServletWebServerApplicationContext 的父类 ServletWebServerApplicationContext 重写了这个方法
1.这个类留给子类重写
这里它做了一件非常重要的事情:创建tomcat服务器并启动
2.ServletWebServerApplicationContext#onRefresh()
留着后面补充
3.tomcat 启动过程
4.结论:tomcat 服务器启动在前,普通的单例bean创建在后
8.初始化其他特殊的bean【onRefresh()】
1.获取容器中已经存在的监听器,将他们逐个添加到步骤7步骤创建的事件派发器中,留着后面广播
1.从单例池中或者bean定义容器中(就是 DefaultListableBeanFactory 类的 beanDefinitionMap 属性)获取类型为 ApplicationListener 的所有子类的bean name
其他比如步骤1和2中,还只是注册到事件派发器中去,还不会调用监听方法
3.如果有早期的事件,则会在这里进行广播, 就是调用 ApplicationListener 的方法
容器开始刷新事件:ContextStartedEvent
容器刷新完成事件:ContextRefreshedEvent
容器关闭事件:ContextClosedEvent
应用失败事件:ApplicationFailedEvent
。。。。。。
具体事件就是 ApplicationEvent
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {}
4.说明:这个 ApplicationListener 只有一个 onApplicationEvent(E event) 方法,但是它可以监听不同的事件,比如
9.注册监听器【registerListeners()】
1.遍历bean定义容器(就是 DefaultListableBeanFactory 类的 beanDefinitionMap 属性)
1.首先从一级缓存中获取,第一次创建肯定是没有的
2.创建bean 实例
这个是用来解决循环依赖的
DefaultSingletonBeanRegistry 的 singletonFactories 属性
3.添加到三级缓存中
4.属性赋值
BeanNameAware
BeanClassLoaderAware
BeanFactoryAware
1.调用Aware接口的方法
初始化方法前调用
处理器是针对所有的bean
2.调用 BeanPostProcessor 处理器的 postProcessBeforeInitialization()
1.实现了 InitializingBean 接口
2.@Bean 注解或者 <bean> 标签中制定了init 方法
3.调用初始化方法
初始化方法后调用
4.调用 BeanPostProcessor 处理器的 postProcessAfterInitialization()
5.初始化
DefaultSingletonBeanRegistry 的 singletonObjects 属性
6.添加到一级缓存中
2.调用 beanFactory.getBean() 开始获取对象,如果没有,则创建
0.调用beanFactory.getBean() 去获取对象(如果获取不到,就去创建)
getSingletion(beanName)
2.于是去调用 createBean() 去创建 helloController 对象
3.调用 doCreateBean() 去创建 helloContrller 对象
4.创建 helloControlle bean 实例 (createBeanInstance)
1.因为我是通过 @Autowired 注解注入的,所以实现了 InstantiationAwareBeanPostProcessor 接口的 AutowiredAnnotationBeanPostProcessor 处理器会工作,调用 postProcessProperties 方法,开始注入属性值(也就是helloService 对象)
2.于是去调用 createBean() 去创建 helloService 对象
3.调用 doCreateBean() 去创建 helloService对象
4.创建 helloService bean 实例 (createBeanInstance)
helloService 没有任何需要注入的内容,所以该方法可以顺利执行完
6.给 helloService 实例填充属性(populateBean)
7.给 helloService 初始化(initializeBean)
DefaultSingletonBeanRegistry 的 singletonObjects 属性
8.将完整的 helloService bean 放入一级缓存中
9.返回 完整的 helloService bean
2.于是容器中还没有 helloService 对象,所以最后判断 helloService instanceof Class , 于是调用 beanFactory.getBean()方法去获取对象
3.获取出来 helloService 对象后,利用反射将其设置到 HelloController 中; helloController 属性填充完毕
6.给hellController 实例填充属性(populateBean)
7.将 helloController 初始化(initializeBean)
8.将完整的 helloController bean 放入一级缓存中
4.从依赖的角度看对象的创建过程(不存在循环依赖)
循环依赖
前提:参考案例
0.调用 beanFactory.getBean() 获取bean(如果没有则去创建)
2.继续从一级缓存中获取 beanName=a 的bean,获取不到,将其标记为\"正在创建中\
3.调用 doCreateBean() 方法去创建 a
4.去创建 a 的实例 (createBeanInstance)
这一步很重要,将解决循环依赖
0.由于要填充的b对象是Class,所以调用 beanFactory.getBean() 去获取b对象,如果没有则创建
2.继续从一级缓存中获取 beanName=b 的bean,获取不到,将其标记为\"正在创建中\
3.调用 doCreateBean() 方法去创建 b
4.去创建 b 的实例 (createBeanInstance)
注意:getEarlyBeanReference() 方法只有当调用匿名函数的getObject()方法时才会执行
0.由于要填充的a对象是类,所以调用 beanFactory.getBean() 去获取 a 对象,如果没有则创建
2.对象a 此时是 \"正在创建中\" 的
DefaultSingletonBeanRegistry 类的 earlySingletonObjects 属性
DefaultSingletonBeanRegistry 类的 singletonFactories 属性
4.于是从 三级缓存中,根据 beanName=a,获取 ObjectFactory 对象
getObject()
当调用getObject()方法时,来到的是最开始放入三级缓存中匿名函数的方法中,很不好理解
5.因为在最开始创建a的实例完成后,就将其放入了三级缓存,所以这里可以获取的到,然后调用 ObjectFactory.getObject() 方法
8.将 beanName=a 从三级缓存中移除
1.首先从一级缓存中获取对象 a
2.最终利用三级缓存,返回一个早期引用的bean(仅仅实例化,但没有属性赋值和初始化)
6.给 bean b 填充属性(populateBean), 就是填充 a 对象
7.初始化 bean b (initializeBean)
8.将 完整的bean b( 完整:实例化,属性赋值,初始化都完成了)从正在创建中的集合中移除
9.将完整的bean b 放入一级缓存中
11. bean b 只经历了 从三级缓存到一级缓存,没有经过二级缓存
6.给 bean a 填充属性(populateBean),就是填充 b对象
7. 初始化bean a (initializeBean)
8.将 完整的bean a ( 完整:实例化,属性赋值,初始化都完成了)从正在创建中的集合中移除
9.将完整的bean a 放入一级缓存中
10.将 bean a 从三级缓存(实际上已经移除过了),二级缓存中分别移除
11.bean a 经历从三级缓存,二级缓存,一级缓存
3.从依赖的角度看对象的创建过程(存在循环依赖)
1.关于Controller 的在创建的整个过程中,其实没有任何的不同之处,为什么我们的请求会找到对应的Controller 呢?
1.看这个类的初始化方法
2.从bean定义容器中或者单例池中获取所有bean 的名字
org.springframework.util.ReflectionUtils
这个判断可以参考一下,可能用得到 【!method.isBridge() && !method.isSynthetic()】
1.判断是由用户自己写(排除那些继承Object 类方法的判断)标注了@RequestMapping 注解的方法
{GET /get/{id}}
2.并获取每个方法的映射信息 (RequestMappingInfo)
1.解析Controller类中所有的方法
这个对象封装了 当前 Controller 和 当前方法
后面请求时判断Controller 就会判断这个类的
1.创建 HandlerMethod 对象
key 是映射对象 RequestMappingInfo
value 是 HandlerMethod 对象
2.将映射信息保存到 AbstractHandlerMethodMapping#MappingRegistry#mappingLookup 中
它里面包含了映射信息和HandlerMethod 对象信息
value 是 直接new 的 MappingRegistration 对象
3.将映射信息保存到 AbstractHandlerMethodMapping#MappingRegistry#registry 中
2.注册上面解析出来的每一个方法
则这个类就是一个需要进一步处理的类
如果不为空 并且有 @Controller 注解或者 @RequestMapping 注解
3.根据beanName 获取 Calss 对象
2.这个是因为 RequestMappingHandlerMapping
RequestMappingHandlerMapping
SimpleUrlHandlerMapping
WelcomePageHandlerMapping
BeanNameUrlHandlerMapping
RouterFunctionMapping
initHandlerMappings(context)
初始化 HandlerAdapter 对象
RequestMappingHandlerAdapter
HandlerFunctionAdapter
HttpRequestHandlerAdapter
SimpleControllerHandlerAdapter
initHandlerAdapters(context)
初始化视图解析器
BeanNameViewResolver
ViewResolverComposite
InternalResourceViewResolver
ContentNegotiatingViewResolver
initViewResolvers(context)
3.当请求进来,先调用Servlet的初始化方法,最终会调用 DispatcherServlet 的 onRefresh() 方法
4.by the way: 关于springmvc 中Controller 的解析
10.初始化所有剩下的单实例bean【finishBeanFactoryInitialization(beanFactory)】
就是加载进来的我们编写的 .class文件
1.清除 Resource Cache
这里将会发布一个 ServletWebServerInitializedEvent 事件
调用 onRefresh() 方法
2.初始化一个生命周期处理器:DefaultLifecycleProcessor
1.根据事件类型获取所有的事件监听器,我这里就是要获取所有和 ContextRefreshedEvent 相关的事件
2.然后回调接口
获取到步骤7中的事件派发器,然后开始派发事件
3.发布 ContextRefreshedEvent 事件
11.IOC容器刷新完成【finishRefresh()】
进而调用父类 AbstractApplicationContext 的 refresh() 方法(spring经典的容器刷新方法)
调用 容器对象 AnnotationConfigServletWebServerApplicationContext 父类 ServletWebServerApplicationContext 的 refresh() 方法
6.刷新容器
没做任何内容,留给子类重写
7.刷新之后
事件发布监听器 EventPublishingRunListener 发布 ApplicationStartedEvent 事件
8.调用所有 SpringApplicationRunListener 的 started() 方法
1.从容器中获取所有 font color=\"#d32f2f\
2.从容器中获取所有 font color=\"#d32f2f\
9.回调 Runner 的方法
事件发布监听器 EventPublishingRunListener 发布 ApplicationReadyEvent 事件
10.调用所有 SpringApplicationRunListener 的 running() 方法
11.返回容器对象:AnnotationConfigServletWebServerApplicationContext
2.run()方法的运行
springboot 启动过程
0 条评论
回复 删除
下一页