有关Spring对象的创建---getBean(‘‘beanName“)
作者:互联网
我们从此处开始,跟随debug,解读Spring的对象创建过程。
注意:Product对象为单例,且第一次创建的简单对象,即没有使用FactoryBean创建对象
首先,进入AbstractBeanFactory这个对象的doGetBean方法
首先执行transformedBeanName(name)方法,此方法的作用为根据传入的name找到这个对象对应的BeanName,前文中有述,一个对象一定会存在一个BeanName,如果在xml文件中指定了Id,那么BeanName就是Id,如没有就是name作为BeanName,均没有就使用Spring的算法生成一个BeanName,并且保证BeanName的唯一性,原因是Spring的单例池以HashMap为底层,需要一个唯一的key来对应BeanDefinition。
随后我们F8,来到这个语句,我们F7进入它
来到了所谓的三级缓存
因为是第一次创建对象,singletonObjects中是不包含这个对象的,从后边的size也可以看出来,因此此时的 singletonObject为null,最终一定会返回null,因为还没有创建过此对象。
再F7,走到下一行代码执行
因为返回的是null,因此不符合这个if条件,很明显,只有二次创建的对象,才可以进入这个分支,这里简单提一下if中的核心代码,如果sharedInstance不为null,即可以进入这层分支,日志部分我们不管,最终一定会执行getObjectForBeanInstance方法,那sharedInstance已经是对象了,为什么还要执行一次这个方法呢,通常情况下,即便执行了这个方法,返回的还是sharedInstance,但是要考虑另一种情况,这个对象是FactoryBean,即创建复杂对象的工厂对象,显然用户想要的是这个对象生产的对象,而不是这个对象本身,因此这个方法会调用sharedInstance.getObject()方法,来获得复杂对象,然后返回。
我们进入else分支,即创建对象分支,首先Spring还要考虑一种可能性,就是父子容器的情况,即虽然子工厂中没有
何为父子容器?
/*一旦使用父子容器 最终父子容器的配置信息会发生融合,整合为一个配置文件,直接用子容器就能获得父容器的信息
如果遇到同名的配置内容 以子容器的配置为准,可以理解为就近原则,重写?*/
DefaultListableBeanFactory parent = new DefaultListableBeanFactory();
XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(parent);
xmlBeanDefinitionReader.loadBeanDefinitions(new ClassPathResource("applicationContext-parents.xml"));
DefaultListableBeanFactory child = new DefaultListableBeanFactory(parent);
XmlBeanDefinitionReader xmlBeanDefinitionReader1 = new XmlBeanDefinitionReader(child);
xmlBeanDefinitionReader1.loadBeanDefinitions(new ClassPathResource("applicationContext.xml"));
User u = (User) child.getBean("u");
System.out.println("u = " + u);
Product p = child.getBean("p", Product.class);
System.out.println("p = " + p);
简单理解为继承关系。
当前箭头指向的地方,获得当前工厂的父类工厂,很明显为null,下面if的条件判断的逻辑为子容器中没有id所对应的配置,且父容器不为空,要去父容器找。
明显不符合,直接F8进入下一个代码
typeCheckOnly ,是此方法的一个入参,typeCheckOnly 标志 若为true,Spring仅仅对这个类型做校验,而不是去创建对象,beanFactory.getBean("u",User.class),仅仅判断当前工厂获得或创建的对象类型是不是User类型 ,若为 false Spring会创建对象 或者 获得这个对象 返回给调用者。
简单来讲就是若为true则仅判断,不创建,显然if为true,进入这个逻辑,我们F7进入,
alreadyCreated是一个set,标志着这个对象即将被创建,或者已经创建好了,加了锁,然后再次判断,最后将此对象的名字加入set,然后clearMergedBeanDefinition(beanName),解析这段代码之前,我们了解何为MergedBeanDefinition,先向下看,F7进入这个方法
因为我们clearMergedBeanDefinition(beanName),因此此时MergedBeanDefinition中一定是不包含这个对象的,因此mbd一定为null,不符合if分支,F7,进入最后一行的代码
进入方法后,mbd为null,然后bd也不包含父亲,进入else分支,以bd为模型,建立了一个RootBeanDefinition(),何为RootBeanDefinition,简单理解来说就是一个用于父子bean融合的BeanDefinition,所以此方法逻辑也很简单,如果有我就融合父子bean,子类基于父类进行重写,如果没有父类,我就将子类直接作为mbd返回就行了,个人理解为为了后序处理的统一。
最后执行一个Scope的设置就直接将mbd返回了,因为如果没有指定的话,默认是Scope的。
所以,MergedBeanDefinition是什么也就呼之欲出的,就是代表的合并好的BeanDefinition,此处我们就将他理解为BeanDefinition也无所谓,至于clearMergedBeanDefinition(beanName)是什么意思,个人理解就是既然这个对象已经被标记为准备创建了的,为了保证后序代码的执行,我们再清理一遍,保证一定是第一次创建。
后序的这一大段代码,执行到的是测一下我们这个Bean是不是抽象类,然后下边的一大坨是处理depend-on标签,没什么人用过,不做介绍。
然后我们一路F8,来到这里,Spring对象大致分为三种,Singleton,Prototype,和其他,Product对象是单例的,因此进入这个If分支,典型的lamda表达式,getSingleton函数中回调createBean方法,我们F7
一系列的安全校验,日志打印之后,来到lamda函数的回调。我们F7进入
进入createBean方法,F7
此处方法核心函数,我们知道,Spring要根据反射获取对象的无参构造函数来创建对象,那么我们一定是要根据Class.forName(),来创建对象,所以我们就需要类的全限定名,当前所在的代码的这个方法就是获取类的全限定名,获取beanDefinition对应的Class对象,有Class我就直接返回,没有我就根据类的全限定名获取,然后在下面的if分支中,将Class赋值给BeanDefinition,if的逻辑是能找到这个对象对应的Class,并且这个对象的BeanDefinition没有Class属性,但是有对应的类名。
然后我们F8,继续走
这里是为了<lookup-method>和<replaced-method>,进行方法的重写,用的人不是很多,不是重点,继续F8
来到核心代码,F7进入,我有了Class,我还有了BeanDefinition,里边封装了各种属性,我已经拥有了构造对象的一切条件,我们可以根据反射来创建对象了,但是观察代码我们发现,Spring又基于Bean进行了一次封装,BeanWrapper,将Bean封装为BeanWrapper,让User中的属性平行于User,同时封装了对应类型的类型转换器,便于后续的操作
然后我们进入createBeanInstance方法,F7,看他究竟做了什么
最终我们进入了BeanUtils中的instantiateClass方法,根据构造器,来创建对象即我们下面这段代码
Class clazz = Class.forName("com.zxh.User");
Constructor ctor = clazz.getDeclaredConstructor();
User user = (User) ctor.newInstance();
如出一辙,只是Spring更复杂了一些,此时我们也可以通过Alt+F8来看看,BeanWrapper里装了什么?
rootObject里装了反射创建的对象,还未进行属性注入,typeConverterDelegate里封装了类型转换器。
至此,对象创建完毕,我们知道,创建对象只是一小部分,后序还会对对象进行属性注入,初始化,BeanPostProcess处理后,才真正将对象创建完毕。
标签:F7,对象,Spring,beanName,创建对象,getBean,User,Class 来源: https://blog.csdn.net/weixin_58104242/article/details/122772318