当前位置: 首页 > news >正文

《Spring源码深度分析》第5章 Bean的加载

目录标题

  • 前言
  • 一、Bean加载入口与源码分析
    • 1、Bean加载的入口
    • 2、Bean加载源码
  • 二、FactoryBean的使用
  • 三、缓存中获取单例bean(待补充)

前言

经过前面的分析,我们终于结束了对XML 配置文件的解析,接下来将会面临更大的挑战,就是对 bean 加载的探索。bean 加载的功能实现远比 bean 的解析要复杂得多。

一、Bean加载入口与源码分析

1、Bean加载的入口

讲源码之前,我们可以先来找一下源码入口。对于加载 bean 的功能,在 Spring 中的调用方式为:

MyTestBean bean = (MyTestBean) bf.getBean ("myTestBean")

这句代码实现了什么样的功能呢?我们可以先快速体验一下 Spring 中代码是如何实现的。

2、Bean加载源码

protected <T> T doGetBean(String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)throws BeansException {/* 1、转换对应beanName。* 	1.去除FactoryBean的修饰符。name有可能是 &xxx 或者 xxx,如果name是&xxx,那么beanName就是xxx;2.name有可能传入进来的是别名,那么beanName就是id;* */String beanName = transformedBeanName(name);Object beanInstance;/* 2、尝试从缓存中加载单例。*/Object sharedInstance = getSingleton(beanName);if (sharedInstance != null && args == null) {if (logger.isTraceEnabled()) {if (isSingletonCurrentlyInCreation(beanName)) {logger.trace("Returning eagerly cached instance of singleton bean '" + beanName +"' that is not fully initialized yet - a consequence of a circular reference");}else {logger.trace("Returning cached instance of singleton bean '" + beanName + "'");}}/* 3、返回真正的实例.*    缓存中记录的只是最原始的 bean 状态,并不一定是我们最终想要的 bean。*  举个例子,假如我们需要对工厂 bean 进行处理,那么这里得到的其实是工厂 bean 的初始状态,但是我们真正需要的是工厂 bean 中定义的 faetory-method 方法中返回的 bean,*  而 getObjectForBeanInstance 就是完成这个工作的。如果sharedInstance是FactoryBean,那么就调用getObject()返回对象*  */beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, null);}else {/* 4、原型模式的依赖检查(只有在单例情况才会尝试解决循环依赖;原型模式出现循环依赖会报错。)。*      原型模式情况下,如果存在A中有B的属性,B中有A的属性,那么当依赖注入的时候,就会产生当A还未创建完的时候因为对于B的创建再次返回创建A,造成循环依赖,也就是isPrototypeCurrentlyInCreation (beanName)为true的情况;* */if (isPrototypeCurrentlyInCreation(beanName)) {throw new BeanCurrentlyInCreationException(beanName);}BeanFactory parentBeanFactory = getParentBeanFactory();/* 5、检测parentBeanFactory。*   如果 beanDefinitionMap 中也就是在所有已经加载的类中不包括 beanName, 则尝试从parentBeanFactory 中检测,然后再去递归的调用 getBean 方法。*/if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {// Not found -> check parent.// &&&&xxx---->&xxxString nameToLookup = originalBeanName(name);if (parentBeanFactory instanceof AbstractBeanFactory) {return ((AbstractBeanFactory) parentBeanFactory).doGetBean(nameToLookup, requiredType, args, typeCheckOnly);}else if (args != null) {// Delegation to parent with explicit args.return (T) parentBeanFactory.getBean(nameToLookup, args);}else if (requiredType != null) {// No args -> delegate to standard getBean method.return parentBeanFactory.getBean(nameToLookup, requiredType);}else {return (T) parentBeanFactory.getBean(nameToLookup);}}if (!typeCheckOnly) {markBeanAsCreated(beanName);}StartupStep beanCreation = this.applicationStartup.start("spring.beans.instantiate").tag("beanName", name);try {if (requiredType != null) {beanCreation.tag("beanType", requiredType::toString);}/* 6、将存储 XML 配置文件的 GenericBeanDefinition 转换为 RootBeanDefinition。*    1.因为从 XML 配置文件中该取到的 Bean 信息是存储在 GenericBeanDefinition 中的,但是所有的 Bean 后续处理都是针对于 RootBeanDefinition 的,所以这里需要进行一个转换;*    2.转换的同时如果父类 bean 不为空的话,则会一并合并交类的属性。* */RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);// 检查BeanDefinition是不是Abstract的checkMergedBeanDefinition(mbd, beanName, args);/* 7、寻找依赖。*    因为 bean 的初始化过程中很可能会用到某些属性,而某些属性很可能是动态配置的,并且配置成依赖于其他的 bean,那么这个时候就有必要先加载依赖的 bean,* 所以,在 Spring 的加载顺寻中,在初始化某一个 bean 的时候首先会初始化这个 bean 所对应的依赖。* */String[] dependsOn = mbd.getDependsOn();if (dependsOn != null) {// dependsOn表示当前beanName所依赖的,当前Bean创建之前dependsOn所依赖的Bean必须已经创建好了for (String dep : dependsOn) {// beanName是不是被dep依赖了,如果是则出现了循环依赖if (isDependent(beanName, dep)) {throw new BeanCreationException(mbd.getResourceDescription(), beanName,"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");}// dep被beanName依赖了,存入dependentBeanMap中,dep为key,beanName为valueregisterDependentBean(dep, beanName);// 创建所依赖的beantry {getBean(dep);}catch (NoSuchBeanDefinitionException ex) {throw new BeanCreationException(mbd.getResourceDescription(), beanName,"'" + beanName + "' depends on missing bean '" + dep + "'", ex);}}}/* 8、针对不同的scope模式进行bean的创建*/// 8.1.singleton模式的创建if (mbd.isSingleton()) {sharedInstance = getSingleton(beanName, () -> {try {return createBean(beanName, mbd, args);}catch (BeansException ex) {// Explicitly remove instance from singleton cache: It might have been put there// eagerly by the creation process, to allow for circular reference resolution.// Also remove any beans that received a temporary reference to the bean.destroySingleton(beanName);throw ex;}});beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);}// 8.2.prototype模式的创建else if (mbd.isPrototype()) {// It's a prototype -> create a new instance.Object prototypeInstance = null;try {beforePrototypeCreation(beanName);prototypeInstance = createBean(beanName, mbd, args);}finally {afterPrototypeCreation(beanName);}beanInstance = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);}// 8.3.	其他scope类型else {String scopeName = mbd.getScope();if (!StringUtils.hasLength(scopeName)) {throw new IllegalStateException("No scope name defined for bean ´" + beanName + "'");}Scope scope = this.scopes.get(scopeName);if (scope == null) {throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");}try {  // session.getAttriute(beaName)  setAttriObject scopedInstance = scope.get(beanName, () -> {beforePrototypeCreation(beanName);try {return createBean(beanName, mbd, args);}finally {afterPrototypeCreation(beanName);}});beanInstance = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);}catch (IllegalStateException ex) {throw new ScopeNotActiveException(beanName, scopeName, ex);}}}catch (BeansException ex) {beanCreation.tag("exception", ex.getClass().toString());beanCreation.tag("message", String.valueOf(ex.getMessage()));cleanupAfterBeanCreationFailure(beanName);throw ex;}finally {beanCreation.end();}	}/* 9、类型转换。*     1.程序到这里返回 bean后已经基本结束了,通常对该方法的调用参数 requiredType 是为空的,* 但是可能会存在这样的情况,返回的 bean 其实是个 String,但是 requiredType 却传入 Integer类型,那么这时候本步骤就会起作用了,* 它的功能是将返回的 bean 转换为 requiredType 所指定的类型。*     2.当然,String 转换为 Integer 是最简单的一种转换,在 Spring 中提供了各种各样的转换器,用户也可以自己扩展转换器来满足需求。* */return adaptBeanInstance(name, beanInstance, requiredType);}

经过上面的步骤后 bean 的加载就结束了,这个时候就可以返回我们所需要的 bean 了。其中最重要的就是步骤(8),针对不同的 scope 进行 bean 的创建,你会看到各种常用的 Spring 特性在这里的实现

在细化分析各个步骤提供的功能前,我们有必要先了解下 FactoryBean 的用法。

二、FactoryBean的使用

【Spring学习】FactoryBean的使用

三、缓存中获取单例bean(待补充)

http://www.lryc.cn/news/33772.html

相关文章:

  • 华为OD机试真题Java实现【求最大数字】真题+解题思路+代码(20222023)
  • Java——异常机制
  • 【大数据实时数据同步】超级详细的生产环境OGG(GoldenGate)12.2实时异构同步Oracle数据部署方案(下)
  • ESP32设备驱动-土壤湿度传感器驱动
  • 公网远程连接MongoDB数据库【内网穿透】
  • SQL注入——floor报错注入
  • P6入门:在EPS下创建项目(P6Professional)
  • Linux安装及管理应用和账号和权限管理 讲解
  • 【JDK1.8 新特性】Stream API
  • Springboot Maven打包跳过测试的五种方式总结 -Dmaven.test.skip=true
  • 静态链接和动态链接的区别
  • MATLAB学习笔记1
  • Gorm -- 查询记录
  • 「Python 基础」错误、调试与测试
  • 17万字 JUC 看这一篇就够了(一) (精华)
  • C++右值引用/移动语义
  • 小樽C++ 多章⑧ (叁) 指针与字符串、(肆) 函数与指针
  • Mybatis-Plus
  • yolov8行人识别教程(2023年毕业设计+源码)
  • CAD指令框找不到了怎么调出来?CAD指令框调出方法
  • 一般用哪些工具做大数据可视化分析?
  • Python每日一练(20230308)
  • jvm之堆解读
  • 重构·改善既有代码的设计.02
  • 脑电信号处理总成
  • 判断推理之图形推理
  • 【预告】ORACLE Unifier v22.12 虚拟机发布
  • Sql执行流程与Redo log、 Undo log、 Bin log日志文件
  • 如何提高软件测试执行力
  • Open3D 计算点到平面的距离