Spring 推断构造方法

Spring 推断构造方法原理

Spring在创建Bean的生命周期中,在实例化得到一个Bean对象的过程中就需要用到Bean的构造方法,一个类(Class)可能存在一个构造方法多个构造方法的情况,下面对着两种情况来介绍Spring在实例化Bean过程中采用哪个构造方法进行Bean对象的实例化。

一个构造方法

一个类(Class)只有一个构造方法的情况,这个构造方法要么是无参构造方法,要么是有参构造方法,对于一个构造方法的情况,Spring采用构造方法实例化Bean对象的流程:

  • 如果只有一个无参构造方法,那么Spring只能采用这个构造方法进行Bean对象的实例化。

  • 如果只有一个有参构造方法,如果采用AnnotationConfigApplicationContext创建Spring容器时,Spring会根据构造方法的参数信息去寻找bean,然后传给构造方法、如果采用ClassPathXMLApplicationContext创建Spring容器时,表示使用XML的方式来使用bean,要么在XML中指定构造方法的参数值(手动指定),要么配置autowire=constructor让Spring自动取选择bean做为构造方法的参数。

多个构造方法

一个类(Class)中有多个构造方法的情况,Spring采用构造方法实例化Bean对象的流程:

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

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

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

指定构造方法

开发者指定使用构造方法的方式:

  • xml中可以使用标签来指定构造方法参数,根据指定构造方法参数的个数,从而确定指定使用的构造方法。

  • 通过@Autowired注解,@Autowired注解可以写在构造方法上,在构造方法上写@autowired注解,表示开发者指定采用这个构造方法,Spring通过byType+byName的方式去查找符合的bean做为构造方法的参数值

如果在多个构造方法上写了@Autowired注解,那么Spring启动会报错,@Autowired注解有一个属性required,默认值为true,所以一个类中只能有一个构造方法标注@Autowired或@Autowired(required=true),有多个会报错。但是可以有多个@Autowired(required=false),这种情况需要Spring从这些构造方法中取自动选择一个构造方法。

Spring自动选择构造方法

如果开发者没有指定使用的构造方法,则看开发者有没有让Spring自动去选择构造方法,对于Sping自动选择构造方法只能用在ClassPathXMLApplicationContext,AnnotationConfigApplicationContext没有办法去指定bean可以让Spring自动取选择构造方法。可以在xml中指定某个bean的autowire=constructor

Spring 推断构造方法实现
整体流程
  • AbstractAutowireCapableBeanFactory类中的createBeanInstance()方法会去创建一个Bean实例

  • 根据BeanDefinition加载类得到Class对象

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

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

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

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

  • 如果存在可用得构造方法,或者当前BeanDefinition的autowired是AUTOWIRE_CONSTRUCTOR,或者BeanDefinition中指定了构造方法参数值,或者创建Bean的时候指定了构造方法参数值,那么就调用autowireConstructor()方法自动构造一个对象

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

Spring底层实现源码

Spring推断构造方法实例化一个Bean对象底层实现源码位于AbstractAutowireCapableBeanFactory#createBeanInstance方法中。

protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
   // Make sure bean class is actually resolved at this point.
   Class<?> beanClass = resolveBeanClass(mbd, beanName);

   if (beanClass != null && !Modifier.isPublic(beanClass.getModifiers()) && !mbd.isNonPublicAccessAllowed()) {
      throw new BeanCreationException(mbd.getResourceDescription(), beanName,
            "Bean class isn't public, and non-public access not allowed: " + beanClass.getName());
   }

   // BeanDefinition中添加了Supplier,则调用Supplier来得到对象
   Supplier<?> instanceSupplier = mbd.getInstanceSupplier();
   if (instanceSupplier != null) {
      return obtainFromSupplier(instanceSupplier, beanName);
   }

   // @Bean对应的BeanDefinition
   if (mbd.getFactoryMethodName() != null) {
      return instantiateUsingFactoryMethod(beanName, mbd, args);
   }

   // Shortcut when re-creating the same bean...
   // 一个原型BeanDefinition,会多次来创建Bean,那么就可以把该BeanDefinition所要使用的构造方法缓存起来,避免每次都进行构造方法推断
   boolean resolved = false;
   boolean autowireNecessary = false;
   if (args == null) {
      synchronized (mbd.constructorArgumentLock) {
         if (mbd.resolvedConstructorOrFactoryMethod != null) {
            resolved = true;
            // autowireNecessary表示有没有必要要进行注入,比如当前BeanDefinition用的是无参构造方法,那么autowireNecessary为false,否则为true,表示需要给构造方法参数注入值
            autowireNecessary = mbd.constructorArgumentsResolved;
         }
      }
   }
   if (resolved) {
      // 如果确定了当前BeanDefinition的构造方法,那么看是否需要进行对构造方法进行参数的依赖注入(构造方法注入)
      if (autowireNecessary) {
         // 方法内会拿到缓存好的构造方法的入参
         return autowireConstructor(beanName, mbd, null, null);
      }
      else {
         // 构造方法已经找到了,但是没有参数,那就表示是无参,直接进行实例化
         return instantiateBean(beanName, mbd);
      }
   }

   // 如果没有找过构造方法,那么就开始找了

   // Candidate constructors for autowiring?
   // 提供一个扩展点,可以利用SmartInstantiationAwareBeanPostProcessor来控制用beanClass中的哪些构造方法
   // 比如AutowiredAnnotationBeanPostProcessor会把加了@Autowired注解的构造方法找出来,具体看代码实现会更复杂一点
   Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);

   // 如果推断出来了构造方法,则需要给构造方法赋值,也就是给构造方法参数赋值,也就是构造方法注入
   // 如果没有推断出来构造方法,但是autowiremode为AUTOWIRE_CONSTRUCTOR,则也可能需要给构造方法赋值,因为不确定是用无参的还是有参的构造方法
   // 如果通过BeanDefinition指定了构造方法参数值,那肯定就是要进行构造方法注入了
   // 如果调用getBean的时候传入了构造方法参数值,那肯定就是要进行构造方法注入了
   if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||
         mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
      return autowireConstructor(beanName, mbd, ctors, args);
   }

   // Preferred constructors for default construction?
   ctors = mbd.getPreferredConstructors();
   if (ctors != null) {
      return autowireConstructor(beanName, mbd, ctors, null);
   }

   // No special handling: simply use no-arg constructor.
   // 不匹配以上情况,则直接使用无参构造方法
   return instantiateBean(beanName, mbd);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值