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详解

相关推荐
大傻^3 小时前
LangChain4j Spring Boot Starter:自动配置与声明式 Bean 管理
java·人工智能·spring boot·spring·langchain4j
沐硕3 小时前
《基于改进协同过滤与多目标优化的健康饮食推荐系统设计与实现》
java·python·算法·fastapi·多目标优化·饮食推荐·改进协同过滤
yhole3 小时前
springboot 修复 Spring Framework 特定条件下目录遍历漏洞(CVE-2024-38819)
spring boot·后端·spring
大傻^3 小时前
Spring AI 2.0 MCP 协议实战:Model Context Protocol SDK 与多服务器编排
服务器·人工智能·spring
BingoGo3 小时前
Laravel 13 正式发布 使用 Laravel AI 无缝平滑升级
后端·php
愣头不青3 小时前
560.和为k的子数组
java·数据结构
共享家95273 小时前
Java入门(String类)
java·开发语言
l软件定制开发工作室4 小时前
Spring开发系列教程(34)——打包Spring Boot应用
java·spring boot·后端·spring·springboot
0xDevNull4 小时前
Spring Boot 循环依赖解决方案完全指南
java·开发语言·spring
爱丽_4 小时前
GC 怎么判定“该回收谁”:GC Roots、可达性分析、四种引用与回收算法
java·jvm·算法