Spring依赖注入的魔法:深入DI的实现原理【beans 五】

欢迎来到我的博客,代码的世界里,每一行都是一个故事


在这里插入图片描述

Spring依赖注入的魔法:深入DI的实现原理【beans 五】

    • 前言
    • DI的基本概念
      • 基本概念:
      • 为什么使用依赖注入:
    • 构造器注入
      • 构造器注入的基本概念:
      • 示例:
      • 多个构造函数的处理:
    • Setter方法注入
      • Setter方法注入的基本概念:
      • 示例:
      • 可选性的Setter方法:
    • 接口注入
      • 1. 定义接口:
      • 2. 实现接口:
      • 3. 使用注入后的依赖:
    • 自动装配
      • 1. 根据类型进行自动装配:
      • 2. 根据名称进行自动装配:
      • 3. 字段和方法参数的自动装配:
    • Qualifier注解
      • 示例:
    • Primary注解
      • 示例:
    • 使用java配置进行DI
      • 1. 创建一个接口和两个实现类:
      • 2. 创建一个Java配置类:
      • 3. 使用配置类进行依赖注入:

前言

在软件开发的舞台上,依赖注入是一个强大的设计模式,而Spring框架以其优雅的实现而脱颖而出。你可能已经使用了DI,但你是否真正了解它的实现原理呢?在这篇文章中,我们将打开DI的黑盒,揭开Spring DI的神秘面纱,让你更深刻地理解这一关键的框架特性。

DI的基本概念

依赖注入(Dependency Injection,简称DI)是一种软件设计模式,它用于解耦组件之间的依赖关系。在依赖注入中,组件不再负责自己依赖的对象的创建和管理,而是由外部容器(通常是一个框架或容器)负责注入依赖的对象。这种注入通常通过构造函数、方法或属性进行。

基本概念:

  1. 依赖(Dependency): 表示一个对象需要另一个对象来完成特定的功能。例如,一个类可能依赖于一个数据库连接、一个服务类或其他组件。

  2. 注入(Injection): 表示将依赖关系注入到类中。这可以通过构造函数、方法参数或属性来实现。

为什么使用依赖注入:

  1. 解耦: 依赖注入有助于降低组件之间的耦合度。组件不再直接创建或管理它们的依赖关系,而是由外部容器负责。这样,组件之间的关系更加灵活,更容易修改和维护。

  2. 可测试性: 依赖注入使得组件更容易进行单元测试。因为依赖关系被注入,测试时可以使用模拟对象或桩对象替代真实的依赖,从而更容易进行单元测试。

  3. 可维护性: 通过将依赖关系的创建和管理移到外部容器中,代码变得更加清晰、简洁,并且更容易理解和维护。每个组件只需要关注自己的功能,而不必关心如何创建和管理依赖关系。

  4. 灵活性: 依赖注入提供了更大的灵活性,可以轻松更改组件之间的关系,而无需修改组件本身的代码。这使得系统更容易适应变化和演进。

  5. 可重用性: 通过将依赖关系解耦,依赖注入可以促使更多的组件变得可重用。一个组件可以在不同的上下文中使用,而不必担心它的依赖关系。

总体而言,依赖注入是一种有助于提高代码质量、可维护性和可测试性的设计模式,特别是在大型项目和团队中。它有助于创建更灵活、可扩展且易于维护的应用程序。

构造器注入

构造器注入是一种依赖注入的方式,通过构造函数来注入一个类的依赖。在Spring中,构造器注入是一种推荐的注入方式,因为它能够确保依赖在对象创建时就被满足,从而提高对象的不可变性和一致性。以下是构造器注入的基本概念和多个构造函数的处理方法。

构造器注入的基本概念:

  1. 构造函数(Constructor): 一个类的构造函数是用于创建对象的方法。在构造函数中,可以接受依赖关系所需的参数。

  2. 构造器注入(Constructor Injection): 将依赖关系通过构造函数的参数进行注入。这意味着对象在被创建时,其依赖关系必须通过构造函数提供。

