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

在Spring中,一个bean需要通过实例化来获取一个对象,而实例化的过程涉及到构造方法的调用。本文将主要探讨简单的构造推断和实例化过程,让我们首先深入了解实例化的步骤。

实例化源码

java 复制代码
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);

    .....

    // 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);
}

在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()**方法来自动构造一个对象。
  • 最后,如果不符合前述情况,那么将根据不带参数的构造方法来实例化一个对象。

推断构造方法

我们看下源码:

java 复制代码
public Constructor<?>[] determineCandidateConstructors(Class<?> beanClass, final String beanName)
        throws BeanCreationException {
  //前面跟@Lookup相关,我们不看
    .....

    // Quick check on the concurrent map first, with minimal locking.
    Constructor<?>[] candidateConstructors = this.candidateConstructorsCache.get(beanClass);
    if (candidateConstructors == null) {
        // Fully synchronized resolution now...
        synchronized (this.candidateConstructorsCache) {
            candidateConstructors = this.candidateConstructorsCache.get(beanClass);
            if (candidateConstructors == null) {
                Constructor<?>[] rawCandidates;
                try {
                    // 拿到所有的构造方法
                    rawCandidates = beanClass.getDeclaredConstructors();
                }
                catch (Throwable ex) {
                    throw new BeanCreationException(beanName,
                            "Resolution of declared constructors on bean Class [" + beanClass.getName() +
                            "] from ClassLoader [" + beanClass.getClassLoader() + "] failed", ex);
                }
                List<Constructor<?>> candidates = new ArrayList<>(rawCandidates.length);

                // 用来记录required为true的构造方法,一个类中只能有一个required为true的构造方法
                Constructor<?> requiredConstructor = null;
                // 用来记录默认无参的构造方法
                Constructor<?> defaultConstructor = null;
                ......
                int nonSyntheticConstructors = 0;

                // 遍历每个构造方法
                for (Constructor<?> candidate : rawCandidates) {
                    if (!candidate.isSynthetic()) {
                        // 记录一下普通的构造方法
                        nonSyntheticConstructors++;
                    }
                    else if (primaryConstructor != null) {
                        continue;
                    }

                    // 当前遍历的构造方法是否写了@Autowired
                    MergedAnnotation<?> ann = findAutowiredAnnotation(candidate);
                    if (ann == null) {
                        // 如果beanClass是代理类,则得到被代理的类的类型,我们不看这种情况
                        .......
                    }

                    // 当前构造方法上加了@Autowired
                    if (ann != null) {
                        // 整个类中如果有一个required为true的构造方法,那就不能有其他的加了@Autowired的构造方法
                        if (requiredConstructor != null) {
                            throw new BeanCreationException(beanName,
                                    "Invalid autowire-marked constructor: " + candidate +
                                    ". Found constructor with 'required' Autowired annotation already: " +
                                    requiredConstructor);
                        }

                        boolean required = determineRequiredStatus(ann);
                        if (required) {
                            if (!candidates.isEmpty()) {
                                throw new BeanCreationException(beanName,
                                        "Invalid autowire-marked constructors: " + candidates +
                                        ". Found constructor with 'required' Autowired annotation: " +
                                        candidate);
                            }
                            // 记录唯一一个required为true的构造方法
                            requiredConstructor = candidate;
                        }
                        // 记录所有加了@Autowired的构造方法,不管required是true还是false
                        // 如果默认无参的构造方法上也加了@Autowired,那么也会加到candidates中
                        candidates.add(candidate);

                        // 从上面代码可以得到一个结论,在一个类中,要么只能有一个required为true的构造方法,要么只能有一个或多个required为false的方法
                    }
                    else if (candidate.getParameterCount() == 0) {
                        // 记录唯一一个无参的构造方法
                        defaultConstructor = candidate;
                    }

                    // 有可能存在有参、并且没有添加@Autowired的构造方法
                }


                if (!candidates.isEmpty()) {
                    // Add default constructor to list of optional constructors, as fallback.
                    // 如果不存在一个required为true的构造方法,则所有required为false的构造方法和无参构造方法都是合格的
                    if (requiredConstructor == null) {
                        if (defaultConstructor != null) {
                            candidates.add(defaultConstructor);
                        }
                        else if (candidates.size() == 1 && logger.isInfoEnabled()) {
                            logger.info("Inconsistent constructor declaration on bean with name '" + beanName +
                                    "': single autowire-marked constructor flagged as optional - " +
                                    "this constructor is effectively required since there is no " +
                                    "default constructor to fall back to: " + candidates.get(0));
                        }
                    }
                    // 如果只存在一个required为true的构造方法,那就只有这一个是合格的
                    candidateConstructors = candidates.toArray(new Constructor<?>[0]);
                }
                // 没有添加了@Autowired注解的构造方法,并且类中只有一个构造方法,并且是有参的
                else if (rawCandidates.length == 1 && rawCandidates[0].getParameterCount() > 0) {
                    candidateConstructors = new Constructor<?>[] {rawCandidates[0]};
                }
                ......
                else {
                    // 如果有多个有参、并且没有添加@Autowired的构造方法,是会返回空的
                    candidateConstructors = new Constructor<?>[0];
                }
                this.candidateConstructorsCache.put(beanClass, candidateConstructors);
            }
        }
    }
    return (candidateConstructors.length > 0 ? candidateConstructors : null);
}

接下来,让我们更深入地探讨推断构造方法,并且我还简单绘制了一张图示。

通常情况下,一个类通常只包含一个构造方法:

要么是无参的构造方法,要么是有参的构造方法。

如果一个类只有一个无参构造方法,那么在实例化时只能使用这个构造方法;而如果一个类只有一个有参构造方法,实例化时是否可以使用这个构造方法则取决于具体情况:

  • 使用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会根据具体情况决定是否使用该构造方法。如果一个类存在多个构造方法,就需要根据具体情况具体分析。本文简单判断了哪些构造方法是符合实例化的,但在存在多个符合条件的构造方法时,具体使用哪个构造方法尚未讨论。因此,我计划单独写一篇文章来详细讨论这个问题。毕竟是太复杂了。

相关推荐
chuanauc26 分钟前
Kubernets K8s 学习
java·学习·kubernetes
一头生产的驴42 分钟前
java整合itext pdf实现自定义PDF文件格式导出
java·spring boot·pdf·itextpdf
YuTaoShao1 小时前
【LeetCode 热题 100】73. 矩阵置零——(解法二)空间复杂度 O(1)
java·算法·leetcode·矩阵
zzywxc7871 小时前
AI 正在深度重构软件开发的底层逻辑和全生命周期,从技术演进、流程重构和未来趋势三个维度进行系统性分析
java·大数据·开发语言·人工智能·spring
YuTaoShao3 小时前
【LeetCode 热题 100】56. 合并区间——排序+遍历
java·算法·leetcode·职场和发展
程序员张33 小时前
SpringBoot计时一次请求耗时
java·spring boot·后端
llwszx6 小时前
深入理解Java锁原理(一):偏向锁的设计原理与性能优化
java·spring··偏向锁
云泽野7 小时前
【Java|集合类】list遍历的6种方式
java·python·list
二进制person7 小时前
Java SE--方法的使用
java·开发语言·算法
小阳拱白菜8 小时前
java异常学习
java