一、框架前言
1、总体技术体系
- 单一架构
一个项目,一个工程,导出为一个war包,在一个Tomcat上运行。也叫all in one.

单一架构,项目主要应用技术框架为:Spring,SpringMVC,Mybatis等
- 分布式架构
一个项目(对应IDEA中的一个 project),拆分成很多个模块,每个模块是一个IDEA中的一个module。每一个工程都是运行在自己的Tomcat上。模块之间可以互相调用。每一个模块内部可以看成是一个单一架构的应用。
分布式架构,项目主要应用的技术框架:SpringBoot,SpringCloud,中间件等
2、框架概念和理解
框架(Framework)是一个集成了基本结构、规范、设计模式、编程语言和程序库等基础组件的软件系统,它可以用来构建更高级别的应用程序。框架的设计和实现旨在解决特定领域中的常见问题,帮助开发人员更高效、更稳定地实现软件开发目标。
站在文件结构的角度理解框架,可以将框架总结:框架 = jar包 + 配置文件

总之,框架已经对基本的代码进行了封装并提供相应的API,开发者在使用框架是直接调用封装好的API可以省去很多代码编写,从而提高工作效率和开发速度。
二、SpringFramework简介
1、Spring 和 SpringFramework
广义上的Spring:Spring技术栈(全家桶)
广义上的Spring泛指以Spring Framework为基础的Spring技术栈。
经过十多年的发展,Spring已经不再是一个单纯的应用框架,而是逐渐发展成为一个由多个不同子项目(模块)组成的成熟技术,例如Spring Framework、SpringMVC、SpringBoot、Spring Cloud、Spring Data、Spring Security等,其中Spring Framework 是其他子项目的基础。
这些子项目涵盖了从企业级应用开发到云计算等各个方面的内容,能够帮助开发人员解决软件开发过程中不断产生的各种实际问题,给开发人员带来更好的开发体验。
狭义的Spring:Spring Framework(基础框架)
狭义的Spring 特指Spring Framework,通常我们将它称为Spring框架。
Spring Framework(Spring框架)是一个开源的应用程序框架,由SpringSource公司开发,最初为了解决企业级开发中各种常见问题而创建的。它提供了很多功能,例如:依赖注入(Dependency Injection)、面向切面编程(AOP)、声明式事务管理(TX)等。其主要目标是企业级应用程序的开发变得更加简单和快速,并且Spring 框架被广泛应用于Java企业开发领域。
Spring全家桶的其他框架是以SpringFramework框架为基础!
2、SpringFramework主要功能模块
| 功能模块 | 功能介绍 |
|---|---|
| Core Container | 核心容器,控制反转和依赖注入 |
| AOP&Aspects | 面向切面编程 |
| TX | 声明式事务管理 |
| Testing | 快速整合测试环境 |
| Data Access/Integration | 提供了对数据访问/集成的功能。 |
| Spring MVC | 提供了面向Web应用程序的集成功能。 |

3、Spring主要优势
生态好
三、Spring IoC容器概念
1、组件和组件管理概念
什么是组件?
组件承担着程序不可获缺的功能,像工具类util,实体类pojo都不是组件,租件一定是对象,对象不一定是组件。
整个项目就是由各个组件搭建而成的:

Spring充当组件管理角色(IoC)
组件可以完全交给Spring框架进行管理,Spring框架代替了程序员原有的new对象和对象赋值动作。
Spring具体的组件管理动作包含:
- 组件对象实例化
- 组件属性辅赋值
- 组件对象之间的引用
- 组件对象存活周期管理
- ......我们只需要编写元数据(配置文件)告知Spring管理哪些类组件和他们的管理即可!注意:组件是映射到应用程序中所有可重用组件的Java对象,应该是可复用的功能对象!
- 组件一定是对象,对象不一定是组件
- 综上所述,Spring充当一个组件容器,创建、管理、存储组件,减少了我们的编码压力,让我们更加专注进行业务编写。
2、Spring IoC容器和容器实现
Spring IoC 容器,负责实例化、配置和组装bean(组件)。容器通过读取配置元数据来获取有关要实例化、配置和组装组件的指令。配置元数据以 XML、注解表现。它允许表达组成应用程序的组件以及这些组件之间丰富的互相依赖关系。

