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