2023.11.7 Spring 依赖注入的三大方式

目录

前言

属性注入(@Autowired)

Setter 注入

构造方法注入

 @Resource

@Autowired 和 @Resource 的区别

@Autowired 和 @Resource 查找 Bean 对象的区别


前言

配置文件

​
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:content="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
    <!--base-package 表示要扫描的路径-->
    <content:component-scan base-package="com.java.demo"></content:component-scan>
</beans>

​

启动类

import com.java.demo.controller.StudentController;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class App {
    public static void main(String[] args) {
//        得到 Spring 上下文对象
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
//        获取 Bean 对象
        StudentController studentController  = context.getBean("studentController",StudentController.class);
        studentController.sayHi();
    }
}
  • 在启动类中,不能使用下述三种注入方式来实现对象的注入
  • 运行启动类时,需要执行 main 方法,然而 main 方法被 static 关键字所修饰的,即 main 方法为静态方法
  • 静态方法的加载顺序是高于 Spring 容器初始化的
  • 所以在该静态方法中,我们无法使用以下三种依赖注入的方式来获取 Bean 对象
  • 所以在下文 举例实现这三种依赖注入的方式 时,我们还是会通过 启动类 来获取 StudentController 的 Bean 对象
  • 但是会在 StudentController 类中使用以下三种依赖注入的方式,来实现在StudentController 类中成功注入 UserService 类的 Bean 对象

属性注入(@Autowired)

  • 日常开发中,属性注入是我们最常用的一种注入方式

实例

import com.java.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;

// 使用 @Controller 注解将当前类存储到 spring 容器中
@Controller
public class StudentController {

    @Autowired // 注入 UserService 的 Bean 对象
    private UserService userService;

    public void sayHi() {
        System.out.println("student controller say hi");
//        直接调用执行 userService 的成员方法
        userService.sayHi();
    }
}

运行启动类的结果:


重点理解

使用属性注入时:

  • 在 Spring 容器启动时,ApplicationContext 实例将被实例化和初始化,它负责加载配置文件(如 XML 文件)并创建和管理所有的 Bean 对象
  • 随后容器在创建 StudentController 类的 Bean 对象时,会自动检测 StudentController 类的依赖关系并注入相应的依赖对象
  •  UserService 的 Bean 对象,便在此时由 Spring 容器自动注入给了 StudentController 类
  • 即 StudentController 类中的 userService 对象被 Spring 容器直接赋值

优点

  • 实现简单、使用简单
  • 只需给变量上添加一个注解(@Autowired),即可获得注入的 Bean 对象

缺点

1、无法实现 final 修饰的变量注入

  • 在 Java 中被 final 关键字修饰的变量被称为常量
  • 常量的声明和初始化需要在同一时间完成,且只能被赋值一次
  • 方式一:直接赋值
  • 方式二:通过构造方法赋值

2、只适用于 IoC 容器(兼容问题)

  • 如果将属性注入的代码移植到其他非 IoC 的框架中,该代码就无效了,所以其兼容性有限

3、因为写法简单,所以违背单一职责原则的概率更大(存在风险)

  • 单一职责原则 是面向对象设计中的一个重要原则,它指出一个类应该有且只有一个引起它变化的原因
  • 此处强调的是违背单一职责原则的可能性,而不是一定会违背单一职责原则,这与程序员自己的代码强相关

Setter 注入

实例

import com.java.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;

// 使用 @Controller 注解将当前类存储到 spring 容器中
@Controller
public class StudentController {

    //    使用 Setter 注入
    private UserService userService;

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

    public void sayHi() {
        System.out.println("student controller say hi");
//        直接调用执行 userService 的成员方法
        userService.sayHi();
    }
}
  • 此处需要使用 @Autowired 添加到 Setter 方法上
  • 代表着 Spring 会针对该 Setter 方法 的参数进行相应的赋值

运行启动类执行结果:


重点理解

使用 Setter 注入时:

  • 在 Spring 容器启动时,ApplicationContext 实例将被实例化和初始化,它负责加载配置文件(如 XML 文件)并创建和管理所有的 Bean 对象
  • 随后容器在创建 StudentController 类的 Bean 对象时,会自动检测 StudentController 类的依赖关系并注入相应的依赖对象
  •  UserService 的 Bean 对象,便在此时由 Spring 容器自动注入给了 StudentController 类
  • 即 StudentController 类中的 setUserService 方法的参数直接被 Spring 容器给赋值
  • 然后通过方法中的 this.userService = userService 语句
  • 将被 Spring 容器赋值的 userService 对象,传递赋值给 StudentController 类中userService 变量
  • 此刻 StudentController 类便可以成功使用 UserService 类的 Bean 对象了

缺点

1、同样无法实现 final 修饰的变量注入

2、注入对象可被修改

import com.java.demo.controller.StudentController;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class App {
    public static void main(String[] args) {
//        得到 Spring 上下文对象
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
//        获取 Bean 对象
        StudentController studentController  = context.getBean("studentController",StudentController.class);
//        setUserService 可以被随便调用,然后改变原先已经被 Spring 容器注入好的 userService Bean 对象
//        此处直接将 studentController 对象中的 userService 变量的值改为 null
        studentController.setUserService(null);
//        执行到此处时便会发生报错
        studentController.sayHi();
    }
}

运行结果:

图解原因:

构造方法注入

  • 构造方法注入是 Spring 官方推荐的注入方式
  • 但是日常开发用的最多的还是属性注入

特点:

  • 如果当前类中只有一个构造方法的话, @Autowired 注解可以省略

实例

import com.java.demo.service.UserService;
import org.springframework.stereotype.Controller;

// 使用 @Controller 注解将当前类存储到 spring 容器中
@Controller
public class StudentController {

    //    使用 构造方法 注入不可变对象
    private final UserService userService;

//    @Autowired  因为当前类仅一个构造方法,所以此处的 @Autowired 可以省略
    public  StudentController (UserService userService) {
        this.userService = userService;
    }

    public void sayHi() {
        System.out.println("student controller say hi");
        userService.sayHi();
    }
}

运行结果:


优点

1、可以注入一个不可变对象(即用 final 关键字修饰的对象)

  • 被 final 关键字修饰的对象必须满足以下两个条件中的任意一个
  • final 修饰的对象,要么需直接赋值
  • final 修饰的对象,要么需通过构造方法赋值

2、注入对象后不会被修改

  • 构造方法随着类加载仅执行一次

3、构造方法注入可以保证对象完全被完全初始化

  • 当创建一个对象时,一定会调用该类的构造方法来初始化该对象
  • 所以通过构造方法注入时,将必然保证该对象已经是被 Spring 容器所注入了 Bean 对象的

4、相比属性注入,构造方法注入的兼容性更好

  • 构造方法注入可适用于任何环境,无论是 IoC 框架还是非 IoC 框架

 @Resource

  • @Resource 注解由 JDK 所提供

@Autowired 和 @Resource 的区别

  • 出身不同:@Autowired 来自于 Spring,而 @Resource 来自于 JDK
  • @Autowired 可用于 属性注入、Settter 注入、构造函数注入,而 @Resource 只能用于 属性注入、Setter 注入
  • 使用时设置的参数不同:相比于 @Autowired 来说,@Resource 支持更多的参数设置,例如 重命名

@Autowired 和 @Resource 查找 Bean 对象的区别

  • @Autowired 先会根据类型查找,之后再根据名称查找
  • 而 @Resource 会先根据名称来查,再根据类型查找

实例理解

  • 我们创建一个实体类
// 普通的用户实体类
public class User {
    public Integer uid;
    public String username;
    public String password;
    public Integer age;

    public Integer getUid() {
        return uid;
    }

    public void setUid(Integer uid) {
        this.uid = uid;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }
}
  • 再创建一个 UserBean 类,通过该类向 Spring 容器中注入 User 的 Bean 对象
import com.java.demo.entity.User;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;

@Component
public class UserBeans {
    @Bean
    public User getUserByName() {
        User user = new User();
        user.setUid(1);
        user.setUsername("张三");
        user.setPassword("123456");
        user.setAge(18);
        return user;
    }

    @Bean(name = {"user1","u1"})
    public User getUserById() {
        User user = new User();
        user.setUid(1);
        user.setUsername("李四");
        user.setPassword("123456");
        user.setAge(18);
        return user;
    }
}
  •  User 的 Bean 对象通过 @Autowired 注入到 StudentController 类中
import com.java.demo.entity.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;

// 使用 @Controller 注解将当前类存储到 spring 容器中
@Controller
public class StudentController {

    //    使用 属性 注入
    @Autowired
    private User user;

    public void sayHi() {
        System.out.println("student controller say hi");
        user.getUsername();
    }
}

运行结果:

报错分析:

  • 正因为我们在 UserBeans 类中向 Spring 容器注入了两个同为 User 类型的 Bean 对象
  • 所以当我们使用 @Autowired 注解来注入 Bean 对象的时候,它会先根据类型来查找

  • 那此时便在 Spring 容器中查找到了 两个同为 User 类型的 Bean 对象 
  • 从而 将会根据绿框名称来查找,若 Spring 容器中有 id 为 user 的 Bean 对象,便会直接注入

  • 但是我们存入 Spring 容器中的 User 类型的 Bean 对象,其一 id 为 getUserByName,其二 id 为 user1 或 u1
  • 所以 @Autowired 找不到 id 为 user 的 Bean 对象,从而发生报错

解决方法:

方案一:

  • 修改绿框名称,通过 id 来指定注入一个 Bean 对象 

方案二:

  • 使用 @Resource 注解,通过设置参数来查找

  • 当然本应将参数名称设置为 user1 来实现依赖注入的
  • 使用该方式,可以实现 参数名称 的重命名

方案三:

  • 组合使用 @Autowired 和 @Qualifier 注解

  • @Qualifier 注解起到筛选的作用,筛选出 id 为 user1 的 Bean 对象,并将其注入
  • 该方式所呈现的效果 与 方式二 相同
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

茂大师

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

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

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

打赏作者

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

抵扣说明:

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

余额充值