Spring源码刨析之配置文件的解析和bean的创建以及生命周期

   public void test1(){XmlBeanFactory xmlBeanFactory = new XmlBeanFactory(new ClassPathResource("applicationContext.xml"));user u = xmlBeanFactory.getBean("user",org.xhpcd.user.class);// System.out.println(u.getStu());}

先介绍一个类XmlBeanFactory,这个类负责配置文件的解析和bean的创建和初始化。在Spring中对象信息会被封装为一个BeanDefinition对象,这个对象会保存配置文件中所描述的类的信心比如属性名属性值类名等,以便创建bean时根据BeanDefinition对象的信息反射创建并赋值。

配置文件解析

   xmlbeanfactory内部会创建XmlBeanDefinitionReader用来解析配置文件资源。

把xml文件封装为Document,接着调用注册方法。此方法内部再经过调用会调用到parseBeanDefinitions方法。

 这里的Node就是<bean>标签的信息的封装parseDefaultElement(ele, delegate)方法用来解析常规标签,delegate.parseCustomElement(ele)用来解析自定义标签比如<mvc:annotation-driven></mvc:annotation-driven>这种的或者自定义的标签。

信息封装完毕后,会被存放在XmlBeanFactory的Map文件中,keybeanname,value是beanDefinition。到这里基本xml文件都被解析了。

Bean的创建和赋值

 XmlBeanFactory xmlBeanFactory = new XmlBeanFactory(new ClassPathResource("applicationContext.xml"));user u = xmlBeanFactory.getBean("user",org.xhpcd.user.class);

代码在getBean内部有体现

首先transformedBeanName(name)的作用是可能用户提供的是别名转化为beannaem也就是配置文件中bean的id,beanFactory会把创建的bean存放起来以便复用,所以底层使用的就是Map数据结构,根据beanname和bean对象进行存储,主要关注getSingleton(beanName)方法

protected Object getSingleton(String beanName, boolean allowEarlyReference) {Object singletonObject = this.singletonObjects.get(beanName);if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {synchronized (this.singletonObjects) {singletonObject = this.earlySingletonObjects.get(beanName);if (singletonObject == null && allowEarlyReference) {ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);if (singletonFactory != null) {singletonObject = singletonFactory.getObject();this.earlySingletonObjects.put(beanName, singletonObject);this.singletonFactories.remove(beanName);}}}}return singletonObject;}

这里就是所谓的三级缓存,它还有一个重要功能解决循环依赖等问题。

由于我们这里主要讲解bean的生命周期和创建,第一次获取肯定是获取不到。上上个图中if分支是判断bean的类型是不是BeanFactory,如果是的话就调用工厂的getObject方法,else分支内就是父子容器的概念,如果在创建XmlBeanFactory时值定了其它的容器那么会把它存放在parentBeanFactory中,那么我们子容器再查找时就会先找自己的内部看是否有,如果没有就找父类的。

接下来注意markBeanAsCreated(beanName)和final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);方法

先看getMergedLocalBeanDefinition(beanName)方法其内部本质是获取对应的bean信息,然后会去查看bean是否有指定继承的类

  <bean id="abs" class="" abstract="true"><property name="id" value="1"></property></bean><bean id="stu" name="u" class="org.xhpcd.student" parent="abs"></bean>

就像这样存在继承的类,spring做的就是把当前类的bean定义信息和父类的bean定义信息组合在一起并返回,然后这里就是完整的bean定义信息

以此递归调用获取父类bean信息,然后会把这些信息存放在mergedBeanDefinitions中。

接下来看之前的markBeanAsCreated(beanName)方法

将指定的 Bean 标记为已创建(或即将创建)。这允许 Bean 工厂优化其缓存,以便重复创建指定的 Bean。现在我们实际上正在创建 bean,让我们重新合并 bean 定义......以防万一它的某些元数据在此期间发生了变化。(注解原话)

接下来 mbd.getDependsOn()方法就是获取bean标签中的depend-on信息解析,不过用的不多。

这段代码是真正创建bean的代码,首先判断bean是单例还是多例又或者是session,request等作用域。先看单例吧,首先getSingleton方法内部传递了一个lambda表达式,作用是为了后续回调此方法,接下来分析getSingleton方法

首先先看 beforeSingletonCreation(beanName) 方法,

protected void beforeSingletonCreation(String beanName) {if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {throw new BeanCurrentlyInCreationException(beanName);}
}

这段代码就是判断当前类是否被排除并且把它加入到singletonsCurrentlyInCreation集合中,防止在bean的生命周期完成前被多次创建。

然后就是singletonObject = singletonFactory.getObject();此方法就是对传入的表达式的一个方法回调,回调createBean方法,接下来分析此方法

首先解析bean定义信息获取Class信息,

mbdToUse.prepareMethodOverrides()方法是和方法替换有关但平时用的少我会在其他文章进行讲述。

再接下来就是

// Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.
Object bean = resolveBeforeInstantiation(beanName, mbdToUse);

这段代码涉及Aop,通过@EnableAspect注解注册的bean后处理器,提前返回一个代理对象,代理对象不经过后面其它的后处理器等。以后文章会讲解aop这部分

接下来就是重点 doCreateBean(beanName, mbdToUse, args) 方法

首先创建一个包装对象,里面有一个属性就是我们的bean对象,只是此时还没没进行属性赋值初始化。包装类的作用主要就是用于类型转换,配置文件中把字符串转化为int等。

紧接着创建对应工厂放入三级缓存中,主要用来解决代理循环依赖问题。这里用不到

然后就是popilateBean方法了,这个方法内部进行属性的注入;

先看这个方法此方法内部是获取所有InstantiationAwareBeanPostProcessors

类型的后处理器,然后在最开始进行一些修改。

类型转换

首先尝试获取自定义类型转换器,紧接着对bean定义信息中set方法获取到转换的类型的属性的封装信息PropertyValue进行逐个遍历,内部通过set方法获取类型,然后或取真实的类型转换信息进行转换并赋值给PropertyValue,最终把类型转换后的结果存放在beanwrapper的属性中并进行属性赋值

首先根据  GenericTypeAwarePropertyDescriptor 拿到可写的方法就是set方法,然后利用反射进行属性赋值。

紧接着调用initializeBean方法

执行aware注入方法,紧接着执行Bean后置处理器的before方法然后调用init方法再调用后处理器的after方法,就基本完成创建的生命周期

到这里上面所说的回调方法基本分析完毕。

然后调用 afterSingletonCreation(beanName) 

protected void afterSingletonCreation(String beanName) {if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.remove(beanName)) {throw new IllegalStateException("Singleton '" + beanName + "' isn't currently in creation");}}

移除之前上面正在创建bean的信息,然后调用add方法放入一级缓存。到这里bean就创建完毕了

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/617115.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

前 5 名 iPhone 数据恢复软件评测

如今&#xff0c;我们似乎将整个生活都放在手机和移动设备上。他们用许多照片、备忘录、日历日期等记录了我们的生活&#xff0c;我们总是假设这些信息在我们需要时随时可以访问。但是&#xff0c;有许多情况会导致iPhone上的数据丢失&#xff0c;例如iPhone被盗&#xff0c;损…

【JSON2WEB】 13 基于REST2SQL 和 Amis 的 SQL 查询分析器

【JSON2WEB】01 WEB管理信息系统架构设计 【JSON2WEB】02 JSON2WEB初步UI设计 【JSON2WEB】03 go的模板包html/template的使用 【JSON2WEB】04 amis低代码前端框架介绍 【JSON2WEB】05 前端开发三件套 HTML CSS JavaScript 速成 【JSON2WEB】06 JSON2WEB前端框架搭建 【J…

微信跳转页面时发生报错

报错如下图所示&#xff1a; 解决方法&#xff1a;&#xff08;从下面四种跳转方式中任选一种&#xff0c;哪种能实现效果就用哪个&#xff09; 带历史回退 wx.navigateTo() //不能跳转到tabbar页面 不带历史回退 wx.redirectTo() //跳转到另一个页面wx.switchTab() //只能…

【LAMMPS学习】八、基础知识(1.8)键的断裂

8. 基础知识 此部分描述了如何使用 LAMMPS 为用户和开发人员执行各种任务。术语表页面还列出了 MD 术语&#xff0c;以及相应 LAMMPS 手册页的链接。 LAMMPS 源代码分发的 examples 目录中包含的示例输入脚本以及示例脚本页面上突出显示的示例输入脚本还展示了如何设置和运行各…

【MySQL】:深入解析多表查询(下)

&#x1f3a5; 屿小夏 &#xff1a; 个人主页 &#x1f525;个人专栏 &#xff1a; MySQL从入门到进阶 &#x1f304; 莫道桑榆晚&#xff0c;为霞尚满天&#xff01; 文章目录 &#x1f4d1;前言一. 自连接1.1 自连接查询1.2 联合查询 二. 子查询2.1 概述2.2 分类2.3 标量子查…

【第6章】spring转换器

文章目录 前言一、手工处理二、自定义转换器1. 新建bean2. 新建转换器类3. spring配置4. 测试5. 测试结果 总结 前言 不知道大家有没有注意到,我们【第4章】依赖注入和自动装配,我们注入了String、Integer、Date等类型的参数,其实都是通过配置value标签注入的,本质上都一样,那…

Towards IP Geolocation Using Delay and TopologyMeasurements(TBG)(2006年)

下载地址:Towards IP geolocation using delay and topology measurements | Proceedings of the 6th ACM SIGCOMM conference on Internet measurement 被引次数:492 Katz-Bassett E, John J P, Krishnamurthy A, et al. Towards IP geolocation using delay and topology …

FreeRTOS创建第一个程序

使用freeRTOS创建任务时使用如下函数 函数的参数 创建一个FreeRTOS任务点亮led灯实现led灯500毫秒翻转一次 具体的代码实现 #include "stm32f10x.h" // Device header #include "Delay.h" #include "freeRTOS.h" #include &quo…

linux进阶篇:性能分析工具: iostat命令详细讲解

Linux 性能分析工具&#xff1a; iostat命令详细讲解 Linux系统出现了性能问题&#xff0c;一般我们可以通过top、iostat、free、vmstat等命令来查看初步定位问题。在一个以前看到系统监控工具&#xff0c;总在想那些监控工具的代理&#xff0c;如何收集系统性能信息&#xff0…

GitHub repository - Pulse - Contributors - Network

GitHub repository - Pulse - Contributors - Network 1. Pulse2. Contributors3. NetworkReferences 1. Pulse 显示该仓库最近的活动信息。该仓库中的软件是无人问津&#xff0c;还是在火热地开发之中&#xff0c;从这里可以一目了然。 2. Contributors 显示对该仓库进行过…

c语言中声明的问题【求个关注!】

文章目录 1 变量的声明与定义&#xff08;1&#xff09;定义与声明的区别&#xff1a;&#xff08;2&#xff09;为什么要区分定义与声明&#xff1f;&#xff08;3&#xff09;extern是什么 ?&#xff08;4&#xff09;举例&#xff1a; 2 函数的声明&#xff1a;函数声明的格…

Springboot整合nacos报错无法连接nacos

报错信息&#xff1a;Nacos com.alibaba.nacos.api.exception.NacosException: Client not connected, current status:STARTING 关于这个报错的原因有很多&#xff1a;如Nacos未启动、网络问题、配置问题、版本不兼容问题等&#xff0c;我的报错原因主要是版本不兼容。 下面…