【Spring源码核心篇-01】精通Spring的bean的生命周期

server/2024/11/29 13:45:18/

Spring源码核心篇整体栏目


内容链接地址
【一】Spring的bean的生命周期https://zhenghuisheng.blog.csdn.net/article/details/143441012

springbean的生命周期

  • 一,springbean的生命周期
    • 1,生成BeanDefinition
      • 1.1,初始化context和BeanFactory
      • 1.2,核心doScan方法
      • 1.3,获取BeanDefinition
      • 1.4,BeanDefinition属性赋值
      • 1.5,Bedefinition存入BeanDefinitionMap
    • 2,Bean工厂加载bean
      • 2.1,实例化全部的单例bean
      • 2.2,doGetBean
      • 2.3,createBean
        • 2.3.1,bean的前置处理器(实例化前)
        • 2.3.2,实例化bean
        • 2.3.3,bean的后置处理器(实例化后)
        • 2.3.4,属性填充
        • 2.3.5,初始化
      • 2.4,销毁

如需转载,请附上链接:https://blog.csdn.net/zhenghuishengq/article/details/143441012

springbean_10">一,springbean的生命周期

在本人前面的文章中,也写过一些关于spring源码的一些核心方法,主要是对spring中refresh中的九个方法进行的一些源码剖析,接下来的spring系列主要为核心篇,就是疏通spring的主干和脉络,通过spring的主干来了解spring的IOC和aop。源码依旧可以选择5.2的版本: https://github.com/spring-projects/spring-framework/tree/5.2.x

本文主要的内容就是了解spring的IOC中,bean的什么周期以及流程是怎么样的,结合bean的生命周期,了解一些spring中的一些扩展点机制,如bean的前置处理器,后置处理器,bean实例化前能做的事情,bean实例化后做的事情,bean初始化前做的事情,bean初始化后做的事情等

本人通过读取一下源码锁总结的流程图如下:

在这里插入图片描述

1,生成BeanDefinition

首先第一步是生成一个Bean定义,如何理解这个Bean定义,就是类似于我要做一个家具,在做之前需要找一个加工厂帮忙做好,但是加工厂并不知道要做成什么样子,因此就需要我们定义好一份图纸交给工厂,那么这个图纸就是所谓的BeanDefinition,然后这个工厂就是对应的spring容器

1.1,初始化context和BeanFactory

spring中,主要有注解的方式和xml启动上下文,这里直接选用注解的方式启动上下文

java">AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext();

这个对象的继承关系如下,继承了 GenericApplicationContext 类,实现了 AnnotationConfigRegistry 接口

java">public class AnnotationConfigApplicationContext extends GenericApplicationContext implements AnnotationConfigRegistry {}

在初始化这个 AnnotationConfigApplicationContext 对象之后,可以发现这个对象是 BeanFactory 的一个实现类,也就是说在初始化这个context上下文之前,还会先初始化一个 BeanFactory 的工厂,并且BeanFactory工厂有的功能context上下文都有,context就是一个BeanFactory的一个具体实现

在这里插入图片描述

接下来以下面这个包路径的源码进行分析,如分析一段service包下面的所有注解是如何注册到spring中生产bean对象,AnnotationConfigApplicationContext 其他的几个构造方法可以先不看

java">public AnnotationConfigApplicationContext(String... basePackages) {this();		//注册一个读取器和扫描器,也可以先不看scan(basePackages);	//扫描包路径refresh();			//spring中最核心的方法,refresh方法
}

1.2,核心doScan方法

然后直接分析这个scan方法,一直往下点,可以看到一个 doScan 方法,就是扫描的核心方法,然后会循环的寻找包路径集合,最后回去找每一个路径下面的有 @Component 注解的bean,然后将bean生成一个 BeanDefinition

