Spring 之依赖注入底层原理

news2025/1/21 0:58:57

        Spring 框架作为 Java 开发中最流行的框架之一,其核心特性之一就是依赖注入(Dependency Injection,DI)。在Spring中,依赖注入是通过 IOC 容器(Inversion of Control,控制反转)来实现的。本文将详细介绍Spring的依赖注入底层原理,并提供源码示例。

什么是依赖注入

        依赖注入是一种设计模式,它将对象之间的依赖关系从代码中移除,并由容器来管理这些依赖关系。依赖注入的主要目的是降低代码的耦合度,使代码更加灵活和可维护。

        在 Java 中,依赖通常是通过构造函数或者 Setter 方法来注入的。使用依赖注入,我们可以将对象的创建和依赖关系的管理分离开来,从而使得代码更加容易测试和维护。

实现原理

        Spring的依赖注入是通过 IOC 容器来实现的。在Spring中,IOC 容器负责创建和管理对象,以及管理对象之间的依赖关系。

IOC 容器

        IOC 容器是指用于管理对象和依赖关系的容器。Spring提供了多种 IOC 容器实现,包括 BeanFactory 和 ApplicationContext 等。

        BeanFactory 是 Spring 中最基本的 IOC 容器,它提供了基本的 IOC 功能。

        ApplicationContext 则是BeanFactory的扩展,它提供了更多的功能,如事件发布、国际化支持、AOP等。

        在 Spring 中,BeanFactory 和 ApplicationContext 都是通过反射来实例化对象,并通过依赖注入来管理对象之间的依赖关系。

Bean定义

        在 Spring 中,每个被管理的对象都需要有一个对应的 Bean 定义。Bean定义是一个元数据,它描述了一个 Bean 的类型、属性、依赖关系等信息。

        Bean 定义通常是通过XML配置文件、 Java 配置类或者注解来定义的。下面是一个使用 XML 配置文件定义 Bean 的示例:

<bean id="userService" class="com.example.UserService">
    <property name="userRepository" ref="userRepository"/>
</bean>

<bean id="userRepository" class="com.example.UserRepositoryImpl"/>
复制代码

在上面的示例中,我们定义了一个名为 userService的Bean,它的类型是com.example.UserService。它依赖于另一个名为userRepository的Bean,类型是com.example.UserRepositoryImpl。

依赖注入

        在 Spring 中,依赖注入是通过反射来实现的。当 IOC 容器创建 Bean 时,它会检查 Bean 定义中所声明的依赖关系,并尝试通过反射来注入这些依赖关系。

依赖注入通常分为三种方式:构造函数注入、Setter 方法注入和字段注入。

构造函数注入

        构造函数注入是最常见的依赖注入方式。在 Spring 中,我们可以通过构造函数来注入 Bean 的依赖关系。当 IOC 容器创建 Bean 时,它会检查 Bean 定义中所声明的构造函数,并尝试通过反射来调用这个构造函数,并将依赖关系作为参数传递进去。

下面是一个使用构造函数注入的示例:

public class UserService {
    private final UserRepository userRepository;

    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    // ...
}
复制代码

在上面的示例中,我们定义了一个 UserService 类,并将 UserRepository 依赖关系通过构造函数注入进去。

Setter方法注入

        Setter 方法注入是另一种常见的依赖注入方式。我们可以通过 Setter 方法来注入Bean的依赖关系。当 IOC 容器创建Bean时,它会检查 Bean 定义中所声明的 Setter 方法,并尝试通过反射来调用这个 Setter 方法,并将依赖关系作为参数传递进去。

下面是一个使用Setter方法注入的示例:

public class UserService {
    private UserRepository userRepository;

    public void setUserRepository(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    // ...
}
复制代码

在上面的示例中,我们定义了一个 UserService 类,并将 UserRepository 依赖关系通过 Setter 方法注入进去。

字段注入

        字段注入是一种不太常见的依赖注入方式。我们可以通过字段来注入Bean的依赖关系。当 IOC 容器创建 Bean 时,它会尝试通过反射来注入这些字段。

下面是一个使用字段注入的示例:

public class UserService {
    @Autowired
    private UserRepository userRepository;

