Spring的构造注入

注入:通过Spring的配置文件,为成员变量赋值
Set注入:Spring调用Set方法,通过配置文件,为成员变量赋值
构造注入:Spring调用构造方法,通过配置文件,为成员变量赋值

1.开发步骤

  • 提供有参构造方法

    public class Customer implements Serializable {
    private String name;
    private int age;

    public Customer(String name, int age) {
        this.name = name;
        this.age = age;
    }
    
    @Override
    public String toString() {
        return "Customer{" +
                "name='" + name + ''' +
                ", age=" + age +
                '}';
    }
    

    }

  • 通过Spring的配置文件来为成员变量赋值

    tom 1

一个构造参数,对应构造方法的一个构造参数,而且顺序是对应的
在这里插入图片描述

2.构造方法的重载

我们先来回顾一下什么是方法的重载

             1.在同一个类中
             2.方法名相同
             3.形参列表不同(个数,类型,顺序)
             4.方法的重载和返回值类型无关,和权限修饰符也无关

那么当构造方法发生重载的时候,我们想要构造注入为成员变量赋值需要注意什么细节呢?

2.1参数个数不同

public class Customer implements Serializable {
    private String name;
    private int age;

    public Customer(String name) {
        this.name = name;
    }

    public Customer(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "Customer{" +
                "name='" + name + ''' +
                ", age=" + age +
                '}';
    }
}

如果说,我们现在只想为name属性赋值的话,那么age就会有默认值

<bean id="customer" class="com.zyh.basic.Customer">
    <constructor-arg >
        <value>jack</value>
    </constructor-arg>
 
</bean>

在这里插入图片描述

总结:
	我们可以发现当构造参数个数不同的时候,我们可以通过
	<constructor-arg >标签的数量来进行区分

2.2构造参数个数相同时

我们再来看看另外一种情况,当重载的构造方法中,有两个构造方法的参数都只有一个,那么Spring怎么知道我们要为哪一个成员变量赋值呢。这个时候我们就应该通过成员变量的类型来进行区分

通过标签引入type属性,进行类型区分
<bean id="customer" class="com.zyh.basic.Customer">
    <constructor-arg type="int" >
        <value>3</value>
    </constructor-arg>
 
</bean>


public class Customer implements Serializable {
    private String name;
    private int age;

    public Customer(String name) {
        this.name = name;
    }

    public Customer(int age) {
        this.age = age;
    }

