• Spring-推断构造方法


    Spring中的Bean实例化对象,需要构造方法

    通常一个类只有一个构造方法:

    1、无参的构造方法,实例化只能选择这一个

    2、有参的构造方法

    • 使用AnnotationConfigApplicationContext,会使用这个构造方法进行实例化,Spring根据构造方法的参数信息去寻找Bean,然后传给构造方法

    • 使用ClassPathXmlApplicationContext,表示使用XML的方式来使用bean。在XML中指定手动指定构造方法的参数值;或者配置autowire=constructor让Spring自动去寻找bean做为构造方法参数值。

    多个构造方法情况:

    1、如果开发者指定了想要使用的构造方法,那么就用这个构造方法

    • xml中的标签,表示构造方法参数,可以根据这个确定想要使用的构造方法的参数个数,从而确定想要使用的构造方法,直接指定了构造方法的参数值

    • 通过@Autowired注解,@Autowired注解可以写在构造方法上,表示开发者想使用哪个构造方法,其实是Spring通过byType+byName选择具体哪一个构造方法

    2、如果开发者没有指定想要使用的构造方法,则看开发者有没有让Spring自动选择构造方法

    • 只能在ClassPathXmlApplicationContext中使用,可以在xml中指定某个bean的autowire为constructor,通过构造方法自动注入

    3、如果开发者也没有让Spring自动选择构造方法,则Spring利用无参构造方法,如果没有无参构造方法,则报错

    注意:多个构造方法上写了@Autowired注解会报错。@Autowired的属性required,默认为ture

    @Autowired或@Autowired(required=true),有多个会报错

    但是@Autowired(required=false)可以有多个

    源码思路

    1、AbstractAutowireCapableBeanFactory类中的createBeanInstance()方法会去创建一个Bean实例

    2、根据BeanDefinition加载类得到Class对象

    3、如果BeanDefinition绑定了一个Supplier,那就调用Supplier的get方法得到一个对象并直接返回

    4、如果BeanDefinition中存在factoryMethodName,那么就调用该工厂方法得到一个bean对象并返回

    5、如果BeanDefinition已经自动构造过了,那就调用autowireConstructor()自动构造一个对象

    6、调用SmartInstantiationAwareBeanPostProcessor的determineCandidateConstructors()方法得到哪些构造方法是可以用的

    7、存在可用得构造方法;当前BeanDefinition的autowired是 AUTOWIRE_CONSTRUCTOR;BeanDefinition中指定了构造方法参数值;创建Bean的时候指定了构造方法参数值,符合这几种就调用autowireConstructor()方法自动构造一个对象

    8、最后,如果不是上述情况,就根据无参的构造方法实例化一个对象

    autowireConstructor()

    1、先检查是否指定了具体的构造方法和构造方法参数值,或者在BeanDefinition中缓存了具体的构造方法或构造方法参数值,如果存在那么则直接使用该构造方法进行实例化

    2、如果没有确定的构造方法或构造方法参数值,那么

    a)如果没有确定的构造方法,那么则找出类中所有的构造方法

    b)如果只有一个无参的构造方法,那么直接使用无参的构造方法进行实例化

    c)如果有多个可用的构造方法或者当前Bean需要自动通过构造方法注入

    d)根据所指定的构造方法参数值,确定所需要的最少的构造方法参数值的个数

    e)对所有的构造方法进行排序,参数个数多的在前面

    f)遍历每个构造方法

    g)如果不是调用getBean方法时所指定的构造方法参数值,那么则根据构造方法参数类型找值

    h)如果是调用getBean方法时所指定的构造方法参数值,就直接利用这些值

    i)如果根据当前构造方法找到了对应的构造方法参数值,那么这个构造方法就是可用的,但是不一定这个构造方法就是最佳的,所以这里会涉及到是否有多个构造方法匹配了同样的值,这个时候就会用值和构造方法类型进行匹配程度的打分,找到一个最匹配的

    为什么分越少优先级越高?

    主要看计算找到的bean和构造方法参数类型匹配程度有多高

    假设bean的类型为A,A的父类是B,B的父类是C,同时A实现了接口D 如果构造方法的参数类型为A,那么完全匹配,得分为0 如果构造方法的参数类型为B,那么得分为2 如果构造方法的参数类型为C,那么得分为4 如果构造方法的参数类型为D,那么得分为1

    1. Object[] objects = new Object[]{new A()};
    2. // 0
    3. System.out.println(MethodInvoker.getTypeDifferenceWeight(new Class[]{A.class}, objects));
    4. // 2
    5. System.out.println(MethodInvoker.getTypeDifferenceWeight(new Class[]{B.class}, objects));
    6. // 4
    7. System.out.println(MethodInvoker.getTypeDifferenceWeight(new Class[]{C.class}, objects));
    8. // 1
    9. System.out.println(MethodInvoker.getTypeDifferenceWeight(new Class[]{D.class}, objects));

    可以发现,越匹配分数越低

    @Bean的情况

    首先,Spring会把@Bean修饰的方法解析成BeanDefinition:

    1、如果方法是static的,那么解析出来的BeanDefinition中:

    • factoryBeanName为AppConfig所对应的beanName,比如"appConfig"

    • factoryMethodName为对应的方法名,比如"aService"

    • . factoryClass为AppConfig.class

    2、如果方法不是static的,那么解析出来的BeanDefinition中:

    • factoryBeanName为null

    • factoryMethodName为对应的方法名,比如"aService"

    • factoryClass也为AppConfig.class

    @Bean生成的BeanDefinition中,有一个重要的属性isFactoryMethodUnique,表示factoryMethod是不是唯一的,在普通情况下@Bean生成的BeanDefinition的isFactoryMethodUnique为true,但是如果出现了方法重载,那么就是特殊的情况。

    1. @Bean
    2. public static AService aService()
    3. {
    4. return new AService();
    5. }
    6. @Bean
    7. public AService aService(BService bService)
    8. {
    9. return new AService();
    10. }

    注意:上面这种情况只会生成一个aService的Bean

    假设Spring先解析到第一个@Bean,会生成一个BeanDefinition,此时isFactoryMethodUnique为true,但是解析到第二个@Bean时,会判断出来beanDefinitionMap中已经存在一个aService的BeanDefinition了,那么会把之前的这个BeanDefinition的isFactoryMethodUnique修改为false,并且不会生成新的BeanDefinition

    后续在根据BeanDefinition创建Bean时,会根据isFactoryMethodUnique来操作,如果为 true,那就表示当前BeanDefinition只对应了一个方法,那也就是只能用这个方法来创建Bean了, 但是如果isFactoryMethodUnique为false,那就表示当前BeanDefition对应了多个方法,需要和推 断构造方法的逻辑一样,去选择用哪个方法来创建Bean。

  • 相关阅读:
    Go语言中的Gin框架的初步使用
    机械臂<四大家族>你知道多少
    MySQL的级联操作
    软件测试--编写测试计划
    uniapp中tabbar导航的点击事件
    【牛客网刷题】(第四弹)多道中等难度题,早日拿offer,快来看看
    【web-解析目标】(1.2.3)解析应用程序:确定服务器端功能
    精准化设备巡检,大幅度提升设备利用率
    Redis 数据结构操作相关命令
    森林防火系统集成解决方案
  • 原文地址:https://blog.csdn.net/weixin_58482311/article/details/134322844