java">protected Set<BeanDefinitionHolder> doScan(String... basePackages) {Assert.notEmpty(basePackages, "At least one base package must be specified");//创建bean定义的holder对象用于保存扫描后生成的bean定义对象Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();//循环我们的包路径集合for (String basePackage : basePackages) {//找到候选的ComponentsSet<BeanDefinition> candidates = findCandidateComponents(basePackage);for (BeanDefinition candidate : candidates) {//拿到元数据解析器,可以获取类中所有信息ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);candidate.setScope(scopeMetadata.getScopeName());//设置我们的beanNameString beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);//这是默认配置 autowire-candidateif (candidate instanceof AbstractBeanDefinition) {postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);}//获取@Lazy @DependsOn等注解的数据设置到BeanDefinition中if (candidate instanceof AnnotatedBeanDefinition) {AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);}//把我们解析出来的组件bean定义注册到我们的IOC容器中(容器中没有才注册)if (checkCandidate(beanName, candidate)) {BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);definitionHolder =AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);beanDefinitions.add(definitionHolder);registerBeanDefinition(definitionHolder, this.registry);}}}return beanDefinitions;
}

1.3,获取BeanDefinition

可以直接查看这个 findCandidateComponents 方法时如何获取到Bean定义的,内部主要会通过一个 scanCandidateComponents 方法来具体的扫描这些带有@Component注解的主键,如下图所示

如一个com.zhs.service的包下面有一个OrderService的类:

  • 这个方法首先会获取到这个包路径为 com.zhs.service
  • 然后获取到该包下面的全部带有 .class 类文件,将这个OrderService类封装成一个Resource资源
  • 遍历每个资源,判断是否在includeFilters中或者excludeFilters中,Component在容器启动已经加入到includeFilters中,所以有这个注解的会生成一个BeanDefinition被选中加入到集合中

在这里插入图片描述

这里还有一个细节,就是在下面这行代码中,设置到这个Bean的ClassName还是这个类的名字,而不是一个对象,在后面实例化之后那么设置的就是一个Object的对象了,并且这个 beanClass 原本就是一个Object的对象,因为此时在容器启动时,目前还没有真正的bean对象被实例化

java">@Nullable
private volatile Object beanClass;	//原因是目前只扫描到了,但是并没有加载ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);public ScannedGenericBeanDefinition(MetadataReader metadataReader) {this.metadata = metadataReader.getAnnotationMetadata();setBeanClassName(this.metadata.getClassName());
}

1.4,BeanDefinition属性赋值

在上面虽然获取到了BeanDefinition,但是这个bean定义里面只有名字,还需要给BeanDefinition设置一些基础属性,如是否懒加载,是否单例bean

在拿到 BeanDefinition 的set集合之后,会遍历这个set集合,为里面每一个bean定义进行属性赋值,首先是获取到每个bean定义的元数据解析器,可以获取到每个bean定义的所有属性,然后设置bean定义作用域

java">ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
candidate.setScope(scopeMetadata.getScopeName());

然后就是判断类是否懒加载,是否加了 @Dependson注解等,Dependson注解主要用于依赖关系,比如A上面加了一个依赖B的注解,那么在加载A实例前需要先加载B实例,这种方式造成的循环依赖解决不了,只能手动的避免

java">if (candidate instanceof AnnotatedBeanDefinition) {					AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
}

在这里插入图片描述

1.5,Bedefinition存入BeanDefinitionMap

最后会先判断当前bean定义是否已经存在于容器中,如果不存在的话,把我们解析出来的组件bean定义注册到spring的IOC容器中

java">if (checkCandidate(beanName, candidate)) {BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);definitionHolder =AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);beanDefinitions.add(definitionHolder);registerBeanDefinition(definitionHolder, this.registry);
}

最后直接进入到这个 registerBeanDefinition 方法中,可以发现是直接通过一个工具类进行了注册

java">BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, registry);

最后将这个BeanDefinition注册到BeanDefinitionMap

在这里插入图片描述

2,Bean工厂加载bean

上面说了beanDefinition是一张图纸,spring是一个加工厂,那么接下来就需要通过工厂将图纸生成对应的家具,就是需要通过spring将beanDefinitionMap中的全部beanDefinition生成对应的bean

2.1,实例化全部的单例bean

在通过scan扫描方法将全部的beanDefinition拿到之后,接下来的事情就是遍历全部的BeanDefinition,然后将BeanDefinition交给spring的bean工厂,直接来直接分析 refresh 中的初始化单例bean的方法

java">finishBeanFactoryInitialization(beanFactory);

在这个方法中,会有一个专门实例化单例bean的方法 preInstantiateSingletons

java">protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {//实例化剩余的单实例beanbeanFactory.preInstantiateSingletons();
}

