Spring之Bean生命周期源码分析(一)1

简介: Spring之Bean生命周期源码分析(一)

一、前言

这是我Spring专栏的第六篇文章: Spring之Bean生命周期源码分析(一), 主要讲解了Bean声明周期中的 包扫描, Bean实例对象生成的前置流程. 在看本篇文章之前建议先看一下上篇文章当做前置学习  Spring之概念和工作流程在之前我为大家讲解了以下内容:

二、生成、合成BeanDefinition

前置信息

老规矩, 直接通过创建Spring容器的构造方法进去, 可以看到它先执行了无参构造, 在点进去, 在AnnotationConfigApplicationContext() 方法中第一行和第三行不用看, 那个是和JRF相关的, 可以忽略掉

StartupStep createAnnotatedBeanDefReader = this.getApplicationStartup().start("spring.context.annotated-bean-reader.create");
...
createAnnotatedBeanDefReader.end();
复制代码

网络异常,图片无法展示
|

this.reader = new AnnotatedBeanDefinitionReader(this); 这边就不细讲了, 具体可以插看我的上一篇文章Spring之概念和工作流程

还是上面的方法, 直接进入 refresh() 刷新方法

网络异常,图片无法展示
|

进入该方法的详情

网络异常,图片无法展示
|

在该方法下面找到这行代码, 点进去

网络异常,图片无法展示
|

里面有一个方法, 是用来实例化非懒加载的单例Bean的

网络异常,图片无法展示
|

在该方法中, 创建单例Bean的流程大致如下:

  • 取出所有的BeanNameList
  • 遍历 BeanNameList
  • 判断 (不是抽象的BeanDefinition, 是单例, 非懒加载)
  • 生成 Bean对象

抽象的BeanDefinition可通过目录快速跳转到 抽象的 BeanDefinition

网络异常,图片无法展示
|

生成BeanDefinition-Spring包扫描

Spring启动的时候会进行扫描, 会先调用

org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider#scanCandidateComponents(String basePackage)扫描某个包路径, 并得到BeanDefinition集合

首先我们看方法, 可以看到入参就是 包扫描地址, 里面调用了一个 doScan(backPackages) 方法, 这个方法就是具体的获取扫描信息的     // 方法地址 org.springframework.context.annotation.ClassPathBeanDefinitionScanner#scan(String... basePackages)

网络异常,图片无法展示
|

根据包路径扫描获取 BeanDefinition

doScan(backPackages)方法在源码里可以看到, 这边有个方法的返回值就是 BeanDefinition 集合, 那么这个就是核心的存储 Bean的容器了, 我们可以在点进去看看里面具体的实现

网络异常,图片无法展示
|

findCandidateComponents(basePackage)方法在这个方法里面去做了响应的判断, 上面那个判断在本标题的最后进行简单的讲解, 大部分情况都是else的, 红框部分的方法是相对比较核心的的, 接着往下走

网络异常,图片无法展示
|

具体的上面的if语句判断可通过目录快捷指向到标题 findCandidateComponents方法的判断

包扫描方法详情

scanCandidateComponents(basePackage) 方法我把源码在这里贴一下, 一张图截不下, 我放两张, 左侧也有相应的代码行数

网络异常,图片无法展示
|
网络异常,图片无法展示
|

第424行是获取basePackage下所有的文件资源, packageSearchPath是获取一个地址, classpath*:扫描包路径/**/*.class 具体如下图所示

网络异常,图片无法展示
|

获取该路劲路径下的资源文件(.class文件)数组

getResourcePatternResolver().getResources(packageSearchPath); 该方法的作用是获取该路劲路径下的资源文件(.class文件)数组

在往下走, 他会对获取到的所有文件进行一个遍历, 途中红框部分是Spring中的一个元数据读取器, 在上篇文章: Spring之概念和工作流程中标题八有讲到

元数据读取器可以获取到当前注解的信息, 类的名字, 实现的接口, 父类等, 底层用的ASM技术

获取到元数据之后, 我们会对当前类进行判断, 该类是在排除过滤器中还是在包含过滤器

排除过滤器和包含过滤器在上一篇文章标题九中有讲到 Spring之概念和工作流程

  • 注意: 下图中new ScannedGenericBeanDefinition(metadataReader)的注释标注有误, 实际是设置BeanClass的名字

网络异常,图片无法展示
|

进入这个方法就可以看到其对排除和包含过滤器的判断, 我们主要看第三个红框, 因为我们可以看到, 如果该方法想要返回true只能看第三个框中的方法

网络异常,图片无法展示
|

我们在该方法中也是一直点, 进入具体的实现方法中, 可以看到下图所示代码

网络异常,图片无法展示
|

解释一下上面代码中红框部分的判断

// metadata是类上的注解信息
// 如果该类没有注解或者该类上没有 Conditional注解
// 返回FALSE代表不要跳过, 表示它就是一个Bean
if (metadata == null || !metadata.isAnnotated( Conditional.class.getName())) {
   return false;
}
复制代码

通过以上判断确定该类是Bean之后, 通过以下代码设置 Bean的Class名字同时添加 Bean的resource

ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
sbd.setSource(resource);
// 上面方法具体实现如下:
    Assert.notNull(metadataReader, "MetadataReader must not be null");
    this.metadata = metadataReader.getAnnotationMetadata();
    // 这里只是把className设置到BeanDefinition中
    setBeanClassName(this.metadata.getClassName());
    setResource(metadataReader.getResource());
复制代码

添加完成之后, 进入以下判断, 大概判断的是: 是不是内部类, 接口, 抽象类

网络异常,图片无法展示
|

doScan方法内部代码流程

到这里, 我们获取 Set 的具体实现方法就讲解完成了, 我们回到 doScan()方法 接着往下进行

网络异常,图片无法展示
|

关于Bean名字的生成流程, 具体代码各种跳转就不贴了:

  • 判断该类上的注解 @Component 的 value值
  • 若该值存在, 则返回
  • 若该值不存在, 则调用方法生成
  • 若类名前两个字符都是大写字符则直接返回
  • 否则将第一个字符小写返回

检查Spring容器中是否已经存在当前 beanName

上图中红框部分接口: 检查Spring容器中是否已存在当前BeanName, 具体方法如下图所示:

网络异常,图片无法展示
|

findCandidateComponents方法的判断

网络异常,图片无法展示
|
上图这个判断主要是对 BeanDefinition的生成做一个快捷方式扫描 Bean的方式, 具体方式如下图所示

判断哪些类是由 @Component注解的, 具体源代码就不贴了, 很少用到

网络异常,图片无法展示
|

抽象的 BeanDefinition

通过以下方法设置的 BeanDefinition就是抽象的

网络异常,图片无法展示
|

引出下面的父子BeanDefinition

二、实例化非懒加载的单例Bean

首先进入 实例化非懒加载的单例Bean的方法中, 下图中爆红是因为那个方法太长了, 我临时删除了内部一些代码导致的

网络异常,图片无法展示
|

在这个方法里面, 一共有两个for循环遍历 beanNames, 第一个 for循环会生成所有的非懒加载单例Bean

网络异常,图片无法展示
|

第一次for循环流程说明:

  • 根据 beanName 获取 合并后的 BeanDefinition
  • 判断 BeanDefinition 是不是抽象, 单例, 懒加载
  • 判断当前Bean是不是 FactoryBean, 不管是不是都会去创建 Bean对象

第二次for循环流程说明:

  • 根据 beanName 找出对应的的单例对象
  • 判断单例对象是否实现了 SmartInitializingSingleton接口
  • 若实现了, 执行 smartSingleton.afterSingletonsInstantiated(); (后面有讲, 可根据目录快速跳转)

我们回到文章开篇创建Bean的那个方法里面, 我们可以看到它内部做了非常多的操作, 接下来我会为大家讲解一些比较核心重要的执行步骤

网络异常,图片无法展示
|

获取 RootBeanDefinition

