Spring原理篇(4)--为什么你跟别的程序员一起去面试.同样问的Spring的原理;别人可以拿30K 你却只能要到15K;前面三章文章的铺垫,为的就是现在<Spring真正的生命周期-上>

@TOC# Spring系列
记录在程序走的每一步___auth:huf


拨开云雾见天日 守得云开见月明

Spring 真正的生命周期;

看过我的文章的人都知道; 我在写第一篇文章的时候.描述的Spring; 是不是非常像Spring的生命周期; 但是它并不完整; 可以说是简化版本; 是让读者有这种概念. 轮廓;然后逐步往下走;如果在一开始 就描述细节; 到时候每个人心中都有不一样的Spring; 现在开始 讲解Spring真正的生命周期; 附带源码; 附带结果图;附带代码; 这仅仅是上半部分; 会在getBean方法上作停留. getBean方法 下篇文章再讲;

一 Bean的生命周期大图;

在这里插入图片描述
Bean的生命周期: 指的是Bean 如何创建-------> 如何销毁;
那么我们就开始吧;

package com.huf;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
/**
 * auth:huf
 */
@SpringBootApplication(exclude= {
    
    DataSourceAutoConfiguration.class})
public class testMain {
    
    
    public static void main(String[] args) {
    
    
    	//在这一行代码里面; 创建的一个Context 也是Spring运行过程中;第一个步骤; 
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(testMain.class);
    }
}

再执行new AnnotationConfigApplicationContext()方法的时候 会进来构造函数 再它的this()中调度了无参构造器
在这里插入图片描述
在这里插入图片描述
它在无参构造器中 创建了一个StartupStep 这个东西是用于记录 之后代码的运行时间 再后面是有一个createAnnotatedBeanDefReader.end(); 结束 中间的方法;就是去提取系统参数 以及 环境参数; 这里给大家一个图.
在这里插入图片描述
在这个 new AnnotatedBeanDefinitionReader(this); 会去注册几个 Spring自己使用的Bean对象 RootBeanDefinition; 在其方法内部 public static Set registerAnnotationConfigProcessors(
BeanDefinitionRegistry registry, @Nullable Object source)
其源码构造细节如图:
在这里插入图片描述
这里引出了一个东西 就是RootBeanDefinition 是什么? 跟BeanDefinition 有什么样的关系? 先思考这个问题;这里很重要;这里很重要;这里很重要; 之后称BeanDefinition 为BD; 方便写作;
这段源码跳过; 有兴趣的小伙伴可以自己去了解; 避免思维错乱 以及文字冗余问题; 这里创建的RootBD 实际上就是为了去拿我们的包名字; 以下放一张图 进行简单了解 此处不过多介绍;后面会一点一点帮助大家吃透Spring源码;
在这里插入图片描述
为了满足好奇宝宝的好奇心 这个提示 一开始创建的6个RootBeanDefinition 是在这个registerAnnotationConfigProcessors 把断点打到return beanDefs; 在这个Set里面 就有6个 以下是截图:
在这里插入图片描述
此处不再累赘;

在得到我们的包名字后;
在这里插入图片描述
在中间这行代码 传入了我们testMain.class;

下图是接 第三章尾部陈述的 includeFilters 与 excludeFilters 起作用自己回看第三章;
在这里插入图片描述

在同方法下 得到了一个basePackages 这时候好戏就开始了
在这里插入图片描述
在得到packages 的时候 在方法尾部 就进入了scan扫描

return scanner.doScan(StringUtils.toStringArray(basePackages));

在这里插入图片描述
会通过findCandidateComponents() 再 进入 scanCandidateComponents()
在这里插入图片描述

  1. 首先,通过ResourcePatternResolver获得指定包路径下的所有 .class 文件(Spring源码中将 此文件包装成了Resource对象)
  2. 遍历每个Resource对象
  3. 利用MetadataReaderFactory解析Resource对象得到MetadataReader(在Spring源码中 MetadataReaderFactory具体的实现类为CachingMetadataReaderFactory, MetadataReader的具体实现类为SimpleMetadataReader)
  4. 利用MetadataReader进行excludeFilters和includeFilters,以及条件注解@Conditional的筛选 (条件注解并不能理解:某个类上是否存在@Conditional注解,如果存在则调用注解中所指定 的类的match方法进行匹配,匹配成功则通过筛选,匹配失败则pass掉。)
  5. 筛选通过后,基于metadataReader生成ScannedGenericBeanDefinition
  6. 再基于metadataReader判断是不是对应的类是不是接口或抽象类
  7. 如果筛选通过,那么就表示扫描到了一个Bean,将ScannedGenericBeanDefinition加入结果集 MetadataReader表示类的元数据读取器,主要包含了一个AnnotationMetadata,功能有
    1. 获取类的名字、
    2. 获取父类的名字
    3. 获取所实现的所有接口名
    4. 获取所有内部类的名字
    5. 判断是不是抽象类
    6. 判断是不是接口
    7. 判断是不是一个注解
    8. 获取拥有某个注解的方法集合
    9. 获取类上添加的所有注解信息
      10.获取类上添加的所有注解类型集合
      值得注意的是,CachingMetadataReaderFactory解析某个.class文件得到MetadataReader对象是 利用的ASM技术,并没有加载这个类到JVM。并且,最终得到的ScannedGenericBeanDefinition对 象,beanClass属性存储的是当前类的名字,而不是class对象。(beanClass属性的类型是Object, 它即可以存储类的名字,也可以存储class对象) 最后,上面是说的通过扫描得到BeanDefinition对象,我们还可以通过直接定义BeanDefinition,或 解析spring.xml文件的,或者@Bean注解得到BeanDefinition对象。(后续会分析 @Bean注解是怎么生成BeanDefinition的)。

合并BeanDefinition

通过扫描得到所有BeanDefinition之后,就可以根据BeanDefinition创建Bean对象了,但是在 Spring中支持父子BeanDefinition,和Java父子类类似,但是完全不是一回事。 父子BeanDefinition实际用的比较少,使用是这样的,比如

<bean id="parent" class="com.huf.service.Parent" scope="prototype"/>
<bean id="child" class="com.huf.service.Child"/>

这么定义的情况下,child是单例Bean。

<bean id="parent" class="com.huf.service.Parent" scope="prototype"/>
<bean id="child" class="com.huf.service.Child" parent="parent"/>

但是这么定义的情况下,child就是原型Bean了。 因为child的父BeanDefinition是parent,所以会继承parent上所定义的scope属性。 而在根据child来生成Bean对象之前,需要进行BeanDefinition的合并,得到完整的child的 BeanDefinition

合并 BeanDefinition 得到RootBeanDefinition 从之前的两个BD 变成三个BD 这里需要重点记忆;
BeanDefinition合并之后,就可以去创建Bean对象了
此处作为下篇文章的锚点;

此次文章尾部用文字描写 是因为避免再看源码的过程中 迷失了自己; 源码需要一点一点读;

总结

本文章内容 主要讲述了:
Spring 启动过程中 详细的一些源码重点 目前仅仅到了创建对象之前; 也就是说 Bean 并没有创建出来; 之后会划重点讲解源码 – 类 方法名 哪一行 做了什么; 之后会讲清楚;

see you

Guess you like

Origin blog.csdn.net/wenaicoo/article/details/120191070