在这个实例化单例bean的方法中,会遍历全部扫描拿到的单例beanDefinition,做以下流程:

  • 先判断这个beanDefinition是否为抽象的、单例的或者是否为懒加载的
  • 再次判断这个beanDefinition是否为工厂bean,是的话则需要特殊处理转化成对应的bean
  • 最后调用这个getBean方法去创建对应的实例,有这个实例则直接获取,无则创建

在这里插入图片描述

2.2,doGetBean

接下来就是进入bean工厂创建bean的方法,在进入上面的getBean方法之后,里面可以看到这个真正去创建bean的方法 doGetBean 方法

java">@Override
public Object getBean(String name) throws BeansException {//真正的获取bean的逻辑return doGetBean(name, null, null, false);
}

如下图所示,其部分流程如下:

  • 首先第一步就是获取到这个bean的别名,如果有别名则获取到别名,没有则用当前bean的名字
  • 随后去单例池的缓存中获取对象,默认绝大多数的对象都是单例对象

在这里插入图片描述

首先刚进来这个单例池里面肯定是没有对象的,因为还未初始化放到单例池里面,因此看后面的else逻辑。下面这段代码的逻辑也比较简单,就是判断这个BeanDefinition是否存在于单前的BeanFactory中,如果不存在,那么就去父工厂的BeanFactory中去获取

java">BeanFactory parentBeanFactory = getParentBeanFactory();
//若存在父工厂,切当前的bean工厂不存在当前的bean定义,那么bean定义是存在于父beanFacotry中
if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {//获取bean的原始名称String nameToLookup = originalBeanName(name);//若为 AbstractBeanFactory 类型,委托父类处理if (parentBeanFactory instanceof AbstractBeanFactory) {return ((AbstractBeanFactory) parentBeanFactory).doGetBean(nameToLookup, requiredType, args, typeCheckOnly);}else if (args != null) {//  委托给构造函数 getBean() 处理return (T) parentBeanFactory.getBean(nameToLookup, args);}else {// 没有 args,委托给标准的 getBean() 处理return parentBeanFactory.getBean(nameToLookup, requiredType);}
}

如果没有父BeanFactory的话,那么就会继续往下走,其流程如下

  • 首先会拿到合并的RootBeanDefinition,这个才是最终交给bean工厂加载的BeanDefinition
  • 随后就是会对这个合并的RootBeanDefinition进行检查,判断是否抽象的等
  • 随后会校验这个 dependsOn 注解,就是前面所讲到的依赖于谁的注解,如A依赖于B,那么在创建A之前则需要将B先创建出来,这个注解方式避免不了循环依赖,只能手动解决
java">final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
//检查当前创建的bean定义是不是抽象的bean定义
checkMergedBeanDefinition(mbd, beanName, args);
//依赖bean的名称
String[] dependsOn = mbd.getDependsOn();if (dependsOn != null) {// <1> 若给定的依赖 bean 已经注册为依赖给定的 bean// 即循环依赖的情况,抛出 BeanCreationException 异常for (String dep : dependsOn) {//beanName是当前正在创建的bean,dep是正在创建的bean的依赖的bean的名称if (isDependent(beanName, dep)) {throw new BeanCreationException(mbd.getResourceDescription(), beanName,"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");}//保存的是依赖 beanName 之间的映射关系:依赖 beanName - > beanName 的集合registerDependentBean(dep, beanName);try {//获取depentceOn的beangetBean(dep);}catch (NoSuchBeanDefinitionException ex) {throw new BeanCreationException(mbd.getResourceDescription(), beanName,"'" + beanName + "' depends on missing bean '" + dep + "'", ex);}}
}

2.3,createBean

在对上面的那些东西进行校验完成之后,接下来就是真正的进入创建单例bean的方法,在单例方法中,首先会判断是否为单例bean,然后最终调用这个 createBean 方法进行bean的创建