示例:

public class MyService {private final MyDependency myDependency;// 构造函数注入public MyService(MyDependency myDependency) {this.myDependency = myDependency;}// 其他业务逻辑
}

在上述例子中,MyService通过构造函数接受一个MyDependency的实例,从而实现了构造器注入。

多个构造函数的处理:

如果一个类有多个构造函数,Spring会尽量通过匹配参数类型和数量的方式选择合适的构造函数。但是,在有多个构造函数的情况下,可能会出现歧义,需要通过@Autowired@Qualifier注解来明确指定使用哪个构造函数。

public class MyService {private final MyDependency myDependency;// 主要的构造函数@Autowiredpublic MyService(MyDependency myDependency) {this.myDependency = myDependency;}// 辅助的构造函数@Autowiredpublic MyService(MyDependency myDependency, OtherDependency otherDependency) {this.myDependency = myDependency;// 处理其他依赖}// 其他业务逻辑
}

在上述例子中,通过@Autowired注解标注了两个构造函数,Spring会选择匹配的构造函数来注入依赖。如果需要明确指定使用哪个构造函数,可以使用@Qualifier注解。

public class MyService {private final MyDependency myDependency;// 主要的构造函数@Autowiredpublic MyService(@Qualifier("primaryMyDependency") MyDependency myDependency) {this.myDependency = myDependency;}// 辅助的构造函数@Autowiredpublic MyService(@Qualifier("secondaryMyDependency") MyDependency myDependency) {this.myDependency = myDependency;}// 其他业务逻辑
}

在这个例子中,@Qualifier注解用于指定使用哪个具体的MyDependency的实例。

总的来说,构造器注入是一种简单、直观、推荐的依赖注入方式,通过构造函数接受依赖关系,使得对象的创建和初始化更加清晰和可控。在有多个构造函数的情况下,通过@Autowired@Qualifier来进行注解可以更好地处理依赖关系。

Setter方法注入

Setter方法注入是一种通过Setter方法将依赖关系注入到类中的方式。在Spring中,这是另一种常见的依赖注入方式,特别适用于那些希望在对象创建后灵活设置依赖关系的情况。以下是Setter方法注入的基本概念和可选性的Setter方法的处理方法。

Setter方法注入的基本概念:

  1. Setter方法: 一个类中的Setter方法用于设置对象的属性或依赖关系。

  2. Setter方法注入: 将依赖关系通过Setter方法进行注入。这意味着对象在创建后,通过调用相应的Setter方法来提供依赖。

示例:

public class MyService {private MyDependency myDependency;// Setter方法注入public void setMyDependency(MyDependency myDependency) {this.myDependency = myDependency;}// 其他业务逻辑
}

在上述例子中,MyService通过一个名为setMyDependency的Setter方法接受一个MyDependency的实例,从而实现了Setter方法注入。

可选性的Setter方法:

有时候,某个依赖关系可能是可选的,这时你可以通过使用@Autowired注解的required属性为Setter方法设置可选性。如果required属性设置为false,则该依赖关系是可选的,如果找不到匹配的Bean,Spring容器不会报错,而是跳过这个Setter方法的注入。

public class MyService {private MyDependency myDependency;// 可选性的Setter方法注入@Autowired(required = false)public void setMyDependency(MyDependency myDependency) {this.myDependency = myDependency;}// 其他业务逻辑
}

在上述例子中,setMyDependency方法被标注为可选的,如果找不到匹配的MyDependency的Bean,Spring容器不会报错。

Setter方法注入的优势在于它提供了更灵活的方式来设置依赖关系,特别适用于那些依赖关系不是必需的情况。然而,在某些情况下,Setter方法注入可能导致对象处于不完全初始化的状态,因此需要谨慎使用。

接口注入

在Spring中,你可以通过在接口中定义依赖注入的方法,然后在实现这个接口的类中实现该方法,来实现接口注入。这种方式通常用于在接口中定义一些默认的行为或配置,然后在实现类中进行具体的依赖注入。以下是一个简单的示例:

1. 定义接口:

public interface DependencyInjectable {void injectDependency(MyDependency myDependency);
}

在上述例子中,DependencyInjectable接口定义了一个名为injectDependency的方法,用于依赖注入。

2. 实现接口:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;@Component
public class MyService implements DependencyInjectable {private MyDependency myDependency;// 实现接口的依赖注入方法@Override@Autowiredpublic void injectDependency(MyDependency myDependency) {this.myDependency = myDependency;}// 其他业务逻辑
}

在上述例子中,MyService类实现了DependencyInjectable接口,并在该类中使用@Autowired注解实现了injectDependency方法的依赖注入。

3. 使用注入后的依赖:

public class AnotherService {private final DependencyInjectable dependencyInjectable;public AnotherService(DependencyInjectable dependencyInjectable) {this.dependencyInjectable = dependencyInjectable;}public void performOperation() {// 使用注入后的依赖dependencyInjectable.injectDependency(new MyDependency());// 其他操作}
}

在上述例子中,AnotherService类通过构造函数注入了一个实现了DependencyInjectable接口的实例,并在performOperation方法中调用了injectDependency方法。

接口注入的主要优势在于它可以在接口中定义一些默认的行为或配置,然后在实现类中选择性地实现或覆盖这些方法。这使得接口可以在不同的实现类中具有不同的依赖注入逻辑,提供更大的灵活性。但需要注意的是,这种方式可能导致实现类对Spring的依赖,因此在使用时需要谨慎。

自动装配

在Spring中,@Autowired注解用于实现自动装配(Autowired),它可以自动将匹配类型的Bean注入到目标类的字段、构造函数或方法参数中。自动装配可以根据类型和名称进行匹配。以下是关于@Autowired注解的使用示例:

1. 根据类型进行自动装配:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;@Component
public class MyService {private final MyDependency myDependency;// 根据类型进行自动装配@Autowiredpublic MyService(MyDependency myDependency) {this.myDependency = myDependency;}// 其他业务逻辑
}

在上述例子中,MyService类通过构造函数使用@Autowired注解自动注入了一个MyDependency类型的Bean。

2. 根据名称进行自动装配:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;@Component
public class AnotherService {private final MyDependency myDependency;// 根据名称进行自动装配@Autowiredpublic AnotherService(@Qualifier("specificDependency") MyDependency myDependency) {this.myDependency = myDependency;}// 其他业务逻辑
}

在上述例子中,AnotherService类通过构造函数使用@Autowired注解自动注入了一个名为specificDependencyMyDependency类型的Bean。使用了@Qualifier注解来指定具体的Bean名称。

3. 字段和方法参数的自动装配:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;@Component
public class YetAnotherService {private MyDependency myDependency;// 字段自动装配@Autowired@Qualifier("specificDependency")private AnotherDependency anotherDependency;// 方法参数自动装配@Autowiredpublic void setMyDependency(@Qualifier("specificDependency") MyDependency myDependency) {this.myDependency = myDependency;}// 其他业务逻辑
}

在上述例子中,YetAnotherService类通过字段和方法参数分别使用@Autowired注解实现了字段和方法参数的自动注入。同样使用了@Qualifier注解来指定具体的Bean名称。

@Autowired注解是Spring中最常用的自动装配注解之一,它简化了依赖注入的过程,提高了代码的可读性和可维护性。通过@Qualifier注解,你可以更精确地控制自动装配的具体Bean。

Qualifier注解

@Qualifier注解是Spring框架提供的一种用于解决多个同类型Bean自动装配问题的机制。当有多个相同类型的Bean存在于容器中,@Qualifier注解可以与@Autowired注解一起使用,通过指定Bean的名称来明确指定要注入的具体Bean。以下是一个简单的示例:

示例:

假设有两个实现了同一个接口的Bean:

public interface MyInterface {// 接口定义
}
import org.springframework.stereotype.Component;@Component("beanA")
public class BeanA implements MyInterface {// 实现
}
import org.springframework.stereotype.Component;@Component("beanB")
public class BeanB implements MyInterface {// 实现
}

然后在另一个类中进行自动装配:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;@Component
public class MyService {private final MyInterface myInterface;// 使用@Qualifier指定具体Bean的名称@Autowiredpublic MyService(@Qualifier("beanB") MyInterface myInterface) {this.myInterface = myInterface;}// 其他业务逻辑
}

在上述例子中,MyService类通过构造函数使用@Autowired注解进行自动装配,而通过@Qualifier("beanB")注解明确指定了要注入的Bean的名称为"beanB"。这样,在存在多个实现了MyInterface接口的Bean时,Spring就能够根据@Qualifier注解找到特定的Bean来进行注入。

总体而言,@Qualifier注解是一个有效的工具,用于解决多个同类型Bean的自动装配问题。在使用时,需要确保指定的Bean名称与实际的Bean定义名称一致。

Primary注解

@Primary注解是Spring框架提供的一种机制,用于标记一个Bean作为首选的Bean。当有多个同类型的Bean存在于容器中,并且没有使用@Qualifier注解指定具体要注入的Bean时,Spring容器会优先选择标记了@Primary注解的Bean进行注入。这有助于解决多个同类型Bean的歧义性。

以下是一个简单的示例:

示例:

假设有两个实现了同一个接口的Bean:

public interface MyInterface {// 接口定义
}
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;@Component
@Primary
public class PrimaryBean implements MyInterface {// 实现
}
import org.springframework.stereotype.Component;@Component
public class SecondaryBean implements MyInterface {// 实现
}

在上述例子中,PrimaryBean类使用了@Primary注解,标记为首选的Bean。

然后在另一个类中进行自动装配:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;@Component
public class MyService {private final MyInterface myInterface;// 由于PrimaryBean标记了@Primary,它会被首选注入@Autowiredpublic MyService(MyInterface myInterface) {this.myInterface = myInterface;}// 其他业务逻辑
}

在上述例子中,MyService类通过构造函数使用@Autowired注解进行自动装配,由于PrimaryBean标记了@Primary注解,它会被首选注入到myInterface字段中。

总体而言,@Primary注解是一种方便的机制,用于解决多个同类型Bean的自动装配问题,使得Spring容器能够明确知道首选的Bean是哪个。需要注意的是,使用@Primary注解时,确保只在必要的情况下使用,以避免引入歧义。****

使用java配置进行DI

在Spring中,可以使用Java配置类(通过@Configuration注解标记的类)和@Bean注解来实现依赖注入。以下是一个简单的示例,演示了如何使用Java配置进行依赖注入:

1. 创建一个接口和两个实现类:

// MyInterface.java
public interface MyInterface {void myMethod();
}
// MyImplementationA.java
public class MyImplementationA implements MyInterface {@Overridepublic void myMethod() {System.out.println("MyImplementationA");}
}
// MyImplementationB.java
public class MyImplementationB implements MyInterface {@Overridepublic void myMethod() {System.out.println("MyImplementationB");}
}

2. 创建一个Java配置类:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class AppConfig {@Beanpublic MyInterface myImplementationA() {return new MyImplementationA();}@Beanpublic MyInterface myImplementationB() {return new MyImplementationB();}
}

在上述例子中,AppConfig类使用了@Configuration注解标记为Java配置类,并通过@Bean注解定义了两个Bean,分别是myImplementationAmyImplementationB

3. 使用配置类进行依赖注入:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.stereotype.Component;@Component
public class MyService {private final MyInterface myInterface;@Autowiredpublic MyService(MyInterface myInterface) {this.myInterface = myInterface;}public void performOperation() {myInterface.myMethod();}public static void main(String[] args) {AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);MyService myService = context.getBean(MyService.class);myService.performOperation();context.close();}
}

