Spring依赖注入、对象装配

简介: 在这篇博客中,我们学习到了spring依赖注入的三种常见的方式,并且了解了它们的优缺点,还了解了依赖注入的两个重要的关键字:@Autowired、@Resource,并了解了两者的区别。还知道了如何解决同一类型Bean注入的报错问题和了解了软件设计中的单一设计原则。

依赖注入与对象装配

依赖注入(Dependency Injection) 是Spring框架的一大特色。咱们可以把它想象成是一种“自动传递东西”的方式,就像你坐电梯不需要自己操作按钮,电梯会帮你到达目的地一样。在程序中,当一个类需要用到另一个类的功能时,不需要自己去创建,而是由Spring自动帮你注入所需的对象。这样做有点像你在做披萨,需要的配料自己不用操心,厨师会给你加好。

对象装配(Object Assembly) 则是Spring中的一个步骤,就像你把积木拼在一起一样。它是通过配置文件或者注解来告诉Spring如何把不同的类组合在一起,形成一个完整的程序。就像你组装一辆汽车,需要把引擎、轮子、座位等组件组合在一起,Spring帮你把各个类组装成一个强大的系统。

首先,我们需要创建一些类,就像你在搭积木,每个类都有自己的功能。然后,在Spring的配置中,你会告诉Spring这些类之间的关系,以及谁需要谁的功能。Spring会在需要的时候,自动把这些类创建并组装起来,就像你玩乐高积木,把各种小块搭建成一个酷炫的模型。

举个例子吧!假设你要开发一个图书管理系统。你有一个 Book 类表示图书,还有一个 Library 类表示图书馆。在Spring中,你可以通过依赖注入,让图书馆知道它需要用到图书,然后Spring会自动给它注入图书的实例。就像你在图书馆借书,不需要自己找,图书管理员会把书递给你一样。

配置这些关系可以通过XML配置文件,也可以使用注解。你可以告诉Spring哪些类需要注入,哪些属性需要赋值,就像你在指挥乐团,把每个乐器的音符安排得和谐动听。

Spring的依赖注入和对象装配让你的代码更加整洁、灵活,让你可以更专注于解决实际问题,而不用纠结于类与类之间的繁琐连接。

依赖注入的常见方式

Spring提供了多种方法来帮助你将不同的类连接在一起,实现依赖注入。以下是一些常见的依赖注入方式:

属性注入(Property Injection)

属性注入是我们最熟悉,也是日常开发中使用最多的一种注入方式。通过类的属性来注入依赖。这通常需要提供一个设置方法(setter)来设置依赖。就像你在乐高积木上插入一个小块一样,将依赖的类通过属性赋值进来。

@Controller
public class UserController {
   
   
    // 从 spring 容器中获取 UserService 对象
    /**
     * 1.属性注入@Autowired  自动装配
     */
    @Autowired
    private UserService userService;

    public void sayHello() {
   
   
        System.out.println("Hello ");
        userService.doService();
    }
}

这里的UserService对象就像属性一样直接能被UserController调用,所有叫属性注入。

属性注入的优缺点

优点

属性注入最大的优点就是实现简单、使用简单,只需要给变量上添加一个注解(@Autowired),直接获得注入的对象了,所以它的优点就是使用简单。

缺点

  1. 功能缺陷: 不能注入一个final修饰的属性,在JavaSE中final修饰的变量要么使用时直接赋值,要么构造方法赋值,在这里两者都不满足,所以报错。
    image.png

  2. 通用性问题: 只适用于IoC容器。,如果将属性注入的代码移植到其他非 IoC 的框架中,那么代码就无效了,所以属性注入的通用性不是很好。

  3. 设计原则问题: 使用属性注入的方式,因为使用起来很简单,所以开发者很容易在一个类中同时注入多个对象,而这些对象的注入是否有必要?是否符合程序设计中的单一职责原则?就变成了一个问题。但可以肯定的是,注入实现越简单,那么滥用它的概率也越大,所以出现违背单一职责原则的概率也越大
    注意:这里强调的是违背设计原则(单一职责)的可能性,而不是一定会违背设计原则,二者有着本质的区别。

Setter 注入(Setter Injection)

Setter 注入 是通过设置方法(setter)来实现依赖注入的方式。这种方法允许你在类中定义一个或多个设置方法,用于注入依赖对象。

/**
 * 2.setter注入
 */
private UserService userService;

@Autowired
public void setUserService(UserService userService) {
   
   
    this.userService = userService;
}

public void sayHello() {
   
   
    System.out.println("Hello ");
    userService.doService();
}

Setter注入优缺点

优点

完全符合单一职责的设计原则,每一个 Setter 只针对一个对象

缺点

  1. 不能注入不可变对象
    使用 Setter 注入依然不能注入不可变对象,比如以下注入会报错:
    image.png

  2. 注入对象可被修改
    Setter 注入提供了 setXXX 的方法,意味着你可以在任何时候、在任何地方,通过调用 setXXX 的方法来改变注入对象,所以 Setter 注入的问题是,被注入的对象可能随时被修改。

    构造函数注入(Constructor Injection)

    通过构造函数,你可以在创建一个类的实例时传入它所依赖的其他类的实例。就像你在组装一个模型,每个积木块都是构造函数的一个参数,将它们组合在一起就形成了完整的模型。

/**
 * 3.构造器注入
 */
private UserService userService;

@Autowired
public UserController(UserService userService) {
   
   
    this.userService = userService;
}

public void sayHello() {
   
   
    System.out.println("Hello ");
    userService.doService();
}
  • 如果当前的类中只有一个构造方法,那么 @Autowired 可以省略

    构造函数注入优缺点

    优点

构造方法注入相比于前两种注入方法,它可以注入不可变对象,并且它只会执行一次,也不存在像 Setter 注入那样,被注入的对象随时被修改的情况,它的优点有以下 4 个:

  1. 注入不可变对象
    使用构造方法注入可以注入不可变对象,并不会报错
    image.png

  2. 注入对象不会被修改
    构造方法注入不会像 Setter 注入那样,构造方法在对象创建时只会执行一次,因此它不存在注入对象被随时(调用)修改的情况。

  3. 完全初始化
    因为依赖对象是在构造方法中执行的,而构造方法是在对象创建之初执行的,因此被注入的对象在使用之前,会被完全初始化,这也是构造方法注入的优点之一。
  4. 通用性更好
    构造方法和属性注入不同,构造方法注入可适用于任何环境,无论是 IoC 框架还是非 IoC 框架,构造方法注入的代码都是通用的,所以它的通用性更好。

缺点

构造函数可以有多个参数,当有多个参数时,不符合单一设计原则。

依赖注入推荐用法

Spring 4.2 之前推荐的注入用法是 Setter,Spring 4.2 之后推荐使用构造方法注入

@Resource:另一种注入关键字

在进⾏类注⼊时,除了可以使⽤ @Autowired 关键字之外,我们还可以使⽤ @Resource 进⾏注⼊。

  • 实现属性注入
  • 实现setter注入
  • 不能用于构造函数注入

与@Autowired的区别

  • @Autowired 来⾃于 Spring,⽽ @Resource 来⾃于 JDK 的注解
  • 相⽐于 @Autowired 来说,@Resource ⽀持更多的参数设置,例如 name 设置,根据名称获取 Bean

同⼀类型多个 @Bean 报错

当出现以下多个 Bean,返回同⼀对象类型时程序会报错,如下:

@Component
public class Users {
   
   

    @Bean
    public User user1() {
   
   
        User user = new User();
        user.setName("fyd");
        user.setAge(18);
        return user;
    }

    @Bean
    public User user2() {
   
   
        User user = new User();
        user.setName("fyd2");
        user.setAge(19);
        return user;
    }
}

获取User对象,如下:

@Controller
public class UserController {
   
   

    @Resource
    private User user;

    public User getUser() {
   
   
        return user;
    }
}

程序报错如下:
没有 "com.fyd.User "类型的符合条件的 Bean:预期只有一个匹配的 Bean,但发现了 2 个:user1,user2
image.png

这里我们有两个一样类型的Bean对象,所以我们直接注入程序分不清是要注入哪一个。

使⽤ @Resource(name="user1") 解决

image.png

使用 @Qualifier 注解解决

image.png

什么是单一设计原则

当谈论软件设计时,"单一设计原则"(Single Responsibility Principle,简称 SRP)是面向对象编程中的一个重要原则之一。它强调一个类应该只有一个引起它变化的原因,也就是说,一个类应该只负责一项职责。

这个原则的核心思想是,将一个类的功能限制在一个明确的范围内,使得该类的修改只会因为这个范围内的需求变化,而不会受到其他无关功能的影响。这有助于代码的可维护性、可读性和可扩展性,因为当一个类只关注一项职责时,它的代码更加清晰,容易理解和修改。

举个例子来解释单一设计原则:假设你正在开发一个图书馆管理系统,你可能会有一个 Book 类负责表示图书的信息,另一个 Library 类负责管理图书的借阅和归还。按照单一设计原则,Book 类只需要关注图书本身的属性和方法,而 Library 类只需要关注图书的管理,这样两个类的职责得到了明确的划分。