java">//创建单例bean
if (mbd.isSingleton()) {//把beanName 和一个singletonFactory 并且传入一个回调对象用于回调sharedInstance = getSingleton(beanName, () -> {try {//进入创建bean的逻辑return createBean(beanName, mbd, args);}catch (BeansException ex) {//创建bean的过程中发生异常,需要销毁关于当前bean的所有信息destroySingleton(beanName);throw ex;}});bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
2.3.1,bean的前置处理器(实例化前)

接下来直接进入这个创建bean的主角 createBean 方法,也就是bean生命周期最重要的一部分,就是在这个方法里面,在这个方法里面,首先会在真正的doCreateBean创建bean之前,创建一个bean的前置处理器,aop的实现就是在这里去进行扫描所有的@AespectJ注解,然后扫描类内部的@Before,@after等注解,最后进行一个动态代理,这一步就是实例化前的操作

java">@Override
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)throws BeanCreationException {try {//bean定义的前置处理器Object bean = resolveBeforeInstantiation(beanName, mbdToUse);if (bean != null) {return bean;}}   
}

在这个 resolveBeforeInstantiation中,可以在里面看到一下这段代码,将这个对应的切面解析并保存到缓存中

java">//bean的前置处理器
bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);
//bean的后置处理器
bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);

在这里插入图片描述

2.3.2,实例化bean

在上面的getBean方法中继续往下走,可以发现一个真正的去创建实例bean的doCreateBean方法

java">try {/*** 该步骤是我们真正的创建我们的bean的实例对象的过程*/Object beanInstance = doCreateBean(beanName, mbdToUse, args);
}

方法如下图,首先会将这个Bean进行一个 BeanWrapper的包装,然后调用 createBeanInstance 进行bean的实例化,在进行实例化时,会对实例化的方式进行推断,如是否用构造方法、工厂方法等

java">instanceWrapper = createBeanInstance(beanName, mbd, args);

在这里插入图片描述

2.3.3,bean的后置处理器(实例化后)

在实例化之后,这里又会进行一个后置处理器的调用,这里会将一些 @AutoWired @Value 的注解进行预解析

java">synchronized (mbd.postProcessingLock) {if (!mbd.postProcessed) {try {//进行后置处理 @AutoWired @Value的注解的预解析applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);}catch (Throwable ex) {throw new BeanCreationException(mbd.getResourceDescription(), beanName,"Post-processing of merged bean definition failed", ex);}mbd.postProcessed = true;}
}
2.3.4,属性填充

接下来就是进入属性填充的阶段,里面通过调用这个 populateBean 方法进行属性填充

java">//属性赋值 给我们的属性进行赋值(调用set方法进行赋值)
populateBean(beanName, mbd, instanceWrapper);

在属性填充的方法中,会通过这个InstantiationAwareBeanPostProcessor类判断是否有一个 postProcessAfterInstantiation 的后置处理器,和实例化前的前置处理器功能类似,这种接口都是spring留给我们开发人员去自定义扩展的一些接口,改功能为实例化后,属性注入前的一个扩展点

java">if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {//获取容器中的所有的BeanPostProcessorfor (BeanPostProcessor bp : getBeanPostProcessors()) {//判断我们的后置处理器是不是InstantiationAwareBeanPostProcessorif (bp instanceof InstantiationAwareBeanPostProcessor) {//进行强制转化InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;//若存在后置处理器给我们属性赋值了,那么返回false 可以来修改我们的开关变量,就不会走下面的逻辑了if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {// 返回值为是否继续填充 bean// postProcessAfterInstantiation:如果应该在 bean上面设置属性则返回 true,否则返回 false// 一般情况下,应该是返回true 。// 返回 false 的话,将会阻止在此 Bean 实例上调用任何后续的 InstantiationAwareBeanPostProcessor 实continueWithPropertyPopulation = false;break;}}}
}

下面这段代码就是依赖注入的功能,判断注入的类型是通过type类型还是通过name的类型输入属性

java">if (mbd.getResolvedAutowireMode() == AUTOWIRE_BY_NAME || mbd.getResolvedAutowireMode() == AUTOWIRE_BY_TYPE) {//把PropertyValues封装成为MutablePropertyValuesMutablePropertyValues newPvs = new MutablePropertyValues(pvs);//根据bean的属性名称注入if (mbd.getResolvedAutowireMode() == AUTOWIRE_BY_NAME) {autowireByName(beanName, mbd, bw, newPvs);}//根据bean的类型进行注入if (mbd.getResolvedAutowireMode() == AUTOWIRE_BY_TYPE) {autowireByType(beanName, mbd, bw, newPvs);}//把处理过的 属性覆盖原来的pvs = newPvs;
}