SpringIoC容器接口:
org.springframework.beans和org.springframework.context包是Spring Framework的IoC容器的基础包。BeanFactory接口提供了一种高级配置机制,能够管理任何类型的对象,它是SpringIoC容器标准化超接口。
ApplicationContext是BeanFactory的子接口。
- 更容易与Spring的AOP功能集成
- 消息资源处理(用于国际化)
- 特定于应用程序给予此接口实现,例如Web应用程序的WebApplicationContext。简而言之,BeanFactory提供了配置框架和基本功能,而ApplicationContext添加了更多特定于企业的功能。ApplicationContext是BeanFactory的完整超集!
ApplicationContext 容器实现类:
| 类型名 | 简介 |
|---|---|
| ClassPathXmlApplicationContext | 通过读取类路径下的 XML 格式的配置文件创建 IOC 容器对象 |
| FileSystemXmlApplicationContext | 通过文件系统路径读取 XML 格式的配置文件创建 IOC 容器对象 |
| AnnotationConfigApplicationContext | 通过读取Java配置类创建 IOC 容器对象 |
| WebApplicationContext | 专门为 Web 应用准备,基于 Web 环境创建 IOC 容器对象,并将对象引入存入 ServletContext 域中。 |
SpringIoC 容器管理配置方式:
Spring IoC 容器使用多种形式的配置元数据。此配置元数据表示您作为应用程序开发人如何告诉 Spring 容器实例化、配置和组装应用程序的对象。
Spring框架提供了多种配置方式:XML配置方式、注解方式和Java配置类方式
3、Spring IoC/DI概念总结
- IoC容器
Spring IoC 容器,负责实例化、配置和组装bean(组件)。容器通过读取配置元数据来获取有关要实例化、配置和组装组件的指令。
- IoC(Inversion of Control)控制反转
IoC 主要是针对对象的创建和调用控制而言的,也就是说,当应用程序需要使用一个对象时,不再是应用程序直接创建该对象,而是由IoC 容器来创建和管理,即控制权由应用程序转移到 IoC 容器中,也就是“反转”了控制权。这种方式基本上是通过依赖查找的方式来实现的,即 IoC 容器维护着构成应用程序的对象,并负责创建这些对象。
- DI(Dependency Injection)依赖注入
DI 是指在组件之间传递依赖关系的过程中,将依赖关系在容器内部进行处理,这样就不必在应用程序代码中硬编码对象之间的依赖关系,实现了对象之间的解耦合。在Spring中,DI 是通过XML配置文件或注解的方式实现的。它提供了三种形式的依赖注入:构造函数注入、Setter方法注入和接口注入。
四、 Spring IoC / DI 实现
1、Spring IoC / DI 实现步骤
1)配置元数据(配置)
配置元数据,既是编写交给 Spring IoC 容器管理组件的信息,配置方式有三种。
基于 XML 的配置元数据的基本结构:
<?xml version="1.0" encoding="UTF-8"?>
<!-- 此处要添加一些约束,配置文件的标签并不是随意命名 -->
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttps://www.springframework.org/schema/beans/spring-beans.xsd"><bean id="h1" [1] class="com.xxx.xxx.HappyComponent" [2]> </bean><bean id="..." class="..."></bean>
</beans>
Spring IoC 容器管理一个或多个组件,这些组件是使用你提供给容器的配置元数据创建的。
<bean/>标签 == 组件信息声明
id 属性是标识单个 Bean 定义的字符串
class 属性定义 Bean 的类型并使用完全限定的类名
2)实例化 IoC 容器
提供给 ApplicationContext 构造函数的位置路径是资源字符串地址,允许容器从各种外部资源(如本地文件系统、JavaClassPath等)加载配置元数据。
ApplicationContext ioc = new ClassPathXmlApplicationContext("spring.xml");
3)使用容器,获取Bean(组件)
//创建ioc容器对象,指定配置文件,ioc也开始实例组件对象
ApplicationContext ioc = new ClassPathXmlApplicationContext("spring.xml");
//获取ioc容器的组件对象
HappyComponent happyComponent = ioc.getBean("h1",HappyComponent.class);
//使用组件对象
happyComponent.doWork();
2、基于XML方式管理Bean
1)实验一:声明配置文件和创建容器
①创建maven工程
②导入SpringIoC相关依赖
③准备组件类
④创建SpringIoC配置(XML)
⑤创建IoC容器
⑥获取Bean
2)实验二:获取Bean
①根据 id 获取
//方式1: 根据id获取
//没有指定类型,返回为Object,需要类型转化!
HappyComponent happyComponent = (HappyComponent) iocContainer.getBean("h1");
happyComponent.doWork();
②根据类型获取
//方式2: 根据类型获取
//根据类型获取,但是要求,同类型(当前类,或者之类,或者接口的实现类)只能有一个对象交给IoC容器管理
//配置两个或者以上出现: org.springframework.beans.factory.NoUniqueBeanDefinitionException 问题
HappyComponent happyComponent = iocContainer.getBean(HappyComponent.class);
happyComponent.doWork();
③根据 id 和类型获取
//方式3: 根据id和类型获取
HappyComponent happyComponent = iocContainer.getBean("h1", HappyComponent.class);
happyComponent.doWork();
总结:根据类型来获取 bean 时,在满足 bean 唯一性的前提下,其实只要看:【对象 instanceof 指定的类型】的返回结果,只要返回的是 true 就可以认定为和类型匹配,能够获取到。
3)实验三:Bean属性赋值:setter注入
①组件类添加属性(必须配置set方法,属性设置,ioc容器是通过set方法调用,进行属性赋值)
public class HappyComponent {//添加属性private String componentName;public String getComponentName() {return componentName;}//必须配置set方法,属性设置,ioc容器是通过set方法调用,进行属性赋值!!!public void setComponentName(String componentName) {this.componentName = componentName;}public void doWork() {System.out.println("HappyComponent.doWork");}
}
②配置时给属性指定值
<!-- 实验三 [重要]给bean的属性赋值:setter注入 -->
<bean id="happyComponent3" class="com.atguigu.ioc.HappyComponent"><!-- property标签:通过组件类的setXxx()方法给组件对象设置属性 --><!-- name属性:指定属性名(这个属性名是getXxx()、setXxx()方法定义的,和成员变量无关) --><!-- value属性:指定属性值 --><property name="componentName" value="veryHappy"/>
</bean>
③测试属性输出
@Test
public void testExperiment03() {//创建IoC容器,并读取配置文件 注意: 构造函数是可变参数,可以传入一个或者多个配置ApplicationContext iocContainer = new ClassPathXmlApplicationContext("spring-bean-03.xml");HappyComponent happyComponent = iocContainer.getBean("happyComponent3", HappyComponent.class);System.out.println(happyComponent.getComponentName());
}
4)实验四:Bean属性赋值:引用其他Bean
①声明新组件
public class HappyMachine {private String machineName;public String getMachineName() {return machineName;}public void setMachineName(String machineName) {this.machineName = machineName;}
}
②原组件引用新组件
public class HappyComponent {//添加属性private String componentName;public String getComponentName() {return componentName;}//必须配置set方法,属性设置,ioc容器是通过set方法调用,进行属性赋值!!!public void setComponentName(String componentName) {this.componentName = componentName;}//引用新组件private HappyMachine happyMachine;public HappyMachine getHappyMachine() {return happyMachine;}public void setHappyMachine(HappyMachine happyMachine) {this.happyMachine = happyMachine;}public void doWork() {System.out.println("HappyComponent.doWork");}
}
③配置新组件
<bean id="happyMachine" class="com.atguigu.ioc.HappyMachine"><property name="machineName" value="makeHappy"/>
</bean>
④组件之间引用配置
<bean id="happyComponent3" class="com.atguigu.ioc.HappyComponent"><!-- property标签:通过组件类的setXxx()方法给组件对象设置属性 --><!-- name属性:指定属性名(这个属性名是getXxx()、setXxx()方法定义的,和成员变量无关) --><!-- value属性:指定属性值 --><property name="componentName" value="veryHappy"/><!-- ref 属性:通过 bean 的 id 引用另一个 bean --><property name="happyMachine" ref="happyMachine"/>
</bean>
⑤测试
@Test
public void testExperiment04() {ApplicationContext iocContainer = new ClassPathXmlApplicationContext("spring-bean-04.xml");HappyComponent happyComponent = iocContainer.getBean("happyComponent4", HappyComponent.class);//获取另一个beanSystem.out.println(happyComponent.getHappyMachine().getMachineName());
}
注意事项:
- 声明 bean,不分先后顺序,spring 容器内部有缓存机制,先实例化后属性赋值
- ref 容易错写成 value,会抛出Caused by: java.lang.IllegalStateException: Cannot convert value of type 'java.lang.String' to required type 异常!
- 只有声明到 IoC 容器,方可被其他 bean 引用!
5)实验五:Bean属性赋值:内部Bean声明
声明内部bean配置
在bean里面配置的bean就是内部bean,内部bean只能在当前bean内部使用,在其他地方不能使用。
<!-- 实验五 [重要]给bean的属性赋值:内部bean -->
<bean id="happyComponent5" class="com.atguigu.ioc.HappyComponent"><property name="happyMachine"><!-- 在一个 bean 中再声明一个 bean 就是内部 bean --><!-- 内部 bean 可以直接用于给属性赋值,可以省略 id 属性 --><bean class="com.atguigu.ioc.HappyMachine"><property name="machineName" value="makeHappy"/></bean></property>
</bean>
6)实验六:Bean属性赋值:引入外部Properties配置参数
①实现目标
将 Druid 连接池对象交给 SpringIoC 容器管理。
②加入数据依赖
<!-- 数据库驱动 和 连接池-->
<dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.25</version>
</dependency>
<dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.2.8</version>
</dependency>
③创建外部属性文件
文件位置:resources / jdbc.properties
# 配置成你的数据信息
jdbc.user=root
jdbc.password=root
jdbc.url=jdbc:mysql:///数据库名
jdbc.driver=com.mysql.cj.jdbc.Driver
④引入属性文件
<!-- 引入外部属性文件 -->
<context:property-placeholder location="classpath:jdbc.properties"/>
在IDEA中引入Spring配置文件中名称空间。
⑤配置连接池信息
<!-- 实验六 [重要]给bean的属性赋值:引入外部属性文件 -->
<bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource"><property name="url" value="${jdbc.url}"/><property name="driverClassName" value="${jdbc.driver}"/><property name="username" value="${jdbc.user}"/><property name="password" value="${jdbc.password}"/>
</bean>
⑥读取测试
@Test
public void testExperiment06() throws SQLException {ApplicationContext iocContainer = new ClassPathXmlApplicationContext("spring-bean-06.xml");DataSource dataSource = iocContainer.getBean(DataSource.class);Connection connection = dataSource.getConnection();System.out.println("connection = " + connection);
}
7)实验七:高级特性:FactoryBean特性
①FactoryBean简介
FactoryBean 接口是 Spring IoC 容器实例化逻辑的可插拔性。定义了如何创建对象的接口。它允许你自定义对象的实例化过程,提供了一种灵活的方式来创建和获取对象。用于配置复杂的Bean对象,可以将创建过程存储在 FactoryBean 的 getObject 方法!
FactoryBean<T> 接口提供三种方法:
- T getObject():返回此工厂创建的对象的实例。该返回值会被存储到IoC容器!
- boolean isSingleton():如果此 FactoryBean 返回单例,则返回 true ,否则返回 false 。此方法的默认实现返回 true (注意,lombok插件使用,可能影响效果)。
- Class<?> getObjectType():
返回 getObject() 方法返回的对象类型,如果事先不知道类型,则返回 null 。
②FactoryBean使用场景
a.代理类的创建
b.第三方框架整合
c.复杂对象实例化等
③FactoryBean应用
a.准备FactoryBean实现类
// 实现FactoryBean接口时需要指定泛型
// 泛型类型就是当前工厂要生产的对象的类型
public class HappyFactoryBean implements FactoryBean<HappyMachine> {private String machineName; public String getMachineName() {return machineName;} public void setMachineName(String machineName) {this.machineName = machineName;} @Overridepublic HappyMachine getObject() throws Exception { // 方法内部模拟创建、设置一个对象的复杂过程HappyMachine happyMachine = new HappyMachine(); happyMachine.setMachineName(this.machineName); return happyMachine;} @Overridepublic Class<?> getObjectType() { // 返回要生产的对象的类型return HappyMachine.class;}
}
b.配置FactoryBean实现类
<!-- FactoryBean机制 -->
<!-- 这个bean标签中class属性指定的是HappyFactoryBean,但是将来从这里获取的bean是HappyMachine对象 -->
<bean id="h1" class="com.atguigu.ioc.HappyFactoryBean"><!-- property标签仍然可以用来通过setXxx()方法给属性赋值 --><property name="machineName" value="张三"/>
</bean>
c.测试读取FactoryBean和FactoryBean.getObject对象
@Test
public void testExperiment07() {ApplicationContext ioc = new ClassPathXmlApplicationContext("spring-bean-07.xml");//注意: 直接根据声明FactoryBean的id,获取的是getObject方法返回的对象HappyMachine happyMachine = ioc.getBean("h1",HappyMachine.class);System.out.println("happyMachine = " + happyMachine);//如果想要获取FactoryBean对象, 直接在id前添加&符号即可! &happyMachine7 这是一种固定的约束Object bean = ioc.getBean("&h1");System.out.println("bean = " + bean);
}
④FactoryBean和BeanFactory区别
FactoryBean 是 Spring 中一种特殊的 bean,可以在 getObject() 工厂方法自定义的逻辑创建Bean!是一种能够生产其他 Bean 的 Bean。FactoryBean 在容器启动时被创建,而在实际使用时则是通过调用 getObject() 方法来得到其所生产的 Bean。因此,FactoryBean 可以定义任何所需的初始化逻辑,生产出一些定制化的 bean.
一般情况下,整合第三方框架,都是通过定义 FactoryBean 实现!!!
8)实验八:高级特性:Bean的作用域
①Bean作用域概念
<bean 标签声明Bean,只是将 Bean 的信息配置给 SpringIoC 容器!
在IoC容器中,这些 <bean 标签对应的信息转成 Spring 内部 BeanDefinition 对象,BeanDefinition 对象内,包含定义的信息(id,class,属性等等)这意味着,BeanDefinition 与类概念一样,SpringIoC 容器可以根据 BeanDefinition 对象反射创建多个 Bean 对象实例。具体创建多少个 Bean 的实例对象,由 Bean 的作用域 Scope 属性指定。
默认情况:我们全局只需要实例化一个 Bean 对象,绝大情况我们也仅需创建一个对象!
②作用域配置和测试
配置scope范围
<!--bean的作用域 -->
<!-- scope属性:取值singleton(默认值),bean在IOC容器中只有一个实例,IOC容器初始化时创建对象 -->
<!-- scope属性:取值prototype,bean在IOC容器中可以有多个实例,getBean()时创建对象 -->
<bean id="happyMachine8" scope="prototype" class="com.atguigu.ioc.HappyMachine"><property name="machineName" value="happyMachine"/>
</bean>
<bean id="happyComponent8" scope="singleton" class="com.atguigu.ioc.HappyComponent"><property name="componentName" value="happyComponent"/>
</bean>
测试读取
@Test
public void testExperiment08() {ApplicationContext iocContainer = new ClassPathXmlApplicationContext("spring-bean-08.xml");HappyMachine bean = iocContainer.getBean(HappyMachine.class);HappyMachine bean1 = iocContainer.getBean(HappyMachine.class);//多例对比 falseSystem.out.println(bean == bean1);HappyComponent bean2 = iocContainer.getBean(HappyComponent.class);HappyComponent bean3 = iocContainer.getBean(HappyComponent.class);//单例对比 trueSystem.out.println(bean2 == bean3);
}
9)实验九:高级特性:Bean的生命周期
①理解Bean的生命周期作用
Spring Framework的Bean生命周期是指一个Bean对象从它的创建、初始化到销毁的整个过程。
理解Spring Bean 的生命周期可以帮助开发者更好地管理Bean,可以实现以下目的:
a.避免重复初始化Bean,提高Bean实例化的效率;
b.在Bean初始化前后做些额外的处理,如日志记录、权限检查等;
c.实现自定义的操作,如在Bean销毁时释放资源、设置缓存等;
d.理解Bean的整个生命周期有助于排查问题,提高应用程序的可维护性;
e.理解Spring AOP等功能的实现原理,并参与定制过程;
②Bean生命周期清单和步骤内容

