Spring之Bean生命周期源码总结

187 阅读5分钟

回顾-Bean的生命周期

  • 启动容器ApplicationContext

  • 创建BeanFactory

  • 初始化BeanFactory

  • BeanFactory后置处理

    • 进行相关包扫描

      • 首先通过ResourcePatternResolver获得指定路径下所有.class文件,并且封装成Resource对象

      • 遍历每个Resource对象,利用MetadataReaderFactory解析Resource对象得到对应的MetadataReader,这样就可以从其中得到对应的类元数据信息,比如注解信息,接口信息等,底层采用的是ASM字节码技术

      • 通过MetadataReader进行includeFilters和excludeFilters的处理,以及对应条件注解@Conditional的match条件匹配,对于加了@Component注解的类,默认属于includeFilter,如果@Conditional注解所指定类的match方法匹配成功,就属于includeFilter,否则就excludeFilte

      • 在includeFilters中的,也就是匹配都成功的,就会生成ScannedGenericBeanDefinition

      • 基于MetadataReader去判断对应的类是否是独立的类

        • 如果不是独立的类比如说是一个普通的内部类,就不能成为Bean
        • 如果是顶级类或者静态内部类,就是独立的
        • 如果是接口或者抽象类,就不能成为Bean
        • 如果是抽象类,但是有包含了@Lookup注解的方法,也就是说定义了FactoryBean,就是一个独立的类
      • 如果筛选通过,那么就表示扫描到了一个Bean,将ScannedGenericBeanDefinition加入到Set集合中

  • 生成BeanDefinition

    • 在包扫描结束之后,就已经将生成的BeanDefinition放到了candidates的set集合中
  • 合并BeanDefinition

    • 通过扫描得到的所有BeanDefinition之后,就可以根据BeanDefinition创建Bean对象了,但是在Spring中支持父子BeanDefinition,所以如果继承了父BeanDefinition,子BeanDefinition就会把父中有的子中没有的一些属性给子并且重新生成一个BeanDefition,也就是RootBeanDefinition
  • 加载类

    • BeanDefinition合并之后,就可以去创建Bean对象了,而创建Bean对象就需要实例化对象,而实例化对象,就需要加载当前BeanDefinition对应的类,而加载类就需要对应的类加载器
    • 这里如果beanClass类型是Class那么就直接返回,否则就会根据类名进行加载
  • 实例化前

    • InstantiationAwareBeanPostProcessor#postProcessorBeforeInstantation
    • 需要注意的是如果实现了这个扩展点,就会返回一个对象,那么后续Spring的依赖注入也就不会进行了,会跳过一些步骤,直接进行初始化这一步
  • 实例化

    • 首先判断BeanDefinition是否设置了Supplier,如果设置了就会调用Supplier#get得到对象
    • 对于创建对象有两种方式,第一种是通过factoryMethod,另外一种就是FactoryBean,其实@Bean就是一种factoryBean的创建方式,如果@Bean对应的方法是static那么对应的就是factoryMethod
  • 推断构造方法

    • determineCandidateConstructors

    • Spring在基于某个类生成的Bean的过程中,需要利用这个类的构造方法进行实例化得到一个对象,但是如果一个类中存在多个构造方法,Spring就会按照以下逻辑进行判断

      • 如果一个类只存在一个构造方法,不管这个构造方法是无参构造还是有参构造,Spring都会去使用这个构造方法

      • 如果一个类中存在多个构造方法

        • 这些构造方法中,存在一个无参构造方法,那么Spring就会使用这个无参构造方法
        • 这些构造方法中,不存在无参构造方法,那么Spring就会报错
    • 对于Spring的设计思想是这样的

      • 如果一个类只有一个构造方法,那么就没有选择,只能使用这个构造方法

      • 如果一个类中存在多个构造方法,Spring就不知道去选择哪一个,就会看是否有无参构造方法,因为无参构造方法本身就表示了一种默认的意义

      • 如何某个构造方法上加了@Autowired注解,那就表示程序员告诉Spring我采用了这个构造方法来进行构造,如果Spring选择了一个有参构造方法,Spring在调用这个有参构造方法的时候,需要传入参数,那么Spring会根据入参的类型和入参的名字去Spring容器中找Bean对象,我们可以以单例为例子,Spring会从单例池singletonObjects中去找bean对象

        • 先根据入参类型去找,如果找到了一个,那么就直接用来作为入参
        • 如果根据入参类型找到了多个,那么就会再根据入参名字去确定唯一的一个
        • 最终如果没有知道就会报错,无法创建Bean对象
    • 对于推断构造方法中除了会去选择构造方法以及查找入参对象以外,还会去判断是否存在对应的类中存在使用@Lookup注解,如果存在就会把这个方法封装成LookupOverride对象并且添加到BeanDefinition当中

  • BeanDefinition的后置处理

    • Bean对象实例化出来之后,接下来就会给对象进行赋值了,在真正给对象赋值之前,Spring提供了一个扩展点MergedBeanDefinitionPostProcessor#postProcessMergedBeanDefinition,可以在此时的BeanDefinition进行加工.
  • 实例化后

    • 在处理完BeanDefinition之后,Spring提供了一个扩展点InstantationAwareBeanPostProcessor#postProcessAfterInstantation
  • 自动注入

    • Spring的自动注入
  • 填充属性

    • 在这个步骤中,就会进行处理@Autowired、@Resource、@Value等注解
    • 这里是通过InstantationAwareBeanPostProcessor#postProcessProperties实现的,在这里我们可以自定义实现注入逻辑
  • 执行Aware回调接口

    • 完成填充属性之后,Spring会执行一些回调接口,比如BeanNameAware、BeanClassLoaderAware、BeanFactoryAware等,这些会回传对应的信息给程序员
  • 初始化前

    • 初始化前也是Spring提供的一个扩展点BeanPostProcessor#postProcessBeforeInitialization
    • 利用初始化之前,可以对进行了依赖注入的Bean进行处理
    • 在Spring源码InitDestroyAnnotationBeanPostProcessor会在初始化前这个步骤执行@PostConstruct的方法
    • ApplicationContextAwareProcessor会在初始化前执行其他Aware回调接口,比如EnvirnmentAware等
  • 初始化

    • 查看当前Bean对象是否实现了InitializationBean#afterPropertiesSet接口了,如果实现了就会调用afterPropertiesSet方法
    • 执行BeanDefinition中指定的初始化方法
  • 初始化后

    • Spring扩展点BeanPostProcessor#postProcessAfterInitialization
    • 可以在这个步骤中,对Bean最终进行处理,Spring中的AOP就是基于初始化后进行实现的,初始化后返回的对象才是最终的Bean对象