Spring依赖注入、对象装配

文章目录

依赖注入与对象装配

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

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

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

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

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

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

依赖注入的常见方式

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

属性注入(Property Injection)

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

java 复制代码
@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修饰的变量要么使用时直接赋值,要么构造方法赋值,在这里两者都不满足,所以报错。
  2. 通用性问题: 只适用于IoC容器。,如果将属性注入的代码移植到其他非 IoC 的框架中,那么代码就无效了,所以属性注入的通用性不是很好。
  3. 设计原则问题: 使用属性注入的方式,因为使用起来很简单,所以开发者很容易在一个类中同时注入多个对象,而这些对象的注入是否有必要?是否符合程序设计中的单一职责原则?就变成了一个问题。但可以肯定的是,注入实现越简单,那么滥用它的概率也越大,所以出现违背单一职责原则的概率也越大
    注意:这里强调的是违背设计原则(单一职责)的可能性,而不是一定会违背设计原则,二者有着本质的区别。

Setter 注入(Setter Injection)

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

java 复制代码
/**
 * 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 注入依然不能注入不可变对象,比如以下注入会报错:
  2. 注入对象可被修改
    Setter 注入提供了 setXXX 的方法,意味着你可以在任何时候、在任何地方,通过调用 setXXX 的方法来改变注入对象,所以 Setter 注入的问题是,被注入的对象可能随时被修改。

构造函数注入(Constructor Injection)

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

java 复制代码
/**
 * 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. 注入不可变对象
    使用构造方法注入可以注入不可变对象,并不会报错
  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,返回同⼀对象类型时程序会报错,如下:

java 复制代码
@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对象,如下:

java 复制代码
@Controller
public class UserController {

    @Resource
    private User user;

    public User getUser() {
        return user;
    }
}

程序报错如下:

没有 "com.fyd.User "类型的符合条件的 Bean:预期只有一个匹配的 Bean,但发现了 2 个:user1,user2

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

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

使用 @Qualifier 注解解决

什么是单一设计原则

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

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

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

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

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

总结

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

相关推荐
救救孩子把10 分钟前
深入理解 Java 对象的内存布局
java
落落落sss13 分钟前
MybatisPlus
android·java·开发语言·spring·tomcat·rabbitmq·mybatis
万物皆字节18 分钟前
maven指定模块快速打包idea插件Quick Maven Package
java
夜雨翦春韭25 分钟前
【代码随想录Day30】贪心算法Part04
java·数据结构·算法·leetcode·贪心算法
我行我素,向往自由32 分钟前
速成java记录(上)
java·速成
一直学习永不止步38 分钟前
LeetCode题练习与总结:H 指数--274
java·数据结构·算法·leetcode·数组·排序·计数排序
邵泽明38 分钟前
面试知识储备-多线程
java·面试·职场和发展
程序员是干活的1 小时前
私家车开车回家过节会发生什么事情
java·开发语言·软件构建·1024程序员节
煸橙干儿~~1 小时前
分析JS Crash(进程崩溃)
java·前端·javascript
2401_854391081 小时前
Spring Boot大学生就业招聘系统的开发与部署
java·spring boot·后端