Spring Bean 的生命周期指从 Spring 容器创建 Bean 实例开始,到 Bean 销毁的整个过程,可以按照以下几个阶段:
-
实例化Bean实例:Spring 容器使用指定的实例化策略创建 bean,该策略可以是无参构造、工厂方法等。当 Spring 加载 Bean 的配置文件并且 Bean 标签被解析后,这个类(Bean)就会被实例化。
-
Bean实例属性设置:Spring 通过调用 setter 方法或直接设置字段的方式来注入 Bean 的属性。
-
Aware 相关接口的回调:Spring 通过 Aware 接口来把一些 Bean 相关的资源注入到 Bean 中。例如,BeanNameAware 接口可获取到 Bean 的名称;ApplicationContextAware 接口可获取到 ApplicationContext 对象实例;BeanFactoryAware 接口可获取到 BeanFactory 对象实例等。
-
Bean初始化前的操作:在 Bean 的初始化之前,Spring 允许用户自定义 Bean 实例化后的一些操作。
-
如果有BeanPostProcessor注册,先执行beforeInitialization()方法;
-
如果Bean实现了InitializingBean接口,则执行afterPropertiesSet()方法
-
-
Bean 的初始化方法调用:如果在配置文件中使用init-method属性声明了初始化方法,则执行该方法;
-
Bean初始化后的操作:在 Bean 的初始化之后,如果有BeanPostProcessors注册,执行afterInitialization()方法;
此方法中,Bean实例已经完成了实例化和初始化工作,最终会将afterInitialization()方法修改后返回的对象存储到IoC容器中!
Spring Aop的实现,通过定义BeanPostProcessor(AbstractAutoProxyCreator),在后置方法中添加动态代理技术,进行Bean的动态代理对象生成!
-
使用 Bean:即在 IoC 容器中调用 getBean() 方法获取 Bean 实例,使用 Bean 的过程。
-
销毁 Bean:当 Bean 不再被使用时,Spring 容器会自动释放 Bean 占有的资源,关闭 IoC 容器。 开发人员可以自己实现 DisposableBean 接口或者为 Bean 配置一个指定的 destroy-method 方法来实现自定义销毁的逻辑。
-
关闭IoC容器 在整个生命周期过程中,Spring 提供了各种监听器和钩子函数,允许开发人员在不同的 Bean 生命周期阶段添加自己的处理逻辑以实现更加灵活和智能的控制。
③参与Bean生命周期定义
a.测试BeanPostProcessor 接口
public class MyBeanPostProcessor implements BeanPostProcessor {@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {System.out.println("HappyBeanPostProcessor的before执行了");return bean;}@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {System.out.println("HappyBeanPostProcessor的after执行了");return bean;}
}
b.测试 init-method / destroy-method 配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"><bean id="h1" class="com.atguigu.spring.pojo.HappyComponent" init-method="init" destroy-method="destroy"><property name="componentName" value="张三"/></bean><!--创建后置处理器对象后置处理器对象针对的是当前整个容器中的所有对象的init方法的前后--><bean id="happyBeanPostProcessor" class="com.atguigu.spring.processor.HappyBeanPostProcessor"/>
</beans>
c.测试读取配置即可
@Testpublic void testLifeCycle(){ClassPathXmlApplicationContext ioc = new ClassPathXmlApplicationContext("spring-09.xml");HappyComponent h1 = ioc.getBean("h1", HappyComponent.class);System.out.println("4、使用:"+h1);ioc.close();//关闭的是容器,容器在关闭之前,统一销毁所有的单例对象}
执行结果:

