解密Spring中的Bean实例化:推断构造方法(上)

打印 上一主题 下一主题

主题 847|帖子 847|积分 2541

在Spring中,一个bean需要通过实例化来获取一个对象,而实例化的过程涉及到构造方法的调用。本文将主要探讨简单的构造推断和实例化过程,让我们首先深入了解实例化的步骤。
实例化源码
  1. protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
  2.     // Make sure bean class is actually resolved at this point.
  3.     Class<?> beanClass = resolveBeanClass(mbd, beanName);
  4.     .....
  5.     // BeanDefinition中添加了Supplier,则调用Supplier来得到对象
  6.     Supplier<?> instanceSupplier = mbd.getInstanceSupplier();
  7.     if (instanceSupplier != null) {
  8.         return obtainFromSupplier(instanceSupplier, beanName);
  9.     }
  10.     // @Bean对应的BeanDefinition
  11.     if (mbd.getFactoryMethodName() != null) {
  12.         return instantiateUsingFactoryMethod(beanName, mbd, args);
  13.     }
  14.     // Shortcut when re-creating the same bean...
  15.     // 一个原型BeanDefinition,会多次来创建Bean,那么就可以把该BeanDefinition所要使用的构造方法缓存起来,避免每次都进行构造方法推断
  16.     boolean resolved = false;
  17.     boolean autowireNecessary = false;
  18.     if (args == null) {
  19.         synchronized (mbd.constructorArgumentLock) {
  20.             if (mbd.resolvedConstructorOrFactoryMethod != null) {
  21.                 resolved = true;
  22.                 // autowireNecessary表示有没有必要要进行注入,比如当前BeanDefinition用的是无参构造方法,那么autowireNecessary为false,否则为true,表示需要给构造方法参数注入值
  23.                 autowireNecessary = mbd.constructorArgumentsResolved;
  24.             }
  25.         }
  26.     }
  27.     if (resolved) {
  28.         // 如果确定了当前BeanDefinition的构造方法,那么看是否需要进行对构造方法进行参数的依赖注入(构造方法注入)
  29.         if (autowireNecessary) {
  30.             // 方法内会拿到缓存好的构造方法的入参
  31.             return autowireConstructor(beanName, mbd, null, null);
  32.         }
  33.         else {
  34.             // 构造方法已经找到了,但是没有参数,那就表示是无参,直接进行实例化
  35.             return instantiateBean(beanName, mbd);
  36.         }
  37.     }
  38.     // 如果没有找过构造方法,那么就开始找了
  39.     // Candidate constructors for autowiring?
  40.     // 提供一个扩展点,可以利用SmartInstantiationAwareBeanPostProcessor来控制用beanClass中的哪些构造方法
  41.     // 比如AutowiredAnnotationBeanPostProcessor会把加了@Autowired注解的构造方法找出来,具体看代码实现会更复杂一点
  42.     Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
  43.     // 如果推断出来了构造方法,则需要给构造方法赋值,也就是给构造方法参数赋值,也就是构造方法注入
  44.     // 如果没有推断出来构造方法,但是autowiremode为AUTOWIRE_CONSTRUCTOR,则也可能需要给构造方法赋值,因为不确定是用无参的还是有参的构造方法
  45.     // 如果通过BeanDefinition指定了构造方法参数值,那肯定就是要进行构造方法注入了
  46.     // 如果调用getBean的时候传入了构造方法参数值,那肯定就是要进行构造方法注入了
  47.     if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||
  48.             mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
  49.         return autowireConstructor(beanName, mbd, ctors, args);
  50.     }
  51.     // Preferred constructors for default construction?
  52.     ctors = mbd.getPreferredConstructors();
  53.     if (ctors != null) {
  54.         return autowireConstructor(beanName, mbd, ctors, null);
  55.     }
  56.     // No special handling: simply use no-arg constructor.
  57.     // 不匹配以上情况,则直接使用无参构造方法
  58.     return instantiateBean(beanName, mbd);
  59. }
复制代码
在Spring框架中的AbstractAutowireCapableBeanFactory类中的createBeanInstance()方法是用来执行实例化Bean对象的操作,该方法会根据Bean界说信息和配置选项,颠末一系列步骤和逻辑判断,终极创建一个全新的Bean实例。大抵步骤如下:

  • 根据BeanDefinition加载类并获取对应的Class对象
  • 如果BeanDefinition绑定了一个Supplier,那么应调用Supplier的get方法以获取一个对象,并将其直接返回。
  • 如果在BeanDefinition中存在factoryMethodName属性,则调用该工厂方法以获取一个bean对象,并将其返回。
  • 如果BeanDefinition已经自动构造过了,那么就调用autowireConstructor()方法来自动构造一个对象。
  • 调用SmartInstantiationAwareBeanPostProcessor接口的determineCandidateConstructors()方法,以确定哪些构造方法是可用的。
  • 如果存在可用的构造方法,大概当前BeanDefinition的autowired属性设置为AUTOWIRE_CONSTRUCTOR,大概BeanDefinition中指定了构造方法参数值,大概在创建Bean时指定了构造方法参数值,那么将调用autowireConstructor()方法来自动构造一个对象。
  • 最后,如果不符合前述环境,那么将根据不带参数的构造方法来实例化一个对象。
推断构造方法

我们看下源码:
  1. public Constructor<?>[] determineCandidateConstructors(Class<?> beanClass, final String beanName)
  2.         throws BeanCreationException {
  3.   //前面跟@Lookup相关,我们不看
  4.     .....
  5.     // Quick check on the concurrent map first, with minimal locking.
  6.     Constructor<?>[] candidateConstructors = this.candidateConstructorsCache.get(beanClass);
  7.     if (candidateConstructors == null) {
  8.         // Fully synchronized resolution now...
  9.         synchronized (this.candidateConstructorsCache) {
  10.             candidateConstructors = this.candidateConstructorsCache.get(beanClass);
  11.             if (candidateConstructors == null) {
  12.                 Constructor<?>[] rawCandidates;
  13.                 try {
  14.                     // 拿到所有的构造方法
  15.                     rawCandidates = beanClass.getDeclaredConstructors();
  16.                 }
  17.                 catch (Throwable ex) {
  18.                     throw new BeanCreationException(beanName,
  19.                             "Resolution of declared constructors on bean Class [" + beanClass.getName() +
  20.                             "] from ClassLoader [" + beanClass.getClassLoader() + "] failed", ex);
  21.                 }
  22.                 List<Constructor<?>> candidates = new ArrayList<>(rawCandidates.length);
  23.                 // 用来记录required为true的构造方法,一个类中只能有一个required为true的构造方法
  24.                 Constructor<?> requiredConstructor = null;
  25.                 // 用来记录默认无参的构造方法
  26.                 Constructor<?> defaultConstructor = null;
  27.                 ......
  28.                 int nonSyntheticConstructors = 0;
  29.                 // 遍历每个构造方法
  30.                 for (Constructor<?> candidate : rawCandidates) {
  31.                     if (!candidate.isSynthetic()) {
  32.                         // 记录一下普通的构造方法
  33.                         nonSyntheticConstructors++;
  34.                     }
  35.                     else if (primaryConstructor != null) {
  36.                         continue;
  37.                     }
  38.                     // 当前遍历的构造方法是否写了@Autowired
  39.                     MergedAnnotation<?> ann = findAutowiredAnnotation(candidate);
  40.                     if (ann == null) {
  41.                         // 如果beanClass是代理类,则得到被代理的类的类型,我们不看这种情况
  42.                         .......
  43.                     }
  44.                     // 当前构造方法上加了@Autowired
  45.                     if (ann != null) {
  46.                         // 整个类中如果有一个required为true的构造方法,那就不能有其他的加了@Autowired的构造方法
  47.                         if (requiredConstructor != null) {
  48.                             throw new BeanCreationException(beanName,
  49.                                     "Invalid autowire-marked constructor: " + candidate +
  50.                                     ". Found constructor with 'required' Autowired annotation already: " +
  51.                                     requiredConstructor);
  52.                         }
  53.                         boolean required = determineRequiredStatus(ann);
  54.                         if (required) {
  55.                             if (!candidates.isEmpty()) {
  56.                                 throw new BeanCreationException(beanName,
  57.                                         "Invalid autowire-marked constructors: " + candidates +
  58.                                         ". Found constructor with 'required' Autowired annotation: " +
  59.                                         candidate);
  60.                             }
  61.                             // 记录唯一一个required为true的构造方法
  62.                             requiredConstructor = candidate;
  63.                         }
  64.                         // 记录所有加了@Autowired的构造方法,不管required是true还是false
  65.                         // 如果默认无参的构造方法上也加了@Autowired,那么也会加到candidates中
  66.                         candidates.add(candidate);
  67.                         // 从上面代码可以得到一个结论,在一个类中,要么只能有一个required为true的构造方法,要么只能有一个或多个required为false的方法
  68.                     }
  69.                     else if (candidate.getParameterCount() == 0) {
  70.                         // 记录唯一一个无参的构造方法
  71.                         defaultConstructor = candidate;
  72.                     }
  73.                     // 有可能存在有参、并且没有添加@Autowired的构造方法
  74.                 }
  75.                 if (!candidates.isEmpty()) {
  76.                     // Add default constructor to list of optional constructors, as fallback.
  77.                     // 如果不存在一个required为true的构造方法,则所有required为false的构造方法和无参构造方法都是合格的
  78.                     if (requiredConstructor == null) {
  79.                         if (defaultConstructor != null) {
  80.                             candidates.add(defaultConstructor);
  81.                         }
  82.                         else if (candidates.size() == 1 && logger.isInfoEnabled()) {
  83.                             logger.info("Inconsistent constructor declaration on bean with name '" + beanName +
  84.                                     "': single autowire-marked constructor flagged as optional - " +
  85.                                     "this constructor is effectively required since there is no " +
  86.                                     "default constructor to fall back to: " + candidates.get(0));
  87.                         }
  88.                     }
  89.                     // 如果只存在一个required为true的构造方法,那就只有这一个是合格的
  90.                     candidateConstructors = candidates.toArray(new Constructor<?>[0]);
  91.                 }
  92.                 // 没有添加了@Autowired注解的构造方法,并且类中只有一个构造方法,并且是有参的
  93.                 else if (rawCandidates.length == 1 && rawCandidates[0].getParameterCount() > 0) {
  94.                     candidateConstructors = new Constructor<?>[] {rawCandidates[0]};
  95.                 }
  96.                 ......
  97.                 else {
  98.                     // 如果有多个有参、并且没有添加@Autowired的构造方法,是会返回空的
  99.                     candidateConstructors = new Constructor<?>[0];
  100.                 }
  101.                 this.candidateConstructorsCache.put(beanClass, candidateConstructors);
  102.             }
  103.         }
  104.     }
  105.     return (candidateConstructors.length > 0 ? candidateConstructors : null);
  106. }