然而,当一个类承担了过多的职责时,代码可能变得混乱,难以维护。如果在上述例子中,你让 Book 类既负责图书信息又负责图书管理,那么当图书信息或图书管理的需求变化时,就可能需要同时修改 Book 类的多个部分,增加了代码变更的风险和复杂性。

单一设计原则的目标是使每个类都变得更加聚焦和专注,减少类之间的耦合,提高代码的可维护性和灵活性。通过将不同的职责分开,你可以更容易地修改、测试和扩展每个类,从而更好地满足软件的需求。

总结

在这篇博客中,我们学习到了spring依赖注入的三种常见的方式,并且了解了它们的优缺点,还了解了依赖注入的两个重要的关键字:@Autowired、@Resource,并了解了两者的区别。还知道了如何解决同一类型Bean注入的报错问题和了解了软件设计中的单一设计原则。

相关文章
|
2天前
|
设计模式 XML Java
【23种设计模式·全精解析 | 自定义Spring框架篇】Spring核心源码分析+自定义Spring的IOC功能,依赖注入功能
本文详细介绍了Spring框架的核心功能,并通过手写自定义Spring框架的方式,深入理解了Spring的IOC(控制反转)和DI(依赖注入)功能,并且学会实际运用设计模式到真实开发中。
【23种设计模式·全精解析 | 自定义Spring框架篇】Spring核心源码分析+自定义Spring的IOC功能,依赖注入功能
|
4月前
|
XML Java 测试技术
Spring5入门到实战------17、Spring5新功能 --Nullable注解和函数式注册对象。整合JUnit5单元测试框架
这篇文章介绍了Spring5框架的三个新特性:支持@Nullable注解以明确方法返回、参数和属性值可以为空;引入函数式风格的GenericApplicationContext进行对象注册和管理;以及如何整合JUnit5进行单元测试,同时讨论了JUnit4与JUnit5的整合方法,并提出了关于配置文件加载的疑问。
Spring5入门到实战------17、Spring5新功能 --Nullable注解和函数式注册对象。整合JUnit5单元测试框架
|
18天前
|
Java 数据库 数据安全/隐私保护
轻松掌握Spring依赖注入:打造你的登录验证系统
本文以轻松活泼的风格,带领读者走进Spring框架中的依赖注入和登录验证的世界。通过详细的步骤和代码示例,我们从DAO层的创建到Service层的实现,再到Spring配置文件的编写,最后通过测试类验证功能,一步步构建了一个简单的登录验证系统。文章不仅提供了实用的技术指导,还以口语化和生动的语言,让学习变得不再枯燥。
34 2
|
1天前
|
存储 Java Spring
【Spring】获取Bean对象需要哪些注解
@Conntroller,@Service,@Repository,@Component,@Configuration,关于Bean对象的五个常用注解
|
1天前
|
存储 Java 应用服务中间件
【Spring】IoC和DI,控制反转,Bean对象的获取方式
IoC,DI,控制反转容器,Bean的基本常识,类注解@Controller,获取Bean对象的常用三种方式
|
1月前
|
消息中间件 Java 数据库
解密Spring Boot:深入理解条件装配与条件注解
Spring Boot中的条件装配与条件注解提供了强大的工具,使得应用程序可以根据不同的条件动态装配Bean,从而实现灵活的配置和管理。通过合理使用这些条件注解,开发者可以根据实际需求动态调整应用的行为,提升代码的可维护性和可扩展性。希望本文能够帮助你深入理解Spring Boot中的条件装配与条件注解,在实际开发中更好地应用这些功能。
36 2
|
14天前
|
XML 安全 Java
Spring Boot中使用MapStruct进行对象映射
本文介绍如何在Spring Boot项目中使用MapStruct进行对象映射,探讨其性能高效、类型安全及易于集成等优势,并详细说明添加MapStruct依赖的步骤。
|
2月前
|
Java Spring
获取spring工厂中bean对象的两种方式
获取spring工厂中bean对象的两种方式
53 1
|
2月前
|
前端开发 Java Spring
【Spring】“请求“ 之传递单个参数、传递多个参数和传递对象
【Spring】“请求“ 之传递单个参数、传递多个参数和传递对象
143 2
|
2月前
|
存储 Java 程序员
SpringIOC和DI的代码实现,Spring如何存取对象?@Controller、@Service、@Repository、@Component、@Configuration、@Bean DI详解
本文详细讲解了Spring框架中IOC容器如何存储和取出Bean对象,包括五大类注解(@Controller、@Service、@Repository、@Component、@Configuration)和方法注解@Bean的用法,以及DI(依赖注入)的三种注入方式:属性注入、构造方法注入和Setter注入,并分析了它们的优缺点。
42 0
SpringIOC和DI的代码实现,Spring如何存取对象?@Controller、@Service、@Repository、@Component、@Configuration、@Bean DI详解