Spring Boot——@Autowired属性注入问题

🎈 @Autowired问题

当我们在使用@Autowired属性注入时,会发现idea提示Field injection is not recommended ,译为:不推荐使用属性注入
在这里插入图片描述   要想了解Spring和idea之所以不推荐使用@Autowired属性注入,首先就要先了解Spring常用的注入方式:简单类型注入、集合类型注入, 域属性自动注入, 自动注入的类别, 空值注入, 构造注入。

在 Spring 中,常见的依赖注入方式有以下三种:

  1. Constructor Injection(构造函数注入):通过类的构造函数来注入依赖项。

  2. Setter Injection(setter 方法注入):通过类中的 setter 方法来注入依赖项。

  3. Field Injection(属性注入):通过直接在类的属性上使用注解来注入依赖项。

这三种方式各有优缺点,例如:

  • Constructor Injection 在单元测试中更易于模拟依赖项,可以提高代码的可测试性和可维护性。但是,当类的依赖项较多时,构造函数可能会变得很长,使得类的代码难以阅读和理解。

  • Setter Injection 使得依赖项可以在运行时动态修改,使用起来比较灵活方便。但是,使用 Setter Injection 的类可能会存在不完整的状态,如果必需的依赖项没有被设置,可能会导致运行时错误。

  • Field Injection 可以减少类的代码,使其更加简洁易懂。但是,当依赖项过多时,类的属性列表可能会变得很长,降低代码的可读性和可维护性。此外,由于依赖关系是通过类的属性直接注入的,容易出现一些难以发现的错误。

综上所述,建议在实际应用中优先使用 Constructor Injection 或 Setter Injection 来注入依赖项,以提高代码的可测试性和可维护性。Field Injection 应该谨慎使用,只有当属性较少且必需的依赖项很明显时才使用。

🎈 Spring中的三种依赖注入方式

💧 Field Injection

Field Injection(属性注入)是 Spring 中一种依赖注入方式,它使用 @Autowired@Inject 注解直接在属性上进行注入。例如:

@Service
public class UserServiceImpl implements UserService {
 
    @Autowired
    private UserRepository userRepository;
 
    // ...
}

在该例子中,UserServiceImpl 类中的 userRepository 属性通过 @Autowired 注入了 UserRepository 类型的 bean。

虽然 Field Injection 是一种简单方便的依赖注入方式,但也存在一些问题:

  1. 不利于单元测试:Field Injection 的方式使得测试时修改或替换依赖对象可能更加困难。因为需要通过反射机制才能访问和修改被注入的属性。

  2. 缺乏明确的依赖顺序:在类中使用 Field Injection 时,无法明确知道哪些依赖必须先注入,在什么顺序下注入。这可能导致一些潜在的问题。

  3. 难以发现错误:由于依赖关系是通过属性来注入的,容易出现拼写错误、类型错误等问题。这会导致运行时错误非常难以发现和定位。

因此,建议在应用程序中使用 Constructor Injection 或 Setter Injection 来代替 Field Injection,以达到更好的可测试性、可维护性和代码清晰度。

💧 Constructor Injection

Constructor Injection是构造器注入,它通过类的构造函数来注入依赖。例如:

@Service
public class UserServiceImpl implements UserService {
 
    private final UserRepository userRepository;
 
    @Autowired
    public UserServiceImpl(UserRepository userRepository) {
        this.userRepository = userRepository;
    }
 
    // ...
}

在上面的示例中,UserServiceImpl 类中的依赖 userRepository 通过构造函数注入进来。

相比于 Field Injection,Constructor Injection 有以下优点:

  1. 明确的依赖关系:使用构造函数注入时,我们可以明确知道哪些依赖项是必须的,以及它们的顺序。这有助于消除代码中可能存在的歧义。

  2. 更好的可测试性:由于依赖被注入到类的构造函数中,因此在单元测试中可以更容易地模拟依赖项。

  3. 对不变性的支持:当类的依赖被注入后,它们通常是不变的。这使得类可以更好地支持不变性和线程安全性。

  4. 更好的代码清晰度:使用构造函数注入时,我们可以更清晰地看到类与其他组件之间的关系。这使得代码更易于阅读和理解。

