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