前一节介绍了一些spring相关的引入,例如spring的优势,spring的发展历程(Spring6最重要的就是基线更新,JDK17以及命名空间变成Jakarta),Spring的模块介绍(比如核心容器实现Bean对象生命周期和依赖关系的管理,实现IOC/DI,还有事务等等…)
现在聚焦于Spring的IOC/DI的学习,本篇笔记专注于:理解IOC/DI是什么,以及为了实现IOC/DI的练习,提前准备好开发的环境。
1.IOC/DI的引入- 概念的解释辨析
很多视频/书籍都喜欢把IOC和DI两者做区分,但是其实IoC(Inversion of Control,控制反转)和 DI(Dependency Injection,依赖注入)是一个东西,他们是描述同一个东西的不同的名称而已。
首先:
IOC:控制反转指的是创建对象的权利,或者说对对象的控制权,从开发者编写的 Java 代码中转移到了 Spring 容器。 传统上,当一个对象需要使用另一个对象时,开发者会手动使用 new
关键字来创建这个被依赖的对象。而在 IoC 的思想下,开发者不再需要这样做。Spring 容器会负责对象的创建、配置和生命周期管理。
直白来说:【对象的创建、依赖关系的组装及其生命周期的管理权】这个控制权被反转了,转移到由容器自动化管理。
DI:由于“控制反转”这个概念相对抽象,2004 年,著名软件大师 Martin Fowler 提出了一个更具描述性的新名称:“依赖注入”。依赖注入明确描述了“被注入对象依赖 IoC 容器来配置依赖对象”。简单来说,当 Spring 容器创建好某个对象(这个对象就是“依赖对象”)后,它会将这个依赖对象自动地“注入”到那些需要它的地方(即“被注入对象”)。
——一句话理解就是:【创建对象及对象之间依赖关系的管理权,都交由 Spring 容器来完成。 】
开发者无需关心对象的创建过程,只需声明它们之间的依赖关系,Spring 容器就会在运行时自动满足这些依赖。
那么这一点是怎么做的呢?
工作原理: Spring 容器在启动时,会读取开发者提供的配置信息(例如 XML 配置或注解),然后使用【反射】机制创建好相应的对象,并将这些对象存储在一个内部的集合(通常可以理解为一个 Map
集合,数据结构就是键值对)中。当应用程序代码需要某个对象时,它不再通过 new
来创建,而是向 Spring 容器“索取”(通过 ID 或类型),容器就会将已经创建并管理好的对象提供给它。
除了这些,再解释2个概念:
【1】容器Container:在 Spring 框架中,容器是放置所有被管理对象的“场所”或“载体”。 它是 IoC/DI 机制的实现者,负责管理应用程序中组件的生命周期和依赖关系。
- Spring 容器的本质是一个复杂的软件组件,其核心是一个全局的
Map
对象(或类似的内部数据结构)。这个Map
中存储了所有被 Spring 容器管理的对象实例。 - 在 Spring 中,容器通常指的是
ApplicationContext
接口及其子接口或实现类。ApplicationContext
提供了更丰富的功能,如国际化、事件传播、资源加载等,是访问和管理 Spring Bean 的主要入口点。早期的 Spring 容器还有BeanFactory
,但ApplicationContext
是BeanFactory
的超集,提供了更多企业级功能。
【2】Bean:在 Spring 容器中,所有被 Spring 容器管理的对象都被称为 Bean。如果特指其中的一个对象,则称为“一个 Bean”。
- Bean 的创建、配置、生命周期(初始化、使用、销毁)都由 Spring 容器管理
- Bean 通常代表应用程序中的一个功能模块或组件,例如一个 Service 类、一个 Dao 类、一个 Controller 类等。
- Bean 可以通过 XML 文件、Java 配置类(
@Configuration
和@Bean
注解)或组件扫描(@Component
、@Service
、@Repository
、@Controller
等注解)来定义和注册到 Spring 容器中。
可以这样理解这三者之间的关系:开发者将需要 Spring 管理的对象(Bean)的定义告知给 Spring 容器,然后容器负责反转控制(IoC),即自行创建和管理这些 Bean,并在 Bean 之间需要协作时,自动地将一个 Bean 注入(DI) 到另一个 Bean 中,从而实现了松耦合的应用程序架构。
2.使用IOC/DI的好处
Spring IoC/DI使用后可以管理项目中相关对象,让对象管理的事情和业务分离(解耦)。同时程序员也不管理对象的依赖关系,所有的依赖关系交给Spring容器进行管理。
没有使用spring之前:
使用spring-IoC/DI以后:
以后如果改变实现类,只需要修改配置文件即可,不需要去java代码中操作了。
要注意我们改都是改实现类,不会改接口,接口是规范
Spring IoC/DI 的使用使得项目中对象管理(对象的创建、组装、生命周期)的职责与业务逻辑完全分离。在没有 Spring 之前,开发者需要在业务代码中手动 new
对象并管理它们的依赖关系,这导致业务代码与对象管理细节紧密耦合。引入 Spring 后,程序员不再需要亲自管理对象的依赖关系,所有的依赖关系都交由 Spring 容器进行管理。
如果通过代码举例,可以看下面的例子:
没有使用 Spring 之前:
1
2
3
4
5
6
7
8
9
10
11
12
13
// 假设 Service 层需要 Dao 层对象
public class PeopleServiceImpl {
private PeopleDao peopleDao;
public PeopleServiceImpl() {
// 需要手动创建 Dao 对象,耦合性高
this.peopleDao = new PeopleDaoImpl();
}
public void doSomething() {
peopleDao.queryData();
}
}
这种方式的缺点是,如果 PeopleDaoImpl
改变为 PeopleDaoHibernateImpl
,那么 PeopleServiceImpl
的代码也必须修改。
使用 Spring-IoC/DI 以后:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// Spring 会自动注入 Dao 对象
public class PeopleServiceImpl {
// 只需要声明依赖,Spring 会在运行时注入具体的实现
private PeopleDao peopleDao;
// 通过构造器、Setter 方法或字段注解实现依赖注入
public PeopleServiceImpl(PeopleDao peopleDao) {
this.peopleDao = peopleDao;
}
// 或者使用 @Autowired 注解
// @Autowired
// private PeopleDao peopleDao;
public void doSomething() {
peopleDao.queryData();
}
}
通过 Spring IoC/DI,开发者只需在配置文件(或通过注解)中声明 PeopleServiceImpl
依赖于 PeopleDao
接口的具体实现,Spring 容器就会在运行时自动将 PeopleDaoImpl
(或任何其他 PeopleDao
的实现)注入到 PeopleServiceImpl
中。
以后如果需要改变 PeopleDao
的具体实现类,例如从 PeopleDaoImpl
切换到 PeopleDaoMybatisImpl
,只需要修改 Spring 的配置文件或 Java 配置类即可,而无需去改动业务 Java 代码。这大大降低了系统变更的风险和成本。
3.IOC/DI的具体使用场景
IoC/DI 的主要作用就是管理对象的实例化和对象之间依赖关系。项目中以前需要自己实例化的各种层对象、以及需要自己实例化的框架或工具包的入口类,都可以交给 Spring 容器进行管理。
- 层对象: 比如
PeopleMapper
接口的代理对象(通常由 Mybatis 生成并与 Spring 集成)、PeopleDaoImpl
(数据访问层实现类)、PeopleServiceImpl
(业务逻辑层实现类)、PeopleController
(控制层类)等这些不同层中的普通对象都可以交给 Spring 容器进行管理。这些放入到容器中的对象可以相互之间直接声明依赖,由 Spring 负责注入。 - 框架入口类: 像
SqlSessionFactory
(Mybatis 的核心工厂类)、Hibernate SessionFactory
(Hibernate 的核心工厂类)、DataSource
(数据库连接池)等这些第三方框架或工具包的入口类,它们通常需要复杂的初始化过程,也都可以交给 Spring 容器进行统一管理。
需要注意的特例: 需要注意的是,并非所有对象都能被 Spring 容器管理。例如,Servlet
只能被 Tomcat 或其他 Web 容器管理(由 Tomcat 帮助实例化和创建的),所以 Spring 容器是无法直接管理 Servlet
的生命周期的。然而,Servlet
可以在其内部通过 Spring 的 API 从 Spring 容器中取出它所需要的对象(即 Bean),从而实现业务逻辑的解耦。这体现了 Spring 容器的强大集成能力和灵活性。
4.开发环境的搭建
注意:JDK17,maven起码3.6+
目前spring版本(最新的GA版本):6.2.7
实现案例:创建Person类,让Spring帮我们管理对象。
在`pom.xml中添加spring的依赖。
Spring项目的最基本依赖包含:
- spring-context.jar。spring上下文依赖,它依赖了下面的四个jar。
- spring-core.jar。Spring 核心jar包。它依赖了spring-jcl.jar
- spring-aop.jar。Spring AOP面向切面编程的基本支持。
- spring-expression.jar。Spring的表达式语言支持。
- spring-beans.jar。Spring容器的bean管理,创建对象非常重要的包。
- spring-jcl.jar。内置的日志包,Spring 4版本时使用的还是common-logging.jar呢,但是从5开始Spring自己对日志进行了封装。
但是在Maven项目中想要使用Spring框架只需要在项目中导入spring-context就可以了,其他的jar包根据Maven依赖传递性都可以导入进来。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.kirsten</groupId>
<artifactId>IOCProject01</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>IOCProject01</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>6.2.7</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>