在实践中,建议使用 Constructor Injection 作为首选方法来注入依赖项,以提高代码的可测试性和可维护性。

💧 Setter Injection

Setter Injection也会用到@Autowired注解,但使用方式与Field Injection有所不同,Field Injection是用在成员变量上,而Setter Injection的时候,是用在成员变量的Setter函数上。例如:

@Service
public class UserServiceImpl implements UserService {
 
    private UserRepository userRepository;
 
    @Autowired
    public void setUserRepository(UserRepository userRepository) {
        this.userRepository = userRepository;
    }
 
    // ...
}

在上面的示例中,UserServiceImpl 类中的依赖 userRepository 通过 setter 方法注入进来。

相比于 Field Injection,Setter Injection 的主要优势是可以避免直接访问类的属性,而是通过 setter 方法来设置依赖项,从而实现更好的封装性。同时,使用 Setter Injection 的类可以动态地修改依赖项,这使得它们更加灵活。

但是,Setter Injection 也存在一些缺点。例如,使用 Setter Injection 的类可能会存在不完整的状态,如果必需的依赖项没有被设置,可能会导致运行时错误。此外,Setter Injection 使得代码更加分散,使用起来可能比 Constructor Injection 要更加繁琐。

因此,在使用 Setter Injection 时需要注意以下几点:

  1. 避免在类的构造函数中直接使用依赖项,而应该通过 setter 方法来设置依赖项。

  2. 设计 setter 方法时要确保参数非空,以避免在运行时出现 NullPointerException 错误。

  3. 在使用 Setter Injection 的类中,必需的依赖项应该在对象创建后尽早设置好,以避免出现不完整的状态。

总之,Setter Injection 是 Spring 中一种重要的依赖注入方式,使用它可以让代码更加灵活、封装性更好。但是,在使用 Setter Injection 时需要注意避免一些常见的陷阱,以确保代码的可测试性和可维护性。

🎈 三种依赖注入的对比

这三种注入方式各有优劣,下面我将对它们进行一些简单的对比。

  1. Constructor Injection(构造函数注入):

Constructor Injection 是一种通过类的构造函数来注入依赖项的方式。通过构造函数注入时,可以保证必需的依赖项在对象创建后立即可用,并且在创建对象的过程中就可以将依赖项完全实例化。

优点:使用构造函数注入可以非常明确地表达一个类的依赖关系,使得代码依赖性更加明显和易于维护。此外,由于所有必须的依赖项都必须在构造函数中声明,因此这种方式可以提高代码的可测试性。

缺点:当类的依赖项较多时,构造函数可能会变得很长,使得类的代码难以阅读和理解。此外,使用构造函数注入时必须额外定义构造函数,造成代码冗长。

  1. Setter Injection(setter 方法注入):

Setter Injection 是一种通过类中的 setter 方法来注入依赖的方式。这种注入方式比 Constructor Injection 更加灵活,因为 Setter Injection 允许在运行时动态更改依赖项。

优点:使用 Setter Injection 的类可以动态地修改依赖项,从而实现更好的灵活性。此外,Setter Injection 可以避免直接访问类的属性,从而实现更好的封装性。

缺点:使用 Setter Injection 的类可能会存在不完整的状态,如果必需的依赖项没有被设置,可能会导致运行时错误。此外,Setter Injection 使得代码更加分散,使用起来可能比 Constructor Injection 更加繁琐。

  1. Field Injection(属性注入):

Field Injection 是一种通过直接在类的属性上使用注解来注入依赖项的方式。这种注入方式可以使类的代码更加简洁,但也可能引发一些问题。

优点:Field Injection 能够减少类的代码,使其更加简洁易懂。此外,使用 Field Injection 的类可以直接访问依赖项,从而使代码更加紧凑。

缺点:Field Injection 的主要问题是它容易出现一些难以发现的错误。由于依赖关系是通过类的属性直接注入的,使用 Field Injection 可能会导致一些不必要的依赖项。此外,通过属性直接注入依赖项可能会违反单一责任原则。

综合比较:

注入方式可靠性可维护性可测试性灵活性循环关系的检测性能影响
Field Injection不可靠很灵活不检测启动快
Constructor Injection可靠不灵活自动检测启动慢
Setter Injection不可靠很灵活不检测启动快

总之,在使用三种依赖注入方式时,需要结合具体情况进行选择。在大多数情况下,Constructor Injection 和 Setter Injection 是比较常用和推荐的方式。同时,需要注意避免一些常见的陷阱,以确保代码的可测试性和可维护性。

🎈 属性注入解决方法

属性注入的问题:

  1. 基于属性注入的方式,违反单一职责原则​ 因为现在的业务一般都会使用很多依赖, 但拥有太多的依赖通常意味着承担更多的责任,而这显然违背了单一职责原则.并且类和依 赖容器强耦合,不能在容器外使用。
  2. 基于属性注入的方式,容易导致Spring 初始化失败​ 初始化Spring 容器是,由于属性在被注入前就引用而导致npe(空指针),进而导致容器初始化失败。​ java 在初始化一个类的顺序为,静态变量和静态代码块 -> 实例变量或者初始化语句 -> 构造函数 然后才会执行Spring 注解 @Autowired 自动装配依赖,所以在执行这个类的构造方法时,依赖属性还未被注入。
  3. @Autowired 是ByType注入,当存在两个类型相同的对象时就会注入失败

💧 @Resource

@Resource 是 Java EE 提供的一种注入方式,可以实现对其他组件(如 Bean、EJB 等)进行自动注入。在 Spring 中,@Resource 注解可以用来注入依赖项。

@Resource 注解默认按名称进行装配,可以指定其 name 属性显式指定要装配的 Bean 的名称。如果没有指定 name 属性,则会使用属性名作为 Bean 名称进行装配。如果找不到与 name 或属性名相匹配的 Bean,则会抛出 NoSuchBeanDefinitionException 异常。

下面是一个使用 @Resource 注解进行注入的示例:

@Service
public class UserService {
    public void addUser() {
        // ...
    }
}

@RestController
public class UserController {
    @Resource
    private UserService userService;

    @GetMapping("/addUser")
    public String addUser() {
        userService.addUser();
        return "Success";
    }
}

在上述示例中,@Resource 注解会自动注入 UserService 类型的 Bean。

需要注意的是,@Resource 注解和 @Autowired 注解类似,但存在一些细微的区别。例如,@Autowired 注解默认按类型进行装配,而 @Resource 注解默认按名称进行装配。此外,@Autowired 注解可以通过 @Qualifier 注解指定要装配的 Bean 的名称,而 @Resource 注解则直接使用 name 属性指定要装配的 Bean 的名称。因此,在使用 @Resource 注解时需要格外注意其装配方式和使用方法。

总之,在 Spring 中可以使用 @Resource 注解进行依赖注入,它可以根据名称或类型进行装配,提高了代码的灵活性和可维护性。

💧 @RequiredArgsConstructor

在 Spring 中,可以使用 @RequiredArgsConstructor 进行属性注入。这种方式可以帮助我们避免手动编写构造函数或繁琐的 setter 方法,同时还可以增加代码的可读性和可维护性。

在使用 @RequiredArgsConstructor 进行属性注入时,需要满足以下条件:

  1. 类中必须有 final 修饰的属性;
  2. 需要使用 lombok 插件。

下面是一个使用 @RequiredArgsConstructor 进行属性注入的示例:

@Service
@RequiredArgsConstructor
public class UserService {
    private final UserRepository userRepository;

    public User getUserById(Long id) {
        return userRepository.findById(id).orElse(null);
    }
}

在上述示例中,@RequiredArgsConstructor 注解会为 UserService 自动生成一个有参构造函数,并自动注入 final 修饰的 userRepository 属性。

需要注意的是,在使用 @RequiredArgsConstructor 进行属性注入时,我们应该尽可能地将依赖项声明为 final 属性。这样可以确保依赖项一旦被初始化就无法修改,增加代码的安全性和稳定性。

总之,@RequiredArgsConstructor 能够帮助我们简化代码,提高代码的可读性和可维护性。在实际开发中,可以根据具体情况合理使用 @RequiredArgsConstructor 进行属性注入。

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Wen先森

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值