    // ...
}
复制代码

在上面的示例中,我们使用了 @Autowired 注解来将 UserRepository 依赖关系注入到 userService 对象中的 userRepository 字段中。

生命周期回调

在Spring中,Bean生命周期包括四个阶段:实例化、属性赋值、初始化、销毁。在这些阶段中,Spring 提供了多个回调方法,以便我们在 Bean 的生命周期中进行一些自定义操作。

下面是一些常用的Bean生命周期回调方法:

实例化:Bean 实例化之后,Spring 会调用 BeanPostProcessor 的 postProcessBeforeInitialization 方法。

属性赋值:在Bean实例化之后,Spring 会将 Bean 定义中所声明的属性值赋值给 Bean 对象。

初始化:在 Bean 属性赋值之后,Spring 会调用 InitializingBean 的 afterPropertiesSet 方法或者 @Bean 注解的 initMethod 方法。

销毁:在 IOC 容器关闭时,Spring 会调用 DisposableBean 的 destroy 方法或者 @Bean 注解的 destroyMethod方 法。

下面是一个实现 InitializingBean 和 DisposableBean 接口的示例:

public class UserService implements InitializingBean, DisposableBean {
    private UserRepository userRepository;

    public void setUserRepository(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    public void afterPropertiesSet() throws Exception {
        // 在Bean属性赋值之后,执行一些初始化操作
    }

    public void destroy() throws Exception {
        // 在 IOC 容器关闭时,执行一些销毁操作
    }

    // ...
}
复制代码

注解

         在 Spring 中,我们可以使用注解来简化 Bean 定义和依赖注入的过程。Spring 提供了多个注解来实现依赖注入和生命周期回调等功能。

下面是一些常用的 Spring 注解:

@Component:用于标记一个类为 Bean。

@Autowired:用于标记一个字段、构造函数或者 Setter 方法需要进行依赖注入。

@Qualifier:当存在多个相同类型的 Bean 时,用于指定依赖注入的具体实现。

@Value:用于注入一个常量值。

@PostConstruct:用于标记一个方法为 Bean 初始化方法。

@PreDestroy:用于标记一个方法为 Bean 销毁方法。

下面是一个使用注解的示例:

@Component
public class UserService {
    @Autowired
    private UserRepository userRepository;

    @PostConstruct
    public void init() {
        // 在Bean属性赋值之后,执行一些初始化操作
    }

    @PreDestroy
    public void destroy() {
        // 在 IOC 容器关闭时,执行一些销毁操作
    }

    // ...
}
复制代码

源码示例

下面是一个使用Spring的依赖注入功能的示例:

@Component
public class UserService {
    @Autowired
    private UserRepository userRepository;

    public void save(User user) {
        userRepository.save(user);
    }
}

@Component
public class UserRepositoryImpl implements UserRepository {
    public void save(User user) {
        // 保存用户信息
    }
}

@Configuration
public class AppConfig {
    @Bean
    public UserService userService() {
        return new UserService();
    }

    @Bean
    public UserRepository userRepository() {
        return new UserRepositoryImpl();
    }
}

public class Main {
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
        UserService userService = context.getBean(UserService.class);
        User user = new User();
        userService.save(user);
    }
}

复制代码

        在上面的示例中,我们使用了 @Component 注解标记了 UserService 和 UserRepositoryImpl 两个类为 Bean。在 AppConfig 类中,我们使用 @Bean 注解来定义了 UserRepository 和 UserService 两个 Bean。在 Main 类中,我们使用 AnnotationConfigApplicationContext 来创建 IOC 容器,并通过依赖注入来获取 UserService 对象,并调用它的 save 方法。

总结

