一文详解Spring构造函数推断

 更新时间:2023年04月07日 11:31:52   作者:这堆干货有点猛  
这篇文章主要介绍了Spring构造函数推断自动注入及底层原理详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加

正文

Spring 提供了一组基本的功能,例如依赖注入(DI)和面向切面编程(AOP)。其中一个非常强大的功能是构造函数自动注入,也称为构造函数推断。在本文中,我们将深入探讨Spring构造函数推断的底层原理,并解释Spring是如何实现它的。

自动注入

构造函数自动注入是指 Spring 自动解析 bean 的构造函数参数,并将它们传递给相应的构造函数。这样,我们就不必显式地在XML或Java配置文件中指定每个 bean 的构造函数参数。这是一个非常方便的功能,特别是在有许多 bean 需要注入的情况下。

底层原理

Spring 使用 Java 反射机制来执行构造函数推断。当 Spring 需要实例化一个 bean 时,它将首先使用 Java 反射机制检查该 bean 的构造函数。

然后,它将检查每个构造函数的参数类型,并尝试在 Spring 应用程序上下文中查找与参数类型匹配的 bean。

如果找到匹配的 bean,则 Spring 将使用该 bean 实例化构造函数参数。如果找不到匹配的 bean,则 Spring 将继续检查下一个构造函数。

如果没有任何构造函数可以匹配,则 Spring 将抛出一个异常。但是,可以使用 @Autowired(required=false) 注解来取消必需的依赖关系,这意味着如果找不到与构造函数参数类型匹配的bean,则 Spring 将不会抛出异常。

以下是一个简单的例子,说明如何在 Spring 中使用构造函数推断:

public class MyBean {
    private final MyDependency myDependency;
    public MyBean(MyDependency myDependency) {
        this.myDependency = myDependency;
    }
    public void doSomething() {
        myDependency.doSomething();
    }
}

在这个例子中,MyBean依赖于MyDependency。Spring 将使用构造函数推断自动注入 MyDependency,而不需要在XML或Java配置文件中显式指定。

@Configuration
public class AppConfig {
    @Bean
    public MyDependency myDependency() {
        return new MyDependency();
    }
    @Bean
    public MyBean myBean() {
        return new MyBean(myDependency());
    }
}

在这个例子中,AppConfig 类使用 @Configuration 注解指定一个 Spring 应用程序上下文,并定义两个bean:MyDependency 和 MyBean。在 MyBean 的构造函数中,我们将 MyDependency 作为参数传递,Spring 将使用构造函数推断自动注入 MyDependency。

构造函数推断的限制

构造函数推断有一些限制,例如:

  • 如果有多个构造函数可以匹配,则 Spring 将抛出一个异常。在这种情况下,您需要使用 @Qualifier 注解来指定要注入的 bean。

  • 如果构造函数参数是原始类型(例如int、float、boolean等),则 Spring 无法推断它们。在这种情况下,您需要使用 @Value 注解将值直接注入到构造函数参数中。

  • 如果构造函数参数是数组或集合,则 Spring 无法推断它们。在这种情况下,您需要使用 @Qualifier 注解和 @Autowired 的 List 或 Set 类型的字段来手动注入数组或集合。

源码解析

Spring 使用 BeanWrapperImpl 类来执行构造函数推断。BeanWrapperImpl 类是 Spring 的核心类之一,它提供了一种方便的方式来对 Java 对象进行包装和访问。以下是 Spring 中用于执行构造函数推断的 BeanWrapperImpl 类的源代码:

public class BeanWrapperImpl extends AbstractPropertyAccessor implements BeanWrapper {
    // ...
    @Override
    public Object createInstance() throws BeansException {
        // 获取Bean的构造函数
        Constructor<?> constructorToUse = this.constructorResolver.autowireConstructor;
        if (constructorToUse == null) {
            throw new BeansException("No default constructor found");
        }
        try {
            // 使用构造函数创建Bean实例,并返回
            return constructorToUse.newInstance(getConstructorArgumentValues(constructorToUse), getBeanClassLoader());
        }
        catch (Throwable ex) {
            throw new BeanCreationException("Could not instantiate bean", ex);
        }
    }
    // ...
    private Constructor<?> autowireConstructor;
    // ...
    public BeanWrapperImpl(Class<?> clazz) {
        super();
        this.objectType = clazz;
        this.propertyEditorRegistry = new SimpleTypeConverter();
        this.constructorResolver = new ConstructorResolver(this.objectType, this);
    }
    // ...
    private class ConstructorResolver {
        // ...
        public ConstructorResolver(Class<?> clazz, BeanWrapperImpl bw) {
            // 查找可以使用的构造函数并将其缓存
            Constructor<?>[] candidates = clazz.getDeclaredConstructors();
            Constructor<?> autowireCandidate = null;
            int numAutowireCandidates = 0;
            for (Constructor<?> candidate : candidates) {
                if (candidate.isAnnotationPresent(Autowired.class)) {
                    autowireCandidate = candidate;
                    numAutowireCandidates++;
                }
            }
            if (numAutowireCandidates == 1) {
                this.autowireConstructor = autowireCandidate;
            }
            else if (numAutowireCandidates > 1) {
                throw new BeansException("Multiple autowire constructors found");
            }
        }
        // ...
    }
    // ...
}

