SpringIoC & DI (4)DI详解(三种注入方式)

SpringIoC & DI (4)DI详解(三种注入方式)

文章目录

  • [SpringIoC & DI (4)DI详解(三种注入方式)](#SpringIoC & DI (4)DI详解(三种注入方式))
  • [1. 什么是 DI?](#1. 什么是 DI?)
  • [2. DI 的三种方式](#2. DI 的三种方式)
    • [2.1 第一种:属性注入(@Autowired)](#2.1 第一种:属性注入(@Autowired))
      • [去掉 @Autowired:](#去掉 @Autowired:)
      • [去掉 @Service:](#去掉 @Service:)
      • [注意:一个 @Autowired 只能注入一个对象](#注意:一个 @Autowired 只能注入一个对象)
    • [2.2 第二种:构造方法注入](#2.2 第二种:构造方法注入)
      • [2.2.1 第一种情况:只有一个构造方法:](#2.2.1 第一种情况:只有一个构造方法:)
      • [2.2.2 第二种情况:加入无参的构造方法:(两个构造方法)(空指针异常)](#2.2.2 第二种情况:加入无参的构造方法:(两个构造方法)(空指针异常))
      • [2.2.3 第三种情况:加入另一个属性,再加入一个构造方法(三个构造方法)(空指针异常)](#2.2.3 第三种情况:加入另一个属性,再加入一个构造方法(三个构造方法)(空指针异常))
      • [2.2.4 第四种情况:不使用无参的构造方法,使用另外两个:(两个构造方法)(Bean构造失败异常)](#2.2.4 第四种情况:不使用无参的构造方法,使用另外两个:(两个构造方法)(Bean构造失败异常))
      • [2.2.5 原因分析(重点):](#2.2.5 原因分析(重点):)
      • [2.2.6 手动指定采用的构造方法:](#2.2.6 手动指定采用的构造方法:)
      • [2.2.7 构造方法注入总结:](#2.2.7 构造方法注入总结:)
    • [2.3 第三种:Setter 方法注入](#2.3 第三种:Setter 方法注入)
    • [2.4 总结:](#2.4 总结:)
  • [3. 三种注入方式优缺点分析(一道面试题,频率不高):](#3. 三种注入方式优缺点分析(一道面试题,频率不高):)
  • [4. 总结:](#4. 总结:)

观前提醒

这篇博客的代码,是比较简单的。

你自己可以创建类,使用注解,调用对象,就能够看见效果。

或者,你获取我的源码:
https://gitee.com/mrbgvhbhjv/java-ee-course/tree/master/后端代码/SpringIoC_demo_20251025

1. 什么是 DI?

DI: Dependency Injection,叫做 依赖注入。

依赖注入是一个过程,是指IoC容器在创建Bean时,去提供运行时所依赖的资源,而资源指的就是对象。

传统开发模式中需要自己通过 new 创建对象, 现在不需要再进行创建, 把创建对象的任务交给容器, 程序中只需要依赖注入 (Dependency Injection,DI)就可以了。

什么叫做依赖?

依赖,就是完成这项工作的必须品,是完成这个工作不可或缺的东西。

IoC ,是存储 Bean 的方式。
DI ,是获取 Bean 的方式。

2. DI 的三种方式

DI(依赖注入),有三种方式:

  1. 属性注入(Field Injection)
  2. 构造方法注入(Constructor Injection)
  3. Setter 注入(Setter Injection)

2.1 第一种:属性注入(@Autowired)

@Autowired的作用:从 Spring 容器当中,根据类型获取对象,并赋值。

代码:

java 复制代码
package org.example.springioc_demo_20251024.controller;

import org.example.springioc_demo_20251024.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;

@Controller
public class UserController {

//    属性注入
    @Autowired
    private UserService userService;
    public void print() {
        System.out.println("do Controller");
        userService.print();
    }
}
-----------------------------------------------------------
    package org.example.springioc_demo_20251024.service;

import org.springframework.stereotype.Service;

@Service
public class UserService {
    public void print(){
        System.out.println("do Service");
    }
}

-----------------------------------------------------------
    package org.example.springioc_demo_20251024;

import org.example.springioc_demo_20251024.controller.HelloController;
import org.example.springioc_demo_20251024.controller.UserController;
import org.example.springioc_demo_20251024.model.Student;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;

@SpringBootApplication
public class SpringIoCDemo20251024Application {

    public static void main(String[] args) {
        ApplicationContext context = SpringApplication.run(SpringIoCDemo20251024Application.class, args);

        UserController bean1 = context.getBean(UserController.class);
        bean1.print();

    }
}

如果注入生效,控制器,会将 userService 这个对象的 print()方法 一起执行。

效果:

去掉 @Autowired:

去掉 @Service:

想要从 IoC容器 中获取 UserService 对象,首先容器中得有这个对象,如果我们去掉 @Service 注解,没有将 UserService类 的对象,交给 Spring 进行管理

注意:一个 @Autowired 只能注入一个对象


2.2 第二种:构造方法注入

首先,我们的 UserServicer类 交给了Spring进行管理,Spring就会构造这个类的 对象。

构造类的对象,就需要使用到 构造方法。

2.2.1 第一种情况:只有一个构造方法:

代码:

java 复制代码
import org.example.springioc_demo_20251024.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;

@Controller
public class UserController {

    private UserService userService;
//    构造注入:
    public UserController(UserService userService) {
        this.userService = userService;
    }

    public void print() {
        System.out.println("do Controller");
        userService.print();
    }
}

运行结果:

2.2.2 第二种情况:加入无参的构造方法:(两个构造方法)(空指针异常)

当我们手动添加了构造方法,JDK就不会给我们提供默认的构造方法了,我们手动加上:

代码:

java 复制代码
import org.example.springioc_demo_20251024.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;

@Controller
public class UserController {

    private UserService userService;

    public UserController() {
    }

    //    构造注入:
    public UserController(UserService userService) {
        this.userService = userService;
    }

    public void print() {
        System.out.println("do Controller");
        userService.print();
    }
}

2.2.3 第三种情况:加入另一个属性,再加入一个构造方法(三个构造方法)(空指针异常)

代码:

java 复制代码
import org.example.springioc_demo_20251024.component.StudentComponent;
import org.example.springioc_demo_20251024.component.UserComponent;
import org.example.springioc_demo_20251024.model.Student;
import org.example.springioc_demo_20251024.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;

@Controller
public class UserController {

    private UserService userService;
    private UserComponent userComponent;

    public UserController() {
    }

    //    构造注入:
    public UserController(UserService userService) {
        this.userService = userService;
    }

    public UserController(UserService userService, UserComponent userComponent) {
        this.userService = userService;
        this.userComponent = userComponent;
    }

    public void print() {
        System.out.println("do Controller");
        userService.print();
        userComponent.print();
    }
}

运行结果:

2.2.4 第四种情况:不使用无参的构造方法,使用另外两个:(两个构造方法)(Bean构造失败异常)

代码:

java 复制代码
import org.example.springioc_demo_20251024.component.StudentComponent;
import org.example.springioc_demo_20251024.component.UserComponent;
import org.example.springioc_demo_20251024.model.Student;
import org.example.springioc_demo_20251024.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;

@Controller
public class UserController {

    private UserService userService;
    private UserComponent userComponent;

//    public UserController() {
//    }

    //    构造注入:
    public UserController(UserService userService) {
        this.userService = userService;
    }

    public UserController(UserService userService, UserComponent userComponent) {
        this.userService = userService;
        this.userComponent = userComponent;
    }

    public void print() {
        System.out.println("do Controller");
        userService.print();
        userComponent.print();
    }
}

运行结果:

2.2.5 原因分析(重点):

直接说结论:

  1. Spring管理对象,构造 Bean(对象),是依靠反射
  2. Spring管理对象,构造 Bean(对象)会采用默认的构造方法:无参的构造方法

针对上述情况分析:
第一种情况只有一个构造方法 ,Spring没得选,就使用那个有参的。
第二种情况 :有 两 个构造方法 (包含无参构造方法)的时候,Spring 就会采用 无参的构造方法userService 没有被赋值,为 NULL
第三种情况 :有 三 个构造方法 (包含无参构造方法)的时候,Spring 就会采用 无参的构造方法userService 没有被赋值,为 NULL
第四种情况 :有多个构造方法,但是没有 无参的构造方法 的时候,Spring 不知道使用哪一个,就会构造 Bean 失败!!!

我们构造方法注入,就是当有多个构造方法,但是没有 无参的构造方法 的时候,添加@Autowired,手动设置,Spring构造 UserServicer类 对象的时候,使用哪种构造方法。

2.2.6 手动指定采用的构造方法:

我们需要在我们想要 Spring构建对象 采用的构造方法前,添加 @Autowired注解,就能够指定 Spring构建对象 采用的构造方法:

代码:

java 复制代码
import org.example.springioc_demo_20251024.component.UserComponent;
import org.example.springioc_demo_20251024.model.Student;
import org.example.springioc_demo_20251024.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;

@Controller
public class UserController {

    private UserService userService;
    private UserComponent userComponent;

    public UserController() {
    }

    //    构造注入:
    public UserController(UserService userService) {
        this.userService = userService;
    }

    @Autowired
    // 指定使用这个构造方法
    public UserController(UserService userService, UserComponent userComponent) {
        this.userService = userService;
        this.userComponent = userComponent;
    }

    public void print() {
        System.out.println("do Controller");
        userService.print();
        userComponent.print();
    }
}

运行结果:

2.2.7 构造方法注入总结:

  1. 如果没有提供构造方法,Spring 采用无参的构造方法,构建对象

  2. 如果只有一个构造方法(不是无参的),@Autowired可以省略,Spring 只能采用这个

  3. 如果存在多个构造方法:

    3.1 包含无参 的构造方法:使用 无参 的构造方法,对象不会赋值(可能引发 空指针 异常)

    3.2 不包含无参 的构造方法:Bean(对象)构造失败,丢出 BeanCreationException异常

  4. 通过 @Autowired 指定 Spring构建对象时,默认的构造方法

2.3 第三种:Setter 方法注入

这种方法比较简单,和属性注入差不多,想注入哪个对象,就在哪个对象的set方法上,添加@Autowired即可。

代码:

java 复制代码
import org.example.springioc_demo_20251025.component.UserComponent;
import org.example.springioc_demo_20251025.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;

@Controller
public class UserController {

//    set方法注入
    private UserService userService;
    private UserComponent userComponent;

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

    @Autowired
    public void setUserComponent(UserComponent userComponent) {
        this.userComponent = userComponent;
    }

    public void print(){
        System.out.println("UserController");
//        System.out.println(userService.getNum());
        userService.print();
        userComponent.print();
    }
}

运行结果:

2.4 总结:

这三种注入方式,程序员更推荐使用第一种,方便简单。

这三种注入方式,当然也有区别。

3. 三种注入方式优缺点分析(一道面试题,频率不高):

  • 属性注入
    • 优点: 简洁,使用方便;
    • 缺点:
      • 只能用于 IoC 容器,如果是非 IoC 容器不可用,并且只有在使用的时候才会出现 NPE(空指针异常)
      • 不能注入⼀个Final修饰的属性
  • 构造函数注入(Spring 4.X推荐)
    • 优点:
      • 可以注入final修饰的属性
      • 注入的对象不会被修改
      • 依赖对象在使用前一定会被完全初始化,因为依赖是在类的构造方法中执行的,而构造方法是在类加载阶段就会执行的方法.
      • 通用性好, 构造方法是JDK支持的, 所以更换任何框架,他都是适用的
    • 缺点:
      • 注入多个对象时, 代码会比较繁琐
  • Setter注入(Spring 3.X推荐)
    • 优点: 方便在类实例之后, 重新对该对象进行配置或者注入
    • 缺点:
      • 不能注入⼀个Final修饰的属性
      • 注入对象可能会被改变, 因为setter方法可能会被多次调用, 就有被修改的风险.

4. 总结:

DI: Dependency Injection,叫做 依赖注入。

DI(依赖注入),有三种方式:

  1. 属性注入(Field Injection) (更推荐程序员使用这个)
  2. 构造方法注入(Constructor Injection)
  3. Setter 注入(Setter Injection)

一道面试题:三种注入方式优缺点分析。

最后,如果这篇博客能帮到你的,请你点点赞,有写错了,写的不好的,欢迎评论指出,谢谢!

下一篇博客:SpringIoC & DI(5):DI详解

相关推荐
落羽的落羽2 小时前
【Linux系统】从零实现一个简易的shell!
android·java·linux·服务器·c++·人工智能·机器学习
独断万古他化2 小时前
【SSM开发实战:博客系统】(二)JWT 登录流程、拦截器实现和用户信息接口落地
spring boot·spring·mybatis·博客系统·项目
1104.北光c°2 小时前
【黑马点评项目笔记 | 优惠券秒杀篇】构建高并发秒杀系统
java·开发语言·数据库·redis·笔记·spring·nosql
ruleslol2 小时前
普通流(Stream<T>)和原始类型特化流(IntStream, LongStream, DoubleStream)的区别
java
隐退山林2 小时前
JavaEE初阶:文件操作和IO
java·java-ee
2501_907136822 小时前
PDF增效工具 Quite imposing plus6
java·开发语言
Jaxson Lin2 小时前
Java编程进阶:智能仿真无人机项目3.0
java·笔记·无人机
是阿楷啊2 小时前
Java求职面试实录:互联网大厂场景技术点解析
java·redis·websocket·spring·互联网·大厂面试·支付系统
_周游2 小时前
Java8 API文档搜索引擎_3.搜索模块(实现细节)
java·搜索引擎·intellij-idea