        本文详细介绍了 Spring 的依赖注入底层原理,并提供了源码示例。通过本文的学习,我们可以更好地理解 Spring 的依赖注入机制,以及如何在实际开发中使用它来降低代码的耦合度,使代码更加灵活和可维护。

 

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

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

相关文章

助力研发效能变革,第七届Techo TVP 开发者峰会圆满落下帷幕

引言 在互联网数字企业结束“野蛮扩张”、追求高质量增长的今天&#xff0c;研发效能已然成为企业关注的核心命题。伴随着云原生概念在软件领域的落地生根&#xff0c;云原生正驱动软件应用设计、实现、部署及运维方式的巨变&#xff0c;为研发效能治理带来了新的挑战与机遇&am…

2022国赛31:虚拟化-------安装docker

大赛试题内容: 八、虚拟化(20) 在Windows3安装docker,导入NanoServer镜像。软件包和镜像存放在物理机D:\soft\DockerWindows。创建名称为web的容器,映射虚拟机的80端口到容器的80端口,容器启动后运行cmd命令,保持容器处于运行状态。解答过程: windows docker 安装 1.添…

知识图谱嵌入方法-transE

目录 一、知识图谱嵌入 二、transE算法 三、缺点 一、知识图谱嵌入 知识图谱(Knowledge Graph&#xff0c;KG) 是大规模语义网络知识库&#xff0c;利用三元组&#xff08;实体&#xff0c;关系&#xff0c;实体&#xff09;来描述具体的知识&#xff0c;其具有语义丰富、结…

vue-element-admin 动态菜单改造

vue-element-admin 动态菜单改造 vue-element-admin 是一款优秀后台前端解决方案&#xff0c;它基于 vue 和 element-ui实现。开源后台管理系统解决方案项目 Boot-admin的前端模块就是基于vue-element-admin开发而来。 作为一款纯前端的后台界面解决方案&#xff0c;vue-elem…

剑指 Offer 51. 数组中的逆序对

剑指 Offer 51. 数组中的逆序对 难度&#xff1a;hard\color{red}{hard}hard 题目描述 在数组中的两个数字&#xff0c;如果前面一个数字大于后面的数字&#xff0c;则这两个数字组成一个逆序对。输入一个数组&#xff0c;求出这个数组中的逆序对的总数。 示例 1: 输入: [7…

怎么设计秒杀系统?

秒杀系统需要考虑哪些要素&#xff1f; 要能支持高并发用户体验要好&#xff0c;不要返回异常信息对系统要友好&#xff08;针对秒杀可以做业务上的隔离&#xff0c;单独把秒杀系统部署到独立的集群服务器上&#xff1b;可动态配置业务参数&#xff0c;比如商品金额&#xff0…

电磁兼容(EMC)的标准与测试内容

在国际范围上&#xff0c;电磁兼容标准的制定已经有了70多年的发展历程&#xff0c;最早为了保护无线电通信和广播&#xff0c;国际无线电干扰特别委员会&#xff08;CISPR&#xff09;对各种用电设备和系统提出了相关的电磁干扰发射限值和测量方法。到了20世纪60&#xff5e;7…

机器学习:基于朴素贝叶斯(Naive Bayes)的分类预测

目录 一、简介和环境准备 简介&#xff1a; 环境&#xff1a; 二、实战演练 2.1使用葡萄&#xff08;Wine&#xff09;数据集&#xff0c;进行贝叶斯分类 1.数据导入 2.模型训练 3.模型预测 2.2模拟离散数据集–贝叶斯分类 1.数据导入、分析 2.模型训练、预测 三、原…

TiDB进阶篇-TiDB Server架构

简介 较深入的介绍TiDB Server。 TiDB Server 架构 图解 1.下面是负责SQL语句的解析和优化。 2.下面试负责TiKV存储多版本&#xff0c;过期版本的清理作用。 3.复杂SQL的拆分&#xff08;如果是点查那么就不需要经过DistSQL&#xff09;。 4.事务相关。 5.负责PD和TiKV的通信…

js 事件流程

描述 JavaScript 的执行是单线程的&#xff0c;后面的任务需要等待前面的任务完全完成后&#xff0c;再去执行。DOM 事件&#xff08;文件的加载等&#xff09;、定时器、网络请求等事件&#xff0c;并不会消耗 CPU&#xff0c;这些事件无需等候&#xff0c;所以出现了异步。主…

Java后端新人入职第一天,环境搭建,全看这篇就行了

本文主要是记录一下一个新人java后端开发来到一个新公司,如何快速将自己的相关开发环境搭建好,包括Java、Maven、Tomcat、idea、Redis、Mysql等等,有的公司会有相关版本的要求,不过安装配置步骤基本一样的,我这里就以目前比较流行的版本进行详细说明。 一:基础环境搭建:…

Arduino开发之如何连接GPS模块?

文章目录0、引言1、GPS模块说明2、接调试助手测试GPS模块接收数据3、代码编写4、功能演示0、引言 NEO-6M/7M GPS模块&#xff0c;具有高灵敏度、低功耗、小型化、高追踪灵敏度&#xff0c;大大扩大了其定位的覆盖面&#xff0c;在普通GPS接收模块不能定位的地方&#xff0c;如狭…

编译原理考试大题分析【太原理工大学】

有些基本公式可以看这里&#xff0c;大题内容请以本篇为准&#xff01;https://blog.csdn.net/m0_52861684/article/details/130071191?spm1001.2014.3001.5501 之前说错了&#xff0c;考试题型没有简答题和填空题&#xff0c;只有十个选择题是 20 分&#xff0c;其余全是大题…

ESP32设备驱动-VEML6075紫外线(UV)光传感器驱动

VEML6075紫外线(UV)光传感器驱动 文章目录 VEML6075紫外线(UV)光传感器驱动1、VEML6075介绍2、硬件准备3、软件准备4、驱动实现1、VEML6075介绍 VEML6075 可感应 UVA 和 UVB 光,并使用 CMOS 工艺将光电二极管、放大器和模拟/数字电路集成到单个芯片中。 应用 UV 传感器时,它…

ChatGPT和GPT-4带你选笔记本电脑

大家好,我是herosunly。985院校硕士毕业,现担任算法研究员一职,热衷于机器学习算法研究与应用。曾获得阿里云天池比赛第一名,CCF比赛第二名,科大讯飞比赛第三名。拥有多项发明专利。对机器学习和深度学习拥有自己独到的见解。曾经辅导过若干个非计算机专业的学生进入到算法…

最近ChatGPT封号太严重了,这里是解封攻略步骤(建议收藏)

这个周末&#xff0c;先是意大利暂时封杀ChatGPT&#xff0c;限制OpenAI处理本国用户信息。 接着&#xff0c;据韩国媒体报道&#xff0c;三星导入ChatGPT不到20天&#xff0c;便曝出机密资料外泄。 还没结束&#xff0c;又有大量网友发现ChatGPT目前停止注册&#xff0c;开始…

【vue】vue中下载文件的方法

文章目录1. 下载后端返回文件1.1 后端为post请求返回二进制流文件URL.createObjectURLFileReader1.2 后端直接返回get请求文件2. 下载本地文件1. 下载后端返回文件 1.1 后端为post请求返回二进制流文件 Blob Blob对象标识一个不可变、原始数据的类文件对象。Blob表示的不一定…

RabbitMQ( 发布订阅模式 ==> FanoutExchange )

本章目录&#xff1a; 何为发布订阅模式FanoutExchange具体使用一、何为发布订阅模式 在上一篇文章中&#xff0c;我们创建了Work Queue并且发送任务&#xff0c;在Work Queue中&#xff0c;每个任务只会被一个消费者消费&#xff0c;任务消费后就被清除了。 而在本篇中&…

0202心跳和服务续约源码解析-nacos2.x-微服务架构

文章目录1 客户端心跳任务2 服务端处理2.1 服务注册时开启客户端心跳检查2.2 客户端发送心跳任务续约2.3 服务实例移除2.4 心跳任务闭环结语1 客户端心跳任务 在上一篇文章0201服务注册源码解析-nacos2.x-微服务架构分析客户端服务注册的时候&#xff0c;流程在NacosNamingSer…

重装系统下载网址

[置顶]无论会不会安装系统&#xff0c;都一定会需要&#xff0c;觉得内容不错欢迎一键三连哦 稳定 | 方便 | 好用 1、MSDN 用过最简单好用&#xff0c;下载不限速&#xff0c;支持迅雷、IDM多种下载方式 https://www.xitongku.com 2、Windows系统下载仓储站 为小白重装系统提供…