首页 > 其他分享 >解密Spring中的Bean实例化:推断构造方法(上)

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

时间:2024-03-04 17:23:13浏览次数:22  
标签:构造方法 Autowired mbd Spring required Bean null

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

实例化源码

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

推断构造方法

我们看下源码:

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

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

image

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

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

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

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

标签:构造方法,Autowired,mbd,Spring,required,Bean,null
From: https://www.cnblogs.com/guoxiaoyu/p/18045706

相关文章

  • 【Spring】Spring的事件监听ApplicationListener 、ApplicationEvent
     Spring中的事件监听机制,事件驱动开发可以用在以下这些场景:1.业务代码入口在下一级模块module,sender消息发送者在上一级模块module,不想循环依赖,就这样用2.解耦合的事件驱动模式,观察者模式等3.待补充实例代码:1.业务代码testAAA,执行同步业务privatevoidtestAAA(List<Chann......
  • springboot3+vue3(三)接口参数校验Spring Validation框架
    1、引入Validation依赖<!--参数校验依赖validation--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-validation</artifactId></dependency>va......
  • springboot3+vue3(二)注册接口
     为了方便实体类操作,这里添加一下lombok的依赖,添加好以后右键重新加载mavenlombok注解含义大全:https://www.jianshu.com/p/41c4a226e955<!--lombok依赖--><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifa......
  • 【2024面试刷题】一、Spring Cloud 面试题
    1、什么是SpringCloud?SpringCloud是一系列框架的有序集合。它利用SpringBoot的开发便利性巧妙地简化了分布式系统基础设施的开发,如服务发现注册、配置中心、智能路由、消息总线、负载均衡、断路器、数据监控等,都可以用SpringBoot的开发风格做到一键启动和部署。SpringClou......
  • springboot3+vue3大事件(一)项目准备工作
     1、执行sql脚本,准备数据库表--创建数据库createdatabasedev;--使用数据库usedev;--用户表createtableuser(idintunsignedprimarykeyauto_incrementcomment'ID',usernamevarchar(20)notnullunique......
  • 「Java开发指南」MyEclipse如何支持Spring Scaffolding?(四)
    在上文中主要为大家介绍了应用程序分层、代码助手等内容,本文将继续介绍SpringDSL模型等。MyEclipsev2023.1.2离线版下载MyEclipse技术交流群:742336981欢迎一起进群讨论5.SpringDSL模型支持Spring的MyEclipse还有一个特性,它是面向那些希望通过使用抽象层(模型驱动开发)来......
  • SpringCloud Alibaba-5-并发访问
    微服务架构应用设计目的为了应对高并发环境!1.什么是并发,并行并发:指在同一时间段内,多个任务或进程同时执行或交替执行的能力。并行:指多个任务在同一时间段内同时执行,需要多个处理器或多核处理器来实现。总的来说,并发更多地强调任务之间的交替执行,而并行更多地强调任务的同......
  • Spring 的 IOC 和 AOP 是什么,有哪些优点?
    Spring框架中的IOC是**控制反转**,AOP是**面向切面编程**。IOC是Spring框架的核心特性之一,它代表的是控制反转,意味着将对象的创建和管理交给Spring容器,而不是传统的在对象内部进行控制。这样可以实现对象之间的解耦,提高代码的可维护性和灵活性。IOC的底层原理包括XML解析、工厂模......
  • SpringBoot—01—简介;基本使用;
     @RequestMapping@Controller@GetMapping@ResponstBody(不走视图解析,直接返回数据)@Component(是Controller和Service的父类,就是让sping创建一个bean)@ConfigurationPerproties(prefix=xxx),从配置文件中读取配置赋值给实体类@Autowire,自动装配,但是具体的还是没搞懂@valiated,做......
  • Java高频面试题---Spring
    一、介绍一下Spring的IOC所谓的IOC,就是控制反转的意思。何为控制反转?在传统的程序设计中,应用程序代码通常控制着对象的创建和管理。例如,一个对象需要依赖于其他对象,那么它会直接new出来对象。这样的设计通常被称为“流程控制”。而在IOC中,控制关系发生了反转。控制权被转......