    public Customer(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "Customer{" +
                "name='" + name + ''' +
                ", age=" + age +
                '}';
    }
}

type属性指明要赋值的参数类型

<bean id="customer" class="com.zyh.basic.Customer">
    <constructor-arg type="int" >
        <value>3</value>
    </constructor-arg>
 
</bean>

在这里插入图片描述
这个时候,我们还会有一种疑惑,如果有两个成员变量的类型一样呢?
哈哈,大家想到这里肯定是有经过认真思考的,不过大家可能忘记了重载的定义,我们说重载指的是参数的个数,类型,顺序中有一个不同就构成了重载,但是注意,我们从来都没有说过,参数的名字不同构成重载,着就是语法报错,毕竟我们要记住,参数名是可以随便乱取的

3.注入总结

  • set注入用得比较多
  • 构造注入相对来说毕竟麻烦,毕竟有构造方法重载的问题
  • Spring框架中,大量应用了set注入
<think>我们正在讨论Spring框架中的构造注入和Setter注入。根据提供的引用,我将整理这两种注入方式的原理、区别及使用场景。 原理: - 构造注入:通过类的构造方法来实现依赖注入。在创建Bean实例时,Spring容器会调用相应的构造器,并传入所需的依赖项作为参数。 - Setter注入:通过类的Setter方法来实现依赖注入。在Bean实例化后,Spring容器会调用Setter方法,将依赖项作为参数传入。 区别: 1. 注入时机: - 构造注入:在对象创建时完成依赖注入,确保对象完全初始化后才可使用。 - Setter注入:在对象创建后,通过调用Setter方法注入依赖,因此对象可能处于部分初始化状态(如果某些依赖未注入)。 2. 依赖不可变性: - 构造注入:通常将依赖声明为`final`,注入后不可变。 - Setter注入:依赖可变,可以在对象生命周期中多次注入(但通常不推荐频繁更改)。 3. 依赖必要性: - 构造注入:强制要求依赖,适用于必需依赖。如果容器无法提供所需的依赖,则创建对象失败。 - Setter注入:适用于可选依赖,允许依赖为`null`(需要谨慎处理空指针异常)。 4. 循环依赖处理: - 构造注入:无法解决构造器级别的循环依赖(Spring会抛出`BeanCurrentlyInCreationException`)。 - Setter注入:可以解决循环依赖,因为Spring先创建对象(通过无参构造器),然后通过Setter注入依赖。 5. 初始化完整性: - 构造注入:保证对象完全初始化,因为依赖在构造时已注入。 - Setter注入:可能存在部分初始化状态,因为依赖可能未被完全注入。 使用场景: - 构造注入:推荐用于强制依赖(即对象运行所必需的依赖),特别是当依赖不可变时。此外,它保证了对象在创建后立即处于可用状态。 - Setter注入:适用于可选依赖,或者需要动态更改依赖的场景。也适用于解决循环依赖的情况。 另外,根据引用[2]和[3],实际开发中可以结合使用: - 强制依赖使用构造注入:确保必要的依赖在对象创建时就被注入。 - 可选依赖使用Setter注入:提供灵活性,允许在运行时更改或省略依赖。 注意:如果受控对象没有提供Setter方法,则必须使用构造注入。同时,Spring官方推荐使用构造注入(尤其是在Spring 4.x及以后版本),因为它更安全且能保证不可变性。但具体使用哪种方式,还需根据实际需求灵活选择。 相关问题: 1. 构造注入如何处理循环依赖问题? 2. 在Spring Boot应用中,更推荐使用构造注入还是Setter注入?为什么? 3. 如何配置Spring以同时使用构造注入和Setter注入? </think>### Spring构造注入与Setter注入详解 #### 一、核心原理 1. **构造注入** 通过类构造函数实现依赖注入,在对象创建时强制注入所有必需依赖。 ```java public class UserService { private final UserRepository repo; // final字段保证不变性 public UserService(UserRepository repo) { // 构造注入 this.repo = repo; } } ``` 2. **Setter注入** 通过Setter方法实现依赖注入,允许在对象创建后动态修改依赖。 ```java public class OrderService { private PaymentGateway gateway; // 非final字段 public void setGateway(PaymentGateway gateway) { // Setter注入 this.gateway = gateway; } } ``` #### 二、关键区别 | 特性 | 构造注入 | Setter注入 | |---------------------|------------------------------|-------------------------------| | **注入时机** | 对象创建时强制完成 | 对象创建后可延迟注入 | | **依赖不可变性** | 支持final字段(强制不可变) | 依赖可变(可多次调用Setter) | | **依赖必要性** | 强制要求所有必需依赖 | 支持可选依赖(允许null) | | **初始化完整性** | 对象完全初始化后才可使用 | 可能处于部分初始化状态[^1] | | **循环依赖处理** | 无法解决构造函数循环依赖 | 可通过延迟加载解决循环依赖[^2] | | **代码健壮性** | 避免NPE(依赖不可为空) | 需手动检查空指针风险[^3] | #### 三、使用场景指南 1. **优先使用构造注入** - 强制依赖(如数据库连接、核心服务) - 需要依赖不变性的场景(如并发安全) - Spring Boot官方推荐方式(保证线程安全)[^1] 2. **适用Setter注入的场景** - 可选依赖(如日志服务、配置参数) - 需要动态切换依赖的实现 - 遗留代码(无构造函数可用时) - 解决特定循环依赖问题[^2] 3. **混合使用策略**(推荐实践) ```java public class ProductService { private final InventoryDao dao; // 强制依赖 private DiscountStrategy strategy; // 可选依赖 // 构造注入核心依赖 public ProductService(InventoryDao dao) { this.dao = dao; } // Setter注入可选依赖 public void setStrategy(DiscountStrategy strategy) { this.strategy = strategy; } } ``` #### 四、决策流程图 ``` ┌─────────────┐ │ 依赖是否必需? │ └──────┬──────┘ ┌───────────┴───────────┐ Yes │ │ No ┌────────┴───────┐ ┌───────┴───────┐ │ 需要依赖不可变吗? │ │ 依赖需要动态变更吗?│ └───────┬────────┘ └───────┬───────┘ Yes│ │No Yes│ │No ▼ ▼ ▼ ▼ ┌────────────┐ ┌─────────────┐ ┌──────────┐ ┌──────────┐ │ 构造注入 │ │混合注入 │ │Setter注入 │ │无需注入 │ │ (强制+不可变)│ │(构造器+Setter)│ │(动态依赖) │ │(如常量字段)│ └────────────┘ └─────────────┘ └──────────┘ └──────────┘ ``` > **最佳实践提示**: > - Spring 4.3+ 支持单构造函数的隐式注入(无需`@Autowired`) > - 使用`@Required`注解可强制Setter注入的依赖不为空 > - 避免在Setter注入中修改已初始化的业务状态[^3]
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值