获取合并之后的 BeanDefinition

网络异常,图片无法展示
|

找到当前 BeanDefinition

如果通过当前 beanName能在合并map(mergedBeanDefinitions)中取到 BeanDefinition则返回

网络异常,图片无法展示
|

没找到当前 BeanDefinition

假设当前 beanName 不是 合并BeanDefinition 继续往下走

网络异常,图片无法展示
|

最后我们到了这个方法里面, 注意: 这里参数 containingBd 为null

由于 containingBd 为null , 我们会直接执行下面这个方法, 又因为我们进到这个方法, 就是因为当前 beanName 不是 合并BeanDefinition, 所以 mbd = null, 继续往下执行到红框位置

if (containingBd == null) {
   mbd = this.mergedBeanDefinitions.get(beanName);
}
复制代码


目录
相关文章
|
1月前
|
XML Java 测试技术
Spring IOC—基于注解配置和管理Bean 万字详解(通俗易懂)
Spring 第三节 IOC——基于注解配置和管理Bean 万字详解!
121 26
|
3月前
|
XML 安全 Java
|
4月前
|
监控 Java 应用服务中间件
Spring Boot整合Tomcat底层源码分析
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置和起步依赖等特性,大大简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是其与Tomcat的整合。
89 1
|
2月前
|
监控 Java 应用服务中间件
SpringBoot是如何简化Spring开发的,以及SpringBoot的特性以及源码分析
Spring Boot 通过简化配置、自动配置和嵌入式服务器等特性,大大简化了 Spring 应用的开发过程。它通过提供一系列 `starter` 依赖和开箱即用的默认配置,使开发者能够更专注于业务逻辑而非繁琐的配置。Spring Boot 的自动配置机制和强大的 Actuator 功能进一步提升了开发效率和应用的可维护性。通过对其源码的分析,可以更深入地理解其内部工作机制,从而更好地利用其特性进行开发。
50 6
|
3月前
|
设计模式 XML Java
【23种设计模式·全精解析 | 自定义Spring框架篇】Spring核心源码分析+自定义Spring的IOC功能,依赖注入功能
本文详细介绍了Spring框架的核心功能,并通过手写自定义Spring框架的方式,深入理解了Spring的IOC(控制反转)和DI(依赖注入)功能,并且学会实际运用设计模式到真实开发中。
【23种设计模式·全精解析 | 自定义Spring框架篇】Spring核心源码分析+自定义Spring的IOC功能,依赖注入功能
|
3月前
|
存储 Java Spring
【Spring】获取Bean对象需要哪些注解
@Conntroller,@Service,@Repository,@Component,@Configuration,关于Bean对象的五个常用注解
|
3月前
|
存储 Java 应用服务中间件
【Spring】IoC和DI,控制反转,Bean对象的获取方式
IoC,DI,控制反转容器,Bean的基本常识,类注解@Controller,获取Bean对象的常用三种方式
|
3月前
|
XML Java 数据格式
Spring容器Bean之XML配置方式
通过对以上内容的掌握,开发人员可以灵活地使用Spring的XML配置方式来管理应用程序的Bean,提高代码的模块化和可维护性。
78 6
|
3月前
|
XML Java 数据格式
🌱 深入Spring的心脏:Bean配置的艺术与实践 🌟
本文深入探讨了Spring框架中Bean配置的奥秘,从基本概念到XML配置文件的使用,再到静态工厂方式实例化Bean的详细步骤,通过实际代码示例帮助读者更好地理解和应用Spring的Bean配置。希望对你的Spring开发之旅有所助益。
190 4
|
3月前
|
安全 Java 开发者
Spring容器中的bean是线程安全的吗?
Spring容器中的bean默认为单例模式,多线程环境下若操作共享成员变量,易引发线程安全问题。Spring未对单例bean做线程安全处理,需开发者自行解决。通常,Spring bean(如Controller、Service、Dao)无状态变化,故多为线程安全。若涉及线程安全问题,可通过编码或设置bean作用域为prototype解决。
55 1