Spring
Spring
核心技术
Spring框架的控制反转IoC 容器。
Spring面相切面编程 AOP 技术。
此外还有Spring和AspectJ
IOC容器
Spring IoC容器和Bean简介
原本是 “对象自己找依赖”(比如 A 类要用到 B 类,A 自己 new B、自己找 B 的实例),现在是 “容器给对象送依赖”(A 不用管 B 怎么来,容器提前准备好 B,在创建 A 时主动 “塞” 给 A)—— 这种 “找依赖的权力从对象手里转到容器手里” 的反转,就是 IoC;而容器 “塞依赖” 的具体动作,就是 DI(依赖注入)
IoC 是 “设计原则”(核心思想是 “反转依赖控制权”),DI 是 “实现方式”(具体怎么把依赖给对象)—— 二者本质是同一概念的不同角度描述,Spring 用 DI 的方式实现了 IoC 原则。
一个对象(比如 A 类)要和其他对象(比如 B 类、C 类,也就是 A 的 “依赖”)合作,不用自己去创建或查找这些依赖,只需要 “明确告诉容器自己需要什么”—— 告诉的方式有 3 种:
- 构造参数:A 的构造方法里写
public A(B b) { ... }(告诉容器 “我需要 B”); - 工厂方法参数:如果 A 是通过工厂方法创建的,工厂方法里写
public static A createA(B b) { ... }(告诉容器 “创建我需要 B”); - 属性设置:A 里写
private B b;+ setter 方法public void setB(B b) { ... }(告诉容器 “我需要 B,创建后给我设进来”)。
Spring 的核心是 “IoC 容器”(可以理解为 “对象管家”),容器会提前创建好所有需要的依赖对象(比如 B、C),当容器创建 A(Spring 里的对象叫 “bean”)时,会按照 A 之前 “告诉” 的方式(构造参数 / 工厂参数 / 属性),把 B、C 主动 “塞” 到 A 里 —— 这个 “塞” 的动作就是 “依赖注入(DI)”。
- 这是最关键的一句,解释 “控制反转” 的 “反转” 到底是什么:
- 「传统方式(没有 IoC)」:Bean 自己控制依赖 → A 要用到 B,A 自己用
B b = new B();(直接构建),或者自己找个 “服务定位器” 查 B 的实例(比如B b = ServiceLocator.getB();)—— 控制权在 A 手里。 - 「IoC 方式」:容器控制依赖 → A 不用自己 new B、不用自己查 B,控制权转到了 IoC 容器手里 —— 这就是 “控制的反转”(从对象反转到容器)。
- 「传统方式(没有 IoC)」:Bean 自己控制依赖 → A 要用到 B,A 自己用
例子
IoC 方式(Spring 实现):容器控制依赖,注入给对象
1 | // DataBaseDao:还是原来的类,不用改 |
Spring IoC容器的两个核心组件
BeanFactory 和 ApplicationContext
Spring IoC 容器核心功能由两个接口支撑,他们的关系类似 基础班工具和升级版工具,ApplicationContext包含BeanFactory所有功能并且新增了更多特性。
1、BeanFactory接口
核心功能,能创建对象 Bean,组装对象之间的依赖关系,比如A 依赖B ,就会把 B 塞给 A ,不管对象是什么类型都能管。
2、ApplicationContext 接口
是BeanFactory 的 子接口,相当于继承了基础款的所有功能,是更强大的企业级 IoC容器。
新增了 AOP集成更方便:直接支持Spring AOP 不用额外复杂配置
国际化支持:能支持多语言消息 中文环境显示你好,英文环境显示Hello
事件发布:支持 事件通知 机制,比如某个对象状态变化时,自动通知其他关心这个变化的对象。
场景化扩展:针对特定场景提供专用容器,比如WebApplicationContext 专门给 Web 应用 比如Spring MVC 用,能更好地适配Web环境。
Bean的概念
Bean是Spring IoC容器管理的特殊对象,理解他的关系是区分 普通对象 和 Spring Bean
1、普通对象 vs Spring Bean
普通对象是自己new创建的对象,生命周期由自己控制,创建、销毁全靠代码 创建 -> 使用 -> 不可达 -> 销毁/回收。
Spring Bean:由Spring IoC容器,BeanFactory或者ApplicationContext负责实例化(创建对象),组装(处理依赖,比如给对象的属性赋值)、管理(控制对象的生命周期,比如什么时候创建,什么时候销毁)
2、Bean的 配置元数据
容器怎么知道要创建哪些 Bean、Bean 之间有什么依赖关系?靠 “配置元数据”—— 就是你告诉容器的 “清单”,比如:
- XML 配置文件(
<bean id="userService" class="com.xxx.UserService">...</bean>); - 注解(
@Component、@Service等,标记哪些类需要被容器管理); - Java 配置类(
@Configuration+@Bean注解,手动定义 Bean)。
这些元数据里会写明:要创建哪个类的对象、对象的依赖是谁、对象的初始化参数是什么等,容器照着 “清单” 干活。
IOC容器的概念
Spring IoC容器的工作机制
1、ApplicationContext 是 Spring IoC 容器的 实体代表,相当于一个智能工厂,核心职责有三个
- 实例化:按照规则创建应用程序需要的对象,比如Service、Dao等
- 配置:给对象设置属性,比如给UserService的name属性赋值
- 组装:处理对象之间的依赖 比如OrderService需要UserService,容器会自动把UserService 连接到 OrderService里
2、核心依赖,容器做这些事的依据是配置元数据,相当于给工程的生产清单,清单里写着:
- 要创建哪些类的对象,比如com.xxx.UserService
- 每个对象的属性怎么设置,比如UserService的timeout设置为3000
- 对象之间的依赖关系,比如OrderService 依赖 UserService
3、元数据格式:生产清单可以用3种形式写:
- xml 文件 传统方式:比如
<bean id="userService" class="com.xxx.UserService"/> - Java注解:比如在类上标@Service,告诉容器这个类要被管理
- Java代码:配置类的方式,比如用@Configuration + @Bean注解手动定义对象。
思路:
(1)定义xml文件,里面定义bean标签,因为后续每个bean标签会被解析为一个对象
(2)解析xml文件(dom4j),解析的时候,会解析xml文件中的bean标签,每个bean标签转换为一个bean对象,此对象包含两个属性:id、class,此对象用来存放bean的id和class值。
(3)因为xml文件中bean标签可能是多个,所以定义一个List集合,存储bean对象。
(4)遍历List集合,得到每一个bean对象,通过bean对象的class属性,反射创建对应的对象。
(5)对象创建好以后,将bean对象的id和反射创建的对象,放入map集合中。
(6)定义一个工厂,(2)-(5)步骤放在工厂的构造器中完成
(7)工厂中定义获取对象的方法,通过id从map集合中获取对象。
ApplicationContext的具体实现和使用场景
Spring提供了多个ApplicationContext的视线,就像工厂有 不同的生产线,适配不同的场景。
1、独立应用 非Web 常用的两种
- ClassPathXmlApplicationContext:从项目的 类路径 比如 src/main/resources目录 ,读取XML配置文件
- FileSystemXmlApplicationContext:从操作系统的文件系统路径 比如 D:/config/spring.xml 读取 xml 配置
2、Web应用:用专门的WebApplicationContext,适配Tomcat等Web 服务环境。
3、简化配置的技巧:
- 虽然XML是传统格式,但可以用少量XML配置 “开启注解支持” 比如
<context:component-scan>,之后主要使用@Service,@Autowired等注解管理Bean,不用写大量XML - 实际开发中几乎不用手动写代码创建容器:比如Web应用只需要在web.xml里加几行模版配置,容器会由 Web 服务器自动初始化
配置元数据
Spring IoC容器消费一种配置元数据。这种配置元数据代表了你,作为一个应用开发者,如何告诉Spring容器在你的应用中实例化、配置和组装对象。
基于XML的元数据并不是配置元数据的唯一允许形式。Spring IOC 容器本身与这种配置元数据的实际编写格式是完全解耦的。如今许多开发者用基于Java的配置
关于在Spring容器中使用其他形式的元数据的信息,请参见。
- 基于注解的配置 使用基于注解的配置元数据定义Bean。
- Java-based configuration 通过使用Java而不是XML文件来定义你的应用类外部的Bean。要使用这些特性,请参阅
@Configuration@Bean,@Import, 和@DependsOn注解。
Spring的配置包括至少一个,通常是一个以上的Bean定义,容器必须管理这些定义。基于XML的配置元数据将这些Bean配置为顶层 <beans/> 元素内的 <bean/> 元素。Java配置通常使用 @Configuration 类中的 @Bean 注解的方法。
这些Bean的定义对应于构成你的应用程序的实际对象。通常,你会定义服务层对象、持久层对象(如存储库或数据访问对象(DAO))、表现对象(如Web控制器)、基础设施对象(如JPA EntityManagerFactory)、JMS队列等等。通常,人们不会在容器中配置细粒度的domain对象,因为创建和加载domain对象通常是 repository 和业务逻辑的责任。
下面的例子显示了基于XML的配置元数据的基本结构。
1 |
|
id 属性是一个字符串,用于识别单个Bean定义。 |
|
|---|---|
class 属性定义了 Bean 的类型,并使用类的全路径名。 |
id 属性的值可以用来指代协作对象。本例中没有显示用于引用协作对象的XML。
实例化一个容器
配置好元数据了,那么就可以实例化一个容器然后就可以根据配置在容器中new对象了
在ApplicationContext 构造函数的一条或者多条路径是资源字符串,它让容器从各种外部资源(如本地文件系统、Java CLASSPATH)加载配置元数据
1 | ApplicationContext context = new ClassPathXmlApplicationContext("services.xml","daos.xml") |
然后接口层service的对象引用的持久层的dao层对象,然后dao层的对象引用具体的类,这样子的配置类有些许的不同,下面是例子
下面的例子显示了 service 对象(services.xml)配置文件。
1 |
|
下面的例子显示了数据访问对象(data access object) daos.xml 文件。
1 |
|
从上面的例子可以看出,在xml配置中,
service层的时候由于引用的是对象,所以就用 id 配置service的具体名称 和 class 来制定service的实现类路径,然后在标签下用property标签用name来重新自定义引用的dao的名称,用ref来确定 service用到的dao的名称
dao层的时候,直接用id来制定dao层的名称用class来制定dao层的类路径。
拆分、组织Spring的XML配置元数据
当项目的xml文件过多的时候,为了让复杂的项目配置更清晰,设置了几个规则,核心目的就是拆分,组织xml配置元数据,而不是把所有的Bean定义写在一个XML,臃肿难以维护。
两种整合多XML配置的方式
1、创建ApplicationContext 容器 时,直接传入所有XML文件的路径,容器会自动合并所有Bean定义
1 | // 示例:加载“服务层”和“数据层”的2个XML文件 |
2、用<import/> 标签导入(更常用)
在一个 ’主XML配置文件‘ 中,通过<import resource="文件路径"/>标签导入其他XML,相当于把多个XML ‘合并’成一个
主配置文件(如applicationContext.xml)
1 | <beans> |
容器只用加载主xml,就能自动加载所有导入的子XML。
XML导入的路径规则
首先classpath是Java运行时JVM用来查找类文件.clssh和资源文件xml等的路径集合
1、spring项目默认会给一个resources文件,在该文件夹下默认就是classpath路径,也就是说如果把xml放在里面就直接写文件名就好了不用配置文件路径。
2、如果需要配置就需要配置一下项目路径,然后不要写前导斜线
用`$ {..}这样的占位符语法,在运行时读取JVM系统属性,动态拼接路径.
那么这个占位符的值可以从多个来源获取,优先级如下
1、JVM系统属性在启动命令的时候 java -Dxxx xxx.jar这样启动的时候
2、操作系统变量
3、.yml配置文件
在spring中需要配置占位符解析,在springboot中自动启用,无需配置。
用context命名空间开启注解扫描,在xml里面配置
1 | <!-- 1. 头部声明context命名空间和对应的Schema --> |
来开启扫描功能。
使用容器
现在注册好了,又配置扫描好了,这样所有的配置已经准备就绪开始使用了
1 | // 创建和配置bean |
Bean概览
Bean包含的核心信息与容器的关系
容器的核心功能是管理Bean,容器内部通过BeanDefinition对象 存储Bean的定义信息,相当于Bean的数字档案,记录了创建和管理Bean的所有关键参数。
| 类别 | 核心属性 | 作用说明 |
|---|---|---|
| Bean 的 “身份标识” | Class、Name | - Class:Bean 的全路径类名(如com.example.UserService),容器反射创建实例- Name:Bean 的唯一标识符(如 XML 的id),用于容器内引用 |
| Bean 的 “行为规则” | Scope、Lazy initialization mode | - Scope:Bean 的作用域(如单例singleton、原型prototype),决定实例数量和生命周期- Lazy:是否懒加载(容器启动时不创建,首次使用时创建) |
| Bean 的 “依赖关系” | Constructor arguments、Properties、Autowiring mode | - 前两者:手动指定依赖(构造参数 / 属性注入)- Autowiring mode:自动装配规则(如按类型 / 名称注入依赖),减少手动配置 |
| Bean 的 “生命周期” | Initialization method、Destruction method | - 初始化方法:Bean 实例化后执行(如资源初始化)- 销毁方法:Bean 销毁前执行(如资源释放) |
1、Class 全路径类名,容器通过反射创建实例
2、Name 名称,Bean的唯一表示,用于容器内区分和引用
3、Scope 作用域 ,Bean的生命周期范围,比如单例singleton、原型prototype,决定容器创建Bean的实例数量和存活时间
4、构造参数/属性 ,一来注入的关键信息,指定Bean创建需要的参数或者属性值,以及依赖的其他Bean合作者
5、Autowiring mode自动装配模式 ,容器自动匹配并注入依赖的规则(如按类型、按名称),减少手动配置。
6、懒加载模式, 指定Bean 是否在容器启动时创建(默认立即创建),还是首次使用时才创建。
7、初始化/销毁方法, Bean生命周期回调方法,分别在Bean初始化完成后,销毁前执行




