简述
像 spring 这样比较成熟的框架,都会提供相对完善的参考文档,那么我们学习的第一步就是根据参考文档来学习。
如下图标红部分,我们先来学习核心部分。
核心技术主要包括:
- IoC Container
- AOP
- Resources
- Validation
- Data Binding
- Type Conversion
- Events
- SpEL
- i18n
IoC 和 AOP 两大核心技术是 spring 经久不衰的基本保证,
蓝色部分为我们必须深入学习的,橙色部分根据后续需要进行补充或各位看官自行梳理
接下来我们将首先学习 IoC Container。
IoC Container
什么是 IoC 容器?
Spring Framework实现的控制反转(IoC)原理。 IoC也称为依赖注入(DI)。
这是一个过程,通过这个过程,对象定义它们的依赖关系,
即它们使用的其他对象,只能通过构造函数参数,工厂方法的参数,或者在构造或从工厂方法返回后在对象实例上设置的属性。
然后容器在创建bean时注入这些依赖项。 这个过程基本上是相反的,因此名称Inversion of Control(IoC),
bean本身通过使用类的直接构造或诸如Service Locator模式之类的机制来控制其依赖关系的实例化或位置。
这里我们来使用代码看一下什么是控制反转?
既然有控制反转,那么我们先要明白什么是控制正转
控制正转案例
控制反转案例
这里请参见 java 控制反转和依赖注入的理解
IoC 容器是怎么管理 bean 对象
在Spring中,构成应用程序主干并由Spring IoC容器管理的对象称为bean。 bean是一个由Spring IoC容器实例化,组装和管理的对象。否则,bean只是应用程序中众多对象之一。 Bean及其之间的依赖关系反映在容器使用的配置元数据中。
由上图可知:主要有四个节点
-
Configuration Metadata 配置元数据
- 描述 bean 的信息
-
POJOs 业务对象
- bean 类的定义
-
IoC container IoC 容器
- IoC 容器载入配置元数据进行 bean 对象的处理
-
Ready for Use 可共使用的对象
- IoC 容器将维护处理过的 bean 信息以供系统使用
有以上四个节点引出几个问题:
- 配置元信息如何描述 bean 信息
- bean 类的定义,属性,特点
- IoC 容器如何根据配置元信息进行 bean 对象的处理
- 如何使用 IoC 容器维护的 bean 对象
整体思路就是层层递进,不断提出问题,然后解决问题,形成系统化的知识体系。
Configuration metadata 配置元信息
Spring IoC容器使用一种配置元数据形式; 此配置元数据表示您作为应用程序开发人员如何告诉Spring容器在应用程序中实例化,配置和组装对象。
配置元数据可以由 xml、java config、groovy
如果有需要你也可以使用 json 来进行描述,不过要实现相应的解析器,
作用就是让元信息描述的功能起作用。
IoC 容器类型
IoC 容器基础包
the basis packages for Spring Framework’s IoC container
- org.springframework.beans
- org.springframework.context
两种核心容器
-
BeanFactory
- BeanFactory接口提供了一种能够管理任何类型对象的高级配置机制。
- BeanFactory提供配置框架和基本功能
-
ApplicationContext
- ApplicationContext是BeanFactory的子接口
- ApplicationContext是BeanFactory的完整超集,添加了更多特定于企业的功能
- 它增加了与Spring的AOP功能的更容易的集成
- 消息资源处理(用于国际化)
- 事件发布
- 特定于应用程序层的上下文,例如WebApplicationContext,用于Web应用程序
两种核心容器区别与联系
- ApplicationContext接口,它由BeanFactory接口派生而来,因而提供BeanFactory所有的功能。ApplicationContext以一种更向面向框架的方式工作以及对上下文进行分层和实现继承
- BeanFactroy采用的是延迟加载形式来注入Bean的,即只有在使用到某个Bean时(调用getBean()),才对该Bean进行加载实例化,这样,我们就不能发现一些存在的spring的配置问题。而ApplicationContext则相反,它是在容器启动时,一次性创建了所有的Bean。这样,在容器启动时,我们就可以发现Spring中存在的配置错误。
- BeanFactory和ApplicationContext都支持BeanPostProcessor、BeanFactoryPostProcessor的使用,但两者之间的区别是:BeanFactory需要手动注册,而ApplicationContext则是自动注册
传送门:
容器的实例化
传统从xml文件中加载解析配置元信息进行容器的实例化
- ClassPathXmlApplicationContext 从 classpath 路径下加载元数据
- FileSystemXmlApplicationContext 从文件系统中加载元数据
现在流行的就是使用 annotation 形式 java config
- AnnotationConfigApplicationContext 从 注解配置文件中加载元信息
还提供了一些其他的例如 使用 Groovy
- GenericGroovyApplicationContext 从 Groovy 文件中加载元信息
如果有需要可以自定义实现 使用 json, 这个 spring 框架没有提供需要自己定义实现
- GenericJsonApplicationContext 从 json 文件中加载元信息
Bean
Bean 的属性
属性 | 描述 |
---|---|
class | bean 对应的类 |
name | bean 的名称 |
scope | bean 的范围 |
constructor arguments | 构造参数 |
properties | 属性 |
autowiring mode | 注入模式 |
lazy-initialization mode | 懒加载初始化模式 |
initialization method | 初始化方法 |
destruction method | 销毁方法 |
官方有这样一句提示:
需要尽早注册Bean元数据和手动提供的单例实例,以便容器在自动装配和其他内省步骤期间正确推理它们。 虽然在某种程度上支持覆盖现有元数据和现有单例实例,但是在运行时注册新bean(与对工厂的实时访问同时)并未得到官方支持,并且可能导致bean容器中的并发访问异常和/或不一致状态。
Bean 的命名
Bean 标识符
- 每个bean都有一个或多个标识符。
- 这些标识符在托管bean的容器中必须是唯一的。
- bean通常只有一个标识符,但如果它需要多个标识符,则额外的标识符可以被视为别名。
Bean 命名约定
- 惯例是在命名bean时使用标准Java约定作为实例字段名称。 也就是说,bean名称以小写字母开头,从那时起就是驼峰式的。
- 通过类路径中的组件扫描,Spring按照上述规则为未命名的组件生成bean名称:实质上,采用简单的类名并将其初始字符转换为小写。 但是,在(不常见的)特殊情况下,当有多个字符并且第一个和第二个字符都是大写字母时,原始外壳将被保留。 这些规则与java.beans.Introspector.decapitalize(Spring在此处使用)定义的规则相同。
Bean scopes
Spring Framework支持六个范围,其中四个范围仅在您使用Web感知ApplicationContext时才可用。
范围 | 说明 |
---|---|
singleton | (默认)将每个Spring IoC容器的单个bean定义范围限定为单个对象实例。 |
prototype | 将单个bean定义范围限定为任意数量的对象实例。 |
request | 将单个bean定义范围限定为单个HTTP请求的生命周期; 也就是说,每个HTTP请求都有自己的bean实例,它是在单个bean定义的后面创建的。 仅在Web感知Spring ApplicationContext的上下文中有效。 |
session | 将单个bean定义范围限定为HTTP会话的生命周期。 仅在Web感知Spring ApplicationContext的上下文中有效。 |
application | 将单个bean定义范围限定为ServletContext的生命周期。 仅在Web感知Spring ApplicationContext的上下文中有效。 |
websocket | 将单个bean定义范围限定为WebSocket的生命周期。 仅在Web感知Spring ApplicationContext的上下文中有效。 |
singleton
只管理单个bean的一个共享实例,并且对具有与该bean定义匹配的id或id的bean的所有请求都会导致Spring容器返回一个特定的bean实例。
换句话说,当您定义一个bean定义并且它的范围是一个单例时,Spring IoC容器只创建该bean定义定义的对象的一个实例。 此单个实例存储在此类单例bean的缓存中,并且该命名Bean的所有后续请求和引用都将返回缓存对象。
prototype
bean的非单例原型范围部署导致每次发出对该特定bean的请求时都会创建一个新的bean实例。 也就是说,bean被注入另一个bean,或者通过对容器的getBean()方法调用来请求它。 通常,对所有有状态bean使用原型范围,对无状态bean使用单例范围。
与其他作用域相比,Spring不管理原型bean的完整生命周期:容器实例化,配置和组装原型对象,并将其交给客户端,而不再记录该原型实例。因此,尽管无论范围如何都在所有对象上调用初始化生命周期回调方法,但在原型的情况下,不会调用已配置的销毁生命周期回调。客户端代码必须清理原型范围的对象并释放原型bean所持有的昂贵资源。要让Spring容器释放原型范围内的bean所拥有的资源,请尝试使用自定义bean后处理器,它包含对需要清理的bean的引用。
在某些方面,Spring容器关于原型范围bean的角色是Java new运算符的替代品。超过该点的所有生命周期管理必须由客户端处理。 (有关Spring容器中bean的生命周期的详细信息,请参阅Lifecycle回调。)
Bean 的实例化
bean定义本质上是用于创建一个或多个对象的配方。 容器在被询问时查看命名bean的配方,并使用由该bean定义封装的配置元数据来创建(或获取)实际对象。
实例化方式
- 使用构造函数实例化
- 使用静态工厂方法实例化
- 使用实例工厂方法实例化
In Spring documentation, factory bean refers to a bean that is configured in the Spring container that will create objects through an instance or static factory method. By contrast, FactoryBean (notice the capitalization) refers to a Spring-specific FactoryBean .
依赖注入
注入方式
-
基于构造函数的依赖注入
- 构造函数参数解析
- 构造函数参数类型匹配
- 构造函数参数索引
- 构造函数参数名称
- 基于Setter的依赖注入
基于构造函数或基于setter的DI?
由于您可以混合基于构造函数和基于setter的DI,因此
将 构造函数用于强制依赖项
将 setter方法用于可选依赖项
在setter方法上使用@Required注解可用于使属性成为必需的依赖项。
循环依赖
使用 depends-on
如果bean是另一个bean的依赖项,通常意味着将一个bean设置为另一个bean的属性。 通常,您可以使用基于XML的配置元数据中的<ref />元素来完成此操作。 但是,有时bean之间的依赖关系不那么直接; 例如,需要触发类中的静态初始化程序,例如数据库驱动程序注册。 在初始化使用此元素的bean之前,depends-on属性可以显式强制初始化一个或多个bean。
懒加载 bean
默认情况下,ApplicationContext实现会急切地创建和配置所有单例bean,作为初始化过程的一部分。 通常,这种预先实例化是可取的,因为配置或周围环境中的错误是立即发现的,而不是几小时甚至几天后。 如果不希望出现这种情况,可以通过将bean定义标记为延迟初始化来阻止单例bean的预实例化。 延迟初始化的bean告诉IoC容器在第一次请求时创建bean实例,而不是在启动时。
自动装配模式
模式 | 说明 |
---|---|
no | (默认)无自动装配。 必须通过ref元素定义Bean引用。 不建议对较大的部署更改默认设置,因为明确指定协作者可以提供更好的控制和清晰度。 在某种程度上,它记录了系统的结构。 |
byName | 按属性名称自动装配。 Spring查找与需要自动装配的属性同名的bean。 例如,如果bean定义按名称设置为autowire,并且它包含master属性(即,它具有setMaster(..)方法),则Spring会查找名为master的bean定义,并使用它来设置 属性。 |
byType | 如果容器中只存在一个属性类型的bean,则允许自动装配属性。 如果存在多个,则抛出致命异常,这表示您不能对该bean使用byType自动装配。 如果没有匹配的bean,则没有任何反应; 该物业未设定。 |
constructor | 类似于byType,但适用于构造函数参数。 如果容器中没有构造函数参数类型的一个bean,则会引发致命错误。 |
致谢
由于笔者知识水平有限,难免出现理解或认知上的错误,欢迎勘误。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。