复制代码
接下来,让我们更深入地探讨推断构造方法,并且我还简单绘制了一张图示。

通常环境下,一个类通常只包含一个构造方法:
要么是无参的构造方法,要么是有参的构造方法。
如果一个类只有一个无参构造方法,那么在实例化时只能使用这个构造方法;而如果一个类只有一个有参构造方法,实例化时是否可以使用这个构造方法则取决于具体环境:

  • 使用AnnotationConfigApplicationContext举行实例化时,Spring会根据构造方法的参数信息去寻找对应的bean,并将找到的bean传递给构造方法。这样可以实现依赖注入,确保实例化的对象具有所需的依赖项。
  • 当使用ClassPathXmlApplicationContext时,表明使用XML配置文件的方式来界说bean。在XML中,可以手动指定构造方法的参数值来实例化对象,也可以通过配置autowire=constructor属性,让Spring自动根据构造方法的参数类型去寻找对应的bean作为参数值,实现自动装配。
上面是只有一个构造方法的环境,那么如果一个类存在多个构造方法,那么Spring举行实例化之前,该如何去确定到底用哪个构造方法呢?

  • 如果开发者明确界说了他们想要使用的构造方法,那么程序将会优先使用这个构造方法。
  • 如果开发者没有明确指定他们想要使用的构造方法,体系会自动检查开发者是否已经配置了让Spring框架自动选择构造方法的选项。
  • 如果开发者没有显式地指定让Spring框架自动选择构造方法的环境下,Spring将会默认尝试使用无参构造方法。如果目标类中没有无参构造方法,则体系将会抛出错误提示。
针对第一点,开发者可以通过什么方式来指定使用哪个构造方法呢?

  • 在XML中,标签用于表示构造方法的参数。开发者可以根据这个标签确定所需使用的构造方法的参数个数,从而精确指定想要使用的构造方法。
  • 通过@Autowired注解,我们可以在构造方法上使用@Autowired注解。因此,当我们在特定构造方法上使用@Autowired注解时,表示开发者希望使用该构造方法。与通过xml方式直接指定构造方法参数值的方式不同,@Autowired注解方式需要Spring通过byType+byName的方式来查找符合条件的bean作为构造方法的参数值。
  • 当然,还有一种环境需要思量,即当多个构造方法上都标注了@Autowired注解时,此时Spring会抛出错误。然而,由于@Autowired注解具有一个required属性,默认值为true,因此在一个类中只能有一个构造方法标注了@Autowired大概@Autowired(required=true),否则会导致错误。不外,可以有多个构造方法标注@Autowired(required=false)。在这种环境下,Spring会自动从这些构造方法中选择一个举行注入。
在第二种环境下,如果开发者没有明确指定要使用的构造方法,Spring将尝试自动选择一个符合的构造方法举行注入。这种环境下,只能通过ClassPathXmlApplicationContext实现,由于使用AnnotationConfigApplicationContext无法指定让Spring自动选择构造方法。通过ClassPathXmlApplicationContext,可以在XML配置文件中指定某个bean的autowire属性为constructor,从而告诉Spring可以自动选择构造方法。
总结

在Spring中,实例化Bean对象涉及构造方法的调用。通过分析源码,我们了解到实例化的步骤和推断构造方法的过程。当一个类只有一个构造方法时,Spring会根据具体环境决定是否使用该构造方法。如果一个类存在多个构造方法,就需要根据具体环境具体分析。本文简单判断了哪些构造方法是符合实例化的,但在存在多个符合条件的构造方法时,具体使用哪个构造方法尚未讨论。因此,我筹划单独写一篇文章来详细讨论这个问题。毕竟是太复杂了。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

雁过留声

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表