在BeanWrapperImpl 类中,构造函数推断是在 createInstance() 方法中执行的。该方法首先获取与Bean匹配的构造函数(由constructorResolver.autowireConstructor决定),然后使用该构造函数创建 Bean 实例。

ConstructorResolver 内部类中,构造函数推断是通过查找带有 @Autowired 注解的构造函数来实现的。如果找到了一个带有 Autowired注解 的构造函数,则它将被缓存到 autowireConstructor 字段中,并在 createInstance() 方法中使用。

结论

构造函数自动注入是 Spring 的一个非常强大的功能,它可以大大简化 bean 的配置和管理。我们深入探讨了 Spring 构造函数推断的底层原理,并解释了 Spring 是如何实现它的,还提供了一个简单的例子和源代码解析,以帮助大家更好地了解构造函数推断的工作方式。

以上就是一文详解Spring构造函数推断的详细内容,更多关于Spring 构造函数推断的资料请关注脚本之家其它相关文章!

相关文章

  • 深入浅出MappedByteBuffer(推荐)

    深入浅出MappedByteBuffer(推荐)

    MappedByteBuffer使用虚拟内存,因此分配(map)的内存大小不受JVM的-Xmx参数限制,但是也是有大小限制的,这篇文章主要介绍了MappedByteBuffer的基本知识,需要的朋友可以参考下
    2022-12-12
  • 基于Java实现音乐播放器的示例代码

    基于Java实现音乐播放器的示例代码

    这篇文章主要为大家详细介绍了如何利用Java编写一个简单的音乐播放器,文中的示例代码讲解详细,具有一定的学习价值,感兴趣的小伙伴可以了解一下
    2023-07-07
  • 一文搞懂Java设计模式之责任链模式

    一文搞懂Java设计模式之责任链模式

    这篇文章主要给大家介绍了关于Java设计模式之责任链模式的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-12-12
  • 浅谈Java中的Filter过滤器

    浅谈Java中的Filter过滤器

    本篇文章主要介绍了浅谈Java中的Filter过滤器,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-02-02
  • SpringBoot中的static静态资源访问、参数配置、代码自定义访问规则详解

    SpringBoot中的static静态资源访问、参数配置、代码自定义访问规则详解

    这篇文章主要介绍了SpringBoot的static静态资源访问、参数配置、代码自定义访问规则,本文通过示例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-07-07
  • SpringBoot处理跨域请求的四种方法

    SpringBoot处理跨域请求的四种方法

    在现代Web应用中,由于安全性和隐私的考虑,浏览器限制了从一个域向另一个域发起的跨域HTTP请求,解决这个问题的一种常见方式是实现跨域资源共享(CORS),SpringBoot提供了多种方式来处理跨域请求,本文将介绍其中的几种方法,感兴趣的朋友可以参考下
    2023-12-12
  • JAVA基于SnakeYAML实现解析与序列化YAML

    JAVA基于SnakeYAML实现解析与序列化YAML

    这篇文章主要介绍了JAVA基于SnakeYAML实现解析与序列化YAML,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-12-12
  • springboot2.x引入feign踩的坑及解决

    springboot2.x引入feign踩的坑及解决

    这篇文章主要介绍了springboot2.x引入feign踩的坑及解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-01-01
  • httpclient的disableConnectionState方法工作流程

    httpclient的disableConnectionState方法工作流程

    这篇文章主要为大家介绍了httpclient的disableConnectionState方法工作流程源码解读,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-11-11
  • Java并发编程(CyclicBarrier)实例详解

    Java并发编程(CyclicBarrier)实例详解

    这篇文章主要介绍了Java并发编程(CyclicBarrier)实例详解的相关资料,JAVA编写并发程序的时候,我们需要仔细去思考一下并发流程的控制,如何让各个线程之间协作完成某项工作。
    2017-07-07

最新评论