代码往下执行又可以看到一个InstantiationAwareBeanPostProcessor对象的 ,给开发人员一些扩展接口,用于给开发者在@Autoware等属性中提前进行初始赋值

在这里插入图片描述

2.3.5,初始化

最后一个步骤就是进行初始化,上面已经进行了对象的实例化,属性填充,那么最后一步就剩下初始化了。

java">//进行对象初始化操作(在这里可能生成代理对象)
exposedObject = initializeBean(beanName, exposedObject, mbd);

直接查看这个 initializeBean 方法,可以发现初始化方法中主要有四个步骤:

  • 一个是判断是否实现Aware接口的回调,比如是否实现 BeanNameAware、BeanFactoryAware 接口,还有一些环境的aware,以及Resource资源的aware,事件发布器的aware等
  • 初始化前会调用bean的后置处理器,用于加载一些 @PostConstruct 等注解
  • 然后就是进行调用初始化的方法,
  • 最后执行一个调用bean的后置处理器的一个方法,比如处理所有aop是否需要执行动态代理等

在这里插入图片描述

2.4,销毁

依旧是在这个doCreateBean方法的最后面,有一个销毁的方法 registerDisposableBeanIfNecessary

java">registerDisposableBeanIfNecessary(beanName, bean, mbd);

由于这一块用的相对较少,因此不做过多的分析

在这里插入图片描述


http://www.ppmy.cn/server/137614.html

相关文章

练习LabVIEW第二十六题

学习目标&#xff1a; 刚学了LabVIEW&#xff0c;在网上找了些题&#xff0c;练习一下LabVIEW&#xff0c;有不对不好不足的地方欢迎指正&#xff01; 第二十五题&#xff1a; (1)显示一个二维数组的行数和列数&#xff1b; (2)查找一个二维数组中最大值,以及最大值在数组中…

网站安全,WAF网站保护暴力破解

雷池的核心功能 通过过滤和监控 Web 应用与互联网之间的 HTTP 流量&#xff0c;功能包括&#xff1a; SQL 注入保护&#xff1a;防止恶意 SQL 代码的注入&#xff0c;保护网站数据安全。跨站脚本攻击 (XSS)&#xff1a;阻止攻击者在用户浏览器中执行恶意脚本。暴力破解防护&a…

SpringMVC学习(1)

目录 一、什么是SpringMVC 二、中心控制器&#xff08;DispatcherServlet&#xff09; 三、SpringMVC的执行原理 一、什么是SpringMVC 是Spring Framework的一部分&#xff0c;是基于Java实现MVC的轻量级Web框架 特点&#xff1a; 1、轻量级&#xff0c;简单易学 2、高效…

docker 安装 PostgreSQL

参考链接 https://hub.docker.com/_/postgres 安装 # 后台运行&#xff0c;镜像名称为 postgres # --name postgres 容器名称为 postgres # POSTGRES_PASSWORD 超级用户的密码&#xff0c;超级用户名默认为&#xff1a;postgres&#xff0c;可以使用 POSTGRES_USER 环境变量设…

Knowledge-refined Denoising Network for Robust Recommendation

Knowledge-refined Denoising Network for Robust Recommendation&#xff08;Sigir23&#xff09; 摘要 知识图&#xff08;KG&#xff09;包含丰富的边信息&#xff0c;是提高推荐性能和可解释性的重要组成部分。然而&#xff0c;现有的知识感知推荐方法直接在KG和用户-项目…

基于Python大数据的王者荣耀战队数据分析及可视化系统

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、Vue项目源码、SSM项目源码、微信小程序源码 精品专栏&#xff1a;…

一文速通C++17 std::any

std::any 概念 值类型 一般来说&#xff0c;C是一门类型绑定和类型安全的语言。 值对象&#xff1a;被声明为确定的类型&#xff0c;并且不能改变自身的类型。类型&#xff1a;定义了所有可能的操作、也定义了对象的行为。 std::any是一种在保证类型安全的基础上还能改变自…

对象(JS)

一、什么是对象 JavaScript中的所有事物都是对象&#xff1a;如字符串、数值、数组、函数等...... 在这些之外&#xff0c;JavaScript允许自定义对象 对象是带有属性和方法的特殊数据类型。 对象的特征 定义变量name来描述学生的姓名。 定义变量age来描述学生的年龄。 定…