简述

像 spring 这样比较成熟的框架,都会提供相对完善的参考文档,那么我们学习的第一步就是根据参考文档来学习。
如下图标红部分,我们先来学习核心部分。

clipboard.png

核心技术主要包括:

  • IoC Container
  • AOP
  • Resources
  • Validation
  • Data Binding
  • Type Conversion
  • Events
  • SpEL
  • i18n

clipboard.png

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及其之间的依赖关系反映在容器使用的配置元数据中。

clipboard.png

由上图可知:主要有四个节点

  • 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 从文件系统中加载元数据

clipboard.png

现在流行的就是使用 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定义封装的配置元数据来创建(或获取)实际对象。

实例化方式

  • 使用构造函数实例化

clipboard.png

  • 使用静态工厂方法实例化

clipboard.png

  • 使用实例工厂方法实例化

clipboard.png

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 .

依赖注入

注入方式

  • 基于构造函数的依赖注入

    • 构造函数参数解析
    • 构造函数参数类型匹配
    • 构造函数参数索引
    • 构造函数参数名称

clipboard.png

  • 基于Setter的依赖注入

clipboard.png

基于构造函数或基于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,则会引发致命错误。

致谢

由于笔者知识水平有限,难免出现理解或认知上的错误,欢迎勘误。

参考资料


浮云小生
5 声望1 粉丝

浮云小生