在上述例子中,MyService类通过构造函数注入了一个MyInterface类型的Bean,而这个Bean的具体实现是通过AppConfig配置类定义的。在main方法中,通过AnnotationConfigApplicationContext加载配置类,然后获取MyService的Bean并调用performOperation方法。

这样,就实现了通过Java配置进行依赖注入。这种方式使得依赖关系更清晰,同时能够充分利用Java的编程能力进行灵活的配置。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/603670.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

laravel-admin之 浏览器自动填充密码(如果需要渲染数据库密码的话,首先确认数据库密码是否可以逆向解密)

参考 https://blog.51cto.com/u_10401840/5180106 为什么浏览器端保存的密码一直自动写入到$form->password 解决办法 2、在页面进入的时候,默认表单的type值为text;推荐指数:2颗星 5、设置表单的readonly属性;推荐指数:4颗…

实习遇到问题备忘录

1.Hutool工具包的DB Hutool学习 —— 数据库 - db (一)Db简单操作 - 简书 (jianshu.com) 2.Consumer函数接口 Java 常用函数式接口之Consumer接口 - LeeHua - 博客园 (cnblogs.com) 3.sql高级用法merge into SQL高级知识——MERGE INTO - 知乎 (zhi…

Linux 上 Nginx 配置访问 web 服务器及配置 https 访问配置过程记录

目录 一、前言说明二、配置思路三、开始修改配置四、结尾 一、前言说明 最近自己搭建了个 Blog 网站,想把网站部署到服务器上面,本文记录一下搭建过程中 Nginx 配置请求转发的过程。 二、配置思路 web项目已经在服务器上面运行起来了,运行的端…

222.【2023年华为OD机试真题(C卷)】分配土地(扫描线算法-JavaPythonC++JS实现)

🚀点击这里可直接跳转到本专栏,可查阅顶置最新的华为OD机试宝典~ 本专栏所有题目均包含优质解题思路,高质量解题代码(Java&Python&C++&JS分别实现),详细代码讲解,助你深入学习,深度掌握! 文章目录 一. 题目-分配土地二.解题思路三.题解代码Python题解代码…

FineBI:简介

1 介绍 FineBI 是帆软软件有限公司推出的一款商业智能(Business Intelligence)产品。 FineBI 是定位于自助大数据分析的 BI 工具,能够帮助企业的业务人员和数据分析师,开展以问题导向的探索式分析。 2 现阶段数据分析弊端 现阶…

广义零样本学习综述的笔记

1 Title A Review of Generalized Zero-Shot Learning Methods(Farhad Pourpanah; Moloud Abdar; Yuxuan Luo; Xinlei Zhou; Ran Wang; Chee Peng Lim)【IEEE Transactions on Pattern Analysis and Machine Intelligence 2022】 2 conclusion Generali…

【DevOps-05】Integrate工具

一、简要说明 持续集成、持续部署的工具很多,其中Jenkins是一个开源的持续集成平台。 Jenkins涉及到将编写完毕的代码发布到测试环境和生产环境的任务,并且还涉及到了构建项目等任务。 Jenkins需要大量的插件保证工作,安装成本较高,下面会基于Docker搭建Jenkins。 二、Jenk…

HPM6750开发笔记《DMA接收和发送数据UART例程深度解析》

目录 概述: 端口设置: 代码分析: 运行现象: 概述: DMA(Direct Memory Access)是一种计算机系统中的数据传输技术,它允许数据在不经过中央处理器(CPU)的直…

Vue2:修改默认配置的方法

一、前情概要 之前我们说到,用vue-cli创建vue项目之后,项目结构大概是这样的。其中,标红部分的文件是非常重要的结构文件,不建议修改文件名。 但是,实际上了,vue是允许修改的。 准备配置文件:v…

软件测试|使用PyMySQL访问MySQL数据库的详细指南

简介 PyMySQL是Python中流行的MySQL数据库驱动程序,它提供了便捷的方法来连接、查询和更新MySQL数据库。本文将为您提供使用PyMySQL访问MySQL数据库的详细指南,包括安装PyMySQL、连接数据库、执行查询和更新操作等。 环境准备 在开始之前,…

编译原理笔记(三)

一、词法分析程序的设计 1、词法分析程序的输出 在识别出下一个单词同时验证其词法正确性之后,词法分析程序将结果以单词符号的形式发送至语法分析程序以回应其请求。 单词符号一般分下列5类: 关键字:如:begin、end、if、whil…

双变量probit模型

1. Probit模型 1.1 模型含义 假设个体只有两种选择,y1或y0。影响选择的变量都包括在向量x中。即线性概率模型为 y值服从两点分布 被认为是连接函数,函数选择具有一定的灵活性。如果为标准正态的累积分布函数,则模型成为Probit模型&#xff…

NACHI机器人模拟示教器如何切换中文

前言 现在开始学习机器人的编程语言,那么要学习会用首先得用模拟示教器来学习,但是全是英文确实比较难受一些些,没有中文来的直观。所以摸透一下如何给示教器更换语言。 具体步骤 步骤一:将中文的汉化包下载下来。具体的下载链…

Spring AOP—通知类型 和 切入点表达式 万字详解(通俗易懂)

目录 一、前言 二、AOP—快速入门 1.动态代理优化 : 2.问题分析 : 3.AOP—基本介绍 : 4.AOP—使用说明 : 5.AOP—入门案例 : 三、AOP—切入点表达式 1.基本说明 : 2.语法格式 : 3.注意事项 : 4.代码演示 : 四、AOP—切入点表达式的更多细节 1.JoinPoint : 1.1 简…

华为HCIE课堂笔记第十三章 IPv6地址配置

目录 第十三章 IPv6地址配置 13.1 IPv6地址无状态自动配置 13.1.1 RS和RA报文格式 13.1.2 RA的Flags字段 13.1.3 地址的生存周期 13.1.4 RA报文中前缀中的Flags 13.2 DHCPv6 13.2.1 DHCPV6的概念 13.2.2 DCHPv6的报文 第十三章 IPv6地址配置 13.1 IPv6地址无状态自动…

ENVI无法打开Landsat8的头文件问题和解决

问题 解决方案 双击打开该xxx_MTL.txt的头文件 第一行GROUP LANDSAT_METADATA_FILE 改为 GROUP L1_METADATA_FILE 按住CTRLF,查找GROUP LEVEL1_PROCESSING_RECORD 会查到两个包含的句子,把这两个中间的部分全部删掉 删除空行后,应该为…

基于Python+Django,开发一款房屋租赁系统

学习文档 学习过程中,遇到问题可以咨询作者 功能介绍 平台采用B/S结构,后端采用主流的PythonDjango进行开发,前端采用主流的Vue.js进行开发。 整个平台包括前台和后台两个部分。 前台功能包括:首页、房屋详情页、用户中心模块。…

学习使用js/jquery获取指定class名称的三种方式

学习使用js/jquery获取指定class名称的三种方式 简介一、获取元素的class名称1、通过原生JS获取元素的class名称2、通过Jquery获取元素的class名称 二、应用1、样式修改2、动画效果实现 简介 在开发网页时,我们经常需要通过JS获取元素的class名称进行一些操作&…

IMU用于无人机故障诊断

最近,来自韩国的研究团队通过开发以IMU为中心的数据驱动诊断方法,旨在多旋翼飞行器可以自我评估其性能,即时识别和解决推进故障。该方法从单纯的常规目视检查跃升为复杂的诊断细微差别,标志着无人机维护的范式转变。 与依赖额外传…

Unity中Shader面片一直面向摄像机(个性化修改及适配BRP)

文章目录 前言一、个性化修改面向摄像机效果1、把上一篇文章中求的 Z轴基向量 投影到 XoZ平面上2、其余步骤和之前的一致3、在属性面板定义一个变量,控制面片面向摄像机的类型4、效果 二、适配BRP三、最终代码 前言 在上一篇文章中,我们用Shader实现了面…