Spring之推断构造方法源码解析
1、推断构造方法流程图
https://www.processon.com/view/link/5f97bc717d9c0806f291d7eb
2、AutowiredAnnotationBeanPostProcessor中推断构造方法的不同情况分析

https://www.processon.com/view/link/6146def57d9c08198c58bb26
// 有多个构造方法都可以使用
@Component
public class UserService {
    @Autowired
    private OrderService orderService;
    public UserService() {
        System.out.println("无参的构造方法");
    }
    public UserService(OrderService orderService) {
        this.orderService = orderService;
        System.out.println("参数个数为1的构造方法");
    }
    public UserService(OrderService orderService, OrderService orderService1) {
        this.orderService = orderService;
        System.out.println("参数个数为2的构造方法");
    }
    public void test() {
        System.out.println("orderService = " + orderService);
    }
}
// autowire为AUTOWIRE_CONSTRUCTOR
public static void main(String[] args) {
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
    AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition();
    beanDefinition.setBeanClass(UserService.class);
    beanDefinition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR);
    context.registerBeanDefinition("userService", beanDefinition);
    UserService userService = (UserService) context.getBean("userService");
    userService.test(); // 打印:参数个数为2的构造方法
}
// beanDefinition中指定了构造方法的参数值
public static void main(String[] args) {
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
    AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition();
    beanDefinition.setBeanClass(UserService.class);
    beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(new OrderService());
    context.registerBeanDefinition("userService", beanDefinition);
    UserService userService = (UserService) context.getBean("userService");
    userService.test(); // 打印:参数个数为2的构造方法
}
// getBean()时指定了构造方法参数
public static void main(String[] args) {
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
    UserService userService = (UserService) context.getBean("userService", new OrderService());
    userService.test(); // 打印:参数个数为1的构造方法
}
 
Spring 中的一个 Bean,需要实例化才能得到一个对象,而实例化就需要用到构造方法。
一般情况下,一个类只有一个构造方法:
1、要么是无参的构造方法
2、要么是有参的构造方法
如果只有一个无参的构造方法,那么实例化就只能使用这个无参的构造方法了。
如果只有一个有参的构造方法,那么实例化时能使用这个有参的构造方法吗?要分情况讨论:
1、使用 AnnotationConfigApplicationContext,会使用这个构造方法进行实例化,那么 Spring 会根据构造方法的参数信息去寻找 Bean,然后传给构造方法
2、使用 ClassPathXmlApplicationContext,表示使用 XML 的方式来使用 Bean,要么在 XML 中指定构造方法的参数值(手动指定),要么配置autowire=constructor 让 Spring 自动去寻找 Bean 做为构造方法的参数值。

上面是只有一个构造方法的情况,那如果有多个构造方法呢?
这里又分为两种情况,多个构造方法中存不存在无参的构造方法。
分析:一个类存在多个构造方法,那么 Spring 在进行实例化之前,该如何去确定到底用哪个构造方法呢?
1、如果程序员指定了想要使用的构造方法,那么就用这个指定的构造方法
2、如果程序员没有指定想要使用的构造方法,则看程序员有没有让 Spring 自动去选择构造方法(AUTOWIRE_CONSTRUCTOR)
3、如果程序员也没有让 Spring 自动去选择构造方法,则 Spring 利用无参的构造方法,如果没有无参的构造方法,则 Spring 会报错
针对第一点,程序员可以通过什么方式来指定使用哪个构造方法呢?
1、xml 中的 <constructor-arg> 标签,这个标签表示构造方法参数,所以可以根据这个标签确定想要使用的构造方法的参数个数,从而确定想要使用的构造方法
2、通过 @Autowired 注解,@Autowired 注解可以写在构造方法上,所以哪个构造方法上写了@Autowired 注解,则表示程序员想使用该构造方法进行实例化,当然,它和第一个方式的不同点是,通过 xml 的方式,我们直接指定了构造方法的参数值,而通过 @Autowired 注解的方式,则需要 Spring 通过 byType + byName 的方式去找到符合条件的 Bean 作为构造方法的参数值
再来看第二点
如果程序员没有指定想要使用的构造方法,则看程序员有没有让 Spring 自动去选择构造方法,对于这一点,只能用在 ClassPathXmlApplicationContext,因为通过 AnnotationConfigApplicationContext 没有办法指定某个 bean 可以自动去选择构造方法,而通过 ClassPathXmlApplicationContext 可以在 xml 中指定某个 bean 的 autowire 为 constructor,这个属性表示通过构造方法进行自动注入,所以需要自动去选择一个构造方法进行自动注入,因为是构造方法,所以顺便进行实例化。
注意:自动选择是 xml 方式和注解方式都可以的,但是 xml 需要配置一下 autowire 参数
当然,还有一种情况,就是在多个构造方法上写了
@Autowired注解,那么此时 Spring 会报错。
但是,因为 @Autowired 还有一个属性 required,默认为 ture,所以在一个类中,只能有一个构造方法标注了 @Autowired 或 @Autowired(required=true),如果有多个则会报错。但可以有多个 @Autowired(required=false),在这种情况下,需要 Spring 从这些构造方法中自动选择一个构造方法。
小结
1、默认情况下用无参的构造方法,或者只有一个构造方法就用那一个
2、程序员指定了构造方法的入参值,通过 getBean() 或者 beanDefinition.getConstructorArgumentValues().addGenericArgumentValue() 指定,那就用所匹配的构造方法
3、程序员想让 Spring 自动选择构造方法以及构造方法的入参值, 则通过 XML 方式的 autowire="constructor"
4、程序员通过 @Autowired 注解指定了某个构造方法,但是希望 Spring 自动找到该构造方法的入参值
3、源码分析
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( )


先检查是否指定了具体的构造方法和构造方法的参数值,或者在 BeanDefinition 中缓存了具体的构造方法或构造方法的参数值,如果存在则直接使用该构造方法进行实例化
如果没有确定的构造方法或构造方法的参数值,那么
1、如果没有确定的构造方法,那么则找出类中所有的构造方法
2、如果只有一个无参的构造方法,那么直接使用无参的构造方法进行实例化
3、如果有多个可用的构造方法或者当前 Bean 需要自动通过构造方法注入
4、根据所指定的构造方法参数值,确定所需要的构造方法的参数值的最少个数

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

6、遍历每个构造方法
7、如果没有通过调用 getBean() 方法指定构造方法的参数值,那么则根据构造方法的参数类型找值

8、如果通过调用 getBean() 方法指定了构造方法的参数值,就直接利用这些值

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

为什么分数越低优先级越高?
主要是计算找到的 Bean 和构造方法的参数类型匹配程度有多高。
假设 Bean 的类型为A,A的父类是B,B的父类是C,同时A实现了接口D
public class A extends B implements D {
}
public class B extends C {
}
public class C {
}
public interface D {
}
 
1、如果构造方法的参数类型为A,那么完全匹配,得分为0
2、如果构造方法的参数类型为B,那么得分为2
3、如果构造方法的参数类型为C,那么得分为4
4、如果构造方法的参数类型为D,那么得分为1
可以直接使用以下代码进行测试:
public static void main(String[] args) {
    Object[] objects = new Object[]{new A()};
    System.out.println(MethodInvoker.getTypeDifferenceWeight(new Class[]{A.class}, objects)); // 0
    System.out.println(MethodInvoker.getTypeDifferenceWeight(new Class[]{B.class}, objects)); // 2
    System.out.println(MethodInvoker.getTypeDifferenceWeight(new Class[]{C.class}, objects)); // 4
    System.out.println(MethodInvoker.getTypeDifferenceWeight(new Class[]{D.class}, objects)); // 1
}
 
4、@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 的,但如果出现了方法重载,那么就是特殊的情况,比如:
@Bean
public static AService aService(){
		return new AService();
}
@Bean
public AService aService(BService bService){
		return new AService();
}
 
虽然有两个 @Bean,但结果肯定只会生成一个 aService 的 Bean,那么 Spring 在处理 @Bean 时,也只会生成一个 aService 的 BeanDefinition,比如 Spring 先解析到第一个 @Bean,会生成一个 BeanDefinition,此时 isFactoryMethodUnique 属性为 true,但是当解析到第二个 @Bean 时,会判断出来beanDefinitionMap 中已经存在一个 aService 的 BeanDefinition 了,那么会把之前的这个 BeanDefinition 的 isFactoryMethodUnique 属性值修改为 false,并且不会生成新的 BeanDefinition 了。
并且后续在根据 BeanDefinition 创建 Bean 时,会根据 isFactoryMethodUnique 属性来操作,如果为 true,那就表示当前 BeanDefinition 只对应了一个方法,那也就是只能用这个方法来创建 Bean 了,但是如果 isFactoryMethodUnique 属性为 false,那就表示当前 BeanDefition 对应了多个 @Bean 方法,需要和推断构造方法的逻辑一样,去选择用哪个方法来创建 Bean。
5、@Lookup的情况
@Component
public class UserService {
    @Autowired
    private OrderService orderService;
    public void test() {
        System.out.println(a());
    }
    @Lookup("orderService")
    public OrderService a() {
        return null;
    }
}
@Component
@Scope("prototype")
public class OrderService {
}
 
public class Test {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
        UserService userService = context.getBean("userService", UserService.class);
        userService.test();
        userService.test();
        userService.test();
    }
}
 

源码分析
1、org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean

2、org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBeanInstance

3、org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#determineConstructorsFromBeanPostProcessors

4、org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#determineCandidateConstructors

5、org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#instantiateBean

6、org.springframework.beans.factory.support.InstantiationStrategy

7、org.springframework.beans.factory.support.SimpleInstantiationStrategy#instantiate(org.springframework.beans.factory.support.RootBeanDefinition, java.lang.String, org.springframework.beans.factory.BeanFactory)

8、org.springframework.beans.factory.support.CglibSubclassingInstantiationStrategy.CglibSubclassCreator#instantiate

9、org.springframework.beans.factory.support.CglibSubclassingInstantiationStrategy.LookupOverrideMethodInterceptor#intercept




















