以代码流程方式探索Spring源码(八)--Bean实例化2--createBeanInstance与注解收集

入口:createBeanInstance()

instanceWrapper = createBeanInstance(beanName, mbd, args);

在其中有四类:
1、实例化factoryMethod方法对应的实例。

mbd.getFactoryMethodName() != null
  • bean标签里面配置了factory-method属性。
    有两种使用方式:一种是另外一个类中的非静态方法(@Bean就是这种);另一种是配置了class的这个类中的静态方法。
  • 方法上加了@Bean注解

2、实例化带有@Autowired注解的有参构造函数。
      @Autowired注解的方法或者属性都会触发getBean操作
3、实例化没有@Autowired注解的有参构造函数。
4、实例化无参构造函数。

实例化factoryMethod方法对应的实例

使用factoryMethod实例化举例

在下面第一张图的例子中,我们看到在bean标签的配置中,id为jack的bean标签是没有配置class属性的,配置了factory-bean属性指向了factoryMethodbean,配置了factory-method属性指向了实际生成bean的方法。factory-bean属性的值会封装进BeanDefinition的factoryBeanName属性中,factory-method属性的值会封装进BeanDefinition的factoryMethodName属性中。这时,这个方法必须是非静态的。

在下面第二张图的例子中,我们看到在bean标签的配置中,id为jack的bean标签配置了class属性,并配置了factory-method属性指向了这个类中的factoryMethod方法。这时,这个方法必须是静态的。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

入口:mbd.getFactoryMethodName() != null

在这里插入图片描述
在这里插入图片描述

获取factoryClass及isStatic的属性值

1、新建BeanWrapperImpl的对象,该类是对beanInstance的再一次包装;
2、获取bean标签的factory-bean属性的值,也就是BeanDefinition的factoryBeanName的属性值;
3、如果factoryBeanName不为空,表明factory-method会指向别的类,先去实例化这个类,并取到这个bean的class值赋值给factoryClass属性;并将isStatic赋值为false,表示factory-method指向的方法为非静态的。
4、如果factoryBeanName为空,表明factory-method会执行本标签的class属性代表的类,获取本BeanDefiniton的factoryClass属性;并将isStatic赋值为true,表示factory-method指向的方法为静态的。
在这里插入图片描述

获取factoryMethodToUse和argsToUse

1、新建一些使用的对象;
2、如果有配置了参数则获取参数;
3、没有配置参数的话就去mbd里面找找有没有已经处理过的结果值,第一次进来的实例化的时候显然是没有的。
在这里插入图片描述
1、如果BeanDefiniton的isFactoryMethodUnique有属性值,就走上面的逻辑,不常见。
2、否则,通过getCandidateMethods()方法获得该类下的所有方法,包括继承自Object的方法。通过反射工具类ReflectionUtils反射获取。
3、遍历这些rawCandidates,将静态情况符合条件的和factory-method名字符合条件的加入candidates集合中。
在这里插入图片描述
1、判断有几个候选的factory-method,只有一个的话就走下面的逻辑,我们上面举的例子就是走这一条分支。
2、给mbd的个属性赋值,下次进来的时候就可以直接获取,而不用再次解析。
3、实例化Instance
在这里插入图片描述

调用实例化方法进行实例化instance()

很显然,这个地方就是反射调用方法进行bean的实例化。
在这里插入图片描述
在这里插入图片描述
注:有重载方法的会调用无参的方法,因为实例化的时候args为null的。

实例化带有@Autowired注解的有参构造函数。

举个栗子

第一种情况:有一个带有@Autowired注解的构造函数
结论:正常实例化
在这里插入图片描述
第二种情况:有多个带有@Autowired注解的构造函数
结论:报异常
在这里插入图片描述
怎么解决呢?
答案:在@Autowired注解中加required=false属性设置
在这里插入图片描述

入口:createBeanInstance中的autowireConstructor()

1、通过BeanPostProcessor的方式获得带有@Autowired注解的构造函数集合
2、实例化beanInstance
在这里插入图片描述

获得候选方法

此时符合条件的BeanPostProcessor是AutowiredAnnotationBeanPostProcessor。
在这里插入图片描述
先从缓存中取
在这里插入图片描述
缓存中取不到就开始解析,我们看到跟获取factory-method的过程类似,先获取所有的rawCandidates,再根据条件获得candidates。还有两个属性:requiredConstructor和defaultConstructor,后面会用这两个属性参与一些判断。
在这里插入图片描述
1、for循环中遍历处理rawCandidates
2、获取该构造函数上的@Autowired的注解信息,有没有,以及各种属性信息
3、如果有,会判断requiredConstructor属性是否为null,不为null的话,会抛出异常,这个就是我们开始举例子中说到的有多个构造函数带有@Autowired注解会出错的原因。
4、获得@Autowired注解上的属性required的值。如果不设置,默认为true。
5、如果值为true,将该构造函数赋值给requiredConstructor,回到第3步,若有多个构造函数带有@Autowired注解并且required属性为默认属性,那么第二个rawCandidate进来的时候就会报异常了。
6、将该构造函数加入candidates的列表中。
在这里插入图片描述
正常情况下,cadidates中会有所有有@Autowired注解的构造函数,转化为数组赋值给candidateConstructors,添加缓存,并返回。
在这里插入图片描述

根据候选构造函数数组进行实例化

在这里插入图片描述
与使用factory-method方法实例化过程类似,先新建一个BeanWrapperImpl的实例,在从mbd中判断是否已经处理过,第一次进来肯定没处理过,第一次处理过后各种属性值就会赋值给mbd的各种属性,后面再进来这段代码就生效了。
在这里插入图片描述
接下来就根据传进来的candidates进行处理了。
如果传进来的是null,就会走默认方法。
如果传进来的数组只有一个候选值,并且无参,就直接走instantiate()方法进行实例化,跟进去就会发现也是使用反射的方式进行的实例化。
在这里插入图片描述
有参数的话就需要对参数进行处理。
在这里插入图片描述
对所有的候选构造参数进行排序,排序的依据是各个构造函数的参数的个数。
在这里插入图片描述
在循环中依次处理candidates;
这里面有两个短路方法,一个是如果之前已经找到了合适的构造函数,下一个candidate进来的时候就直接break,另一个是如果参数个数不符合要求,直接下一个。
在这里插入图片描述
最后根据找到的合适的构造函数进行实例化并返回。都是反射。
在这里插入图片描述

实例化没有@Autowired注解的有参构造函数。

举个栗子

第一种情况:只有一个无注解有参数的构造函数
结果:正常使用该构造函数初始化
在这里插入图片描述
第二种情况:有多个无注解有参数的构造函数
结果:发生异常

No default constructor found; nested exception is java.lang.NoSuchMethodException

怎么解决:添加一个无参数的构造函数
在这里插入图片描述
在这里插入图片描述

只有一个无注解有参数的构造函数

这种情况的入口跟带注解的构造函数的入口是一样的,走的也是determineConstructorsFromBeanPostProcessors()
在这里插入图片描述
但是返回的时候跟有@Autowired注解走的路径是不一样的。
如下图所示,如果没有有@Autowired注解的构造函数,就会走到标箭头的这个if中,看if的条件我们可以知道是只有一个构造函数并且该构造函数的参数个数大于0;

返回之后就和有@Autowired注解的构造函数实例化是一样的。
在这里插入图片描述

实例化有多个无注解有参数的构造函数

还是看上面那张图,有多个无注解有参数的构造函数在寻找candidateConstructors的时候会走到最后的else中,实例化candidateConstructors这个数组,但是里面什么都没有。看return语句我们发现如果这个数组里面没东西,会直接返回null。
在这里插入图片描述
返回null之后就不会再走有参构造函数的实例化过程,而是进行无参构造函数的实例化。
在这里插入图片描述
如果没有实现无参构造函数的话,就会抛出上述的异常。

实例化无参构造函数

有两种情况:
1、一个类中没有自定义的构造函数,那么Java会默认带一个无参构造函数;
2、用户自己新建了无参构造函数。如果用户自定义了有参构造函数,不手动创建无参构造函数的话,Java也不会创建无参构造函数。

入口:instantiateBean(beanName,mbd)

与上面有点区别的是,这块的实例化的逻辑是先实例化beanInstance,再封装进BeanWrapperImpl中。
在这里插入图片描述
进入初始化方法,可以看到会根据类获取构造函数,如果获取不到,就会抛出异常。前面所说的有多个有参构造函数但没有手动创建无参构造函数的异常就是这里抛出去的。
在这里插入图片描述

注解的收集

在createBeanInstace之后,就会在堆内存中生成这个实例对象,但是是一个空壳子,什么属性都还没有呢。
实例化之后进行注解的收集。
在这里插入图片描述
此处的MergedBeanDefinitionPostProcessors有两个:
1、AutowiredAnnotationBeanPostProcessor:负责@Autowired、@Value
2、CommonAnnotationBeanPostProcessor:负责@PostConstruct、@PreDestroy、@Resource
在这里插入图片描述
以AutowiredAnnotationBeanPostProcessor为例看一看里面是啥?
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
从上面几张图可以看出,就是在类中寻找带有注解的属性和方法,属性封装成AutowiredFieldElement对象,方法封装成AutowiredMethodElement对象,在整体封装为一个InjectionMetadata对象并放到BeanDefinition中。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值