Createsequence's Blog

一个努力前进的程序猿


  • 首页

  • 标签

  • 分类

  • 归档

  • 搜索

深入理解Spring事件机制(一):广播器与监听器的初始化

发表于 2022-08-16 | 分类于 SpringBoot
字数统计: 4.1k

前言

Spring 从 3.x 开始支持事件机制。在 Spring 的事件机制中,我们可以令一个事件类继承 ApplicationEvent 类,然后将实现了 ApplicationListener 的 Bean 注册到 spring 容器,最后向 ApplicationEventPublisher 推送事件对象即可令所有订阅者收到事件。在 4.2 以后,甚至不需要实现 ApplicationListener 接口,仅需在 Bean 中方法标记 @EventListener 注解即可。

笔者将基于 Spring 源码的 5.2.x 分支,分析该功能是如何实现的。

本文是其中的第一篇文章,将分析广播器与监听的是如何被初始化,并完成注解流程的。

在开始前,推荐先阅读前文了解 Spring 容器的初始化过程与 BeanFactory 中 Bean 的创建,如果可能,还可以了解一点 Spring 的注解机制,这将更有利于流程与一些代码的理解。

相关文章:

  • 深入理解Spring事件机制(一):广播器与监听器的初始化
  • 深入理解Spring事件机制(二):事件的推送

一、广播器的创建

在前文,我们知道容器的初始化是通过 AbstractApplicationContext.refresh() 方法完成的,事件机制的相关组件同样也离不开容器,因此事件系统的初始化也通过该方法完成。

AbstractApplicationContext.initApplicationEventMulticaster() 是第一步,它的作用很简单:

阅读全文 »

深入理解Spring事件机制(二):事件的传播

发表于 2022-08-16 | 分类于 SpringBoot
字数统计: 5.7k

前言

Spring 从 3.x 开始支持事件机制。在 Spring 的事件机制中,我们可以令一个事件类继承 ApplicationEvent 类,然后将实现了 ApplicationListener 的 Bean 注册到 spring 容器,最后向 ApplicationEventPublisher 推送事件对象即可令所有订阅者收到事件。在 4.2 以后,甚至不需要实现 ApplicationListener 接口,仅需在 Bean 中方法标记 @EventListener 注解即可。

笔者将基于 Spring 源码的 5.2.x 分支,分析该功能是如何实现的。

本文是其中的第二篇文章,将分析事件是如何通过广播器推送,并被监听器接收并处理的。

在开始前,推荐先阅读前文了解一点 Spring 的注解机制或者事务机制,这将更有利于流程与一些代码的理解。

相关文章:

  • 深入理解Spring事件机制(一):广播器与监听器的初始化
  • 深入理解Spring事件机制(二):事件的推送

一、事件的推送

1、将事件推送到上下文

当我们借助 Spring 发送一个事件对象的时候,一般都通过 ApplicationEventPublisher 完成,在默认情况下,通过容器获得的 ApplicationEventPublisher 单例实际上就是 ApplicationContext 本身。

阅读全文 »

深入理解Spring注解机制(三):合并注解的合成

发表于 2022-08-14 | 分类于 Spring
字数统计: 3.7k

前言

众所周知,spring 从 2.5 版本以后开始支持使用注解代替繁琐的 xml 配置,到了 springboot 更是全面拥抱了注解式配置。平时在使用的时候,点开一些常见的等注解,会发现往往在一个注解上总会出现一些其他的注解,比如 @Service:

1
2
3
4
5
6
7
8
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component // @Component
public @interface Service {
@AliasFor(annotation = Component.class)
String value() default "";
}

大部分情况下,我们可以将 @Service 注解等同于 @Component 注解使用,则是因为 spring 基于其 JDK 对元注解的机制进行了扩展。

在 java 中,元注解是指可以注解在其他注解上的注解,spring 中通过对这个机制进行了扩展,实现了一些原生 JDK 不支持的功能,比如允许在注解中让两个属性互为别名,或者将一个带有元注解的子注解直接作为元注解看待,或者在这个基础上,通过 @AliasFor 或者同名策略让子注解的值覆盖元注解的值。

本文将基于 spring 源码 5.2.x 分支,解析 spring 如何实现这套功能的。

这是系列的第三篇文章,将详细介绍 Spring 是如何在经过搜索与属性映射后,将处理后的注解合成为合并注解的。

相关文章:

  • 深入理解Spring注解机制(一):注解的搜索与处理机制;
  • 深入理解Spring注解机制(二):元注解解析与属性映射;
  • 深入理解Spring注解机制(三):合并注解的合成;

一、合并注解

阅读全文 »

深入理解Spring注解机制(二):元注解解析与属性映射

发表于 2022-08-10 | 分类于 Spring
字数统计: 9.2k

前言

众所周知,spring 从 2.5 版本以后开始支持使用注解代替繁琐的 xml 配置,到了 springboot 更是全面拥抱了注解式配置。平时在使用的时候,点开一些常见的等注解,会发现往往在一个注解上总会出现一些其他的注解,比如 @Service:

1
2
3
4
5
6
7
8
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component // @Component
public @interface Service {
@AliasFor(annotation = Component.class)
String value() default "";
}

大部分情况下,我们可以将 @Service 注解等同于 @Component 注解使用,则是因为 spring 基于其 JDK 对元注解的机制进行了扩展。

在 java 中,元注解是指可以注解在其他注解上的注解,spring 中通过对这个机制进行了扩展,实现了一些原生 JDK 不支持的功能,比如允许在注解中让两个属性互为别名,或者将一个带有元注解的子注解直接作为元注解看待,或者在这个基础上,通过 @AliasFor 或者同名策略让子注解的值覆盖元注解的值。

本文将基于 spring 源码 5.2.x 分支,解析 spring 如何实现这套功能的。

这是系列的第二篇文章,将详细介绍 Spring 是如何解析 @AliasFor,实现各种别名功能。

相关文章:

  • 深入理解Spring注解机制(一):注解的搜索与处理机制;
  • 深入理解Spring注解机制(二):元注解解析与属性映射;
  • 深入理解Spring注解机制(三):合并注解的合成;

一、创建合并注解聚合

阅读全文 »

深入理解Spring注解机制(一):注解的搜索与处理机制

发表于 2022-08-06 | 分类于 Spring
字数统计: 6.2k

前言

众所周知,spring 从 2.5 版本以后开始支持使用注解代替繁琐的 xml 配置,到了 springboot 更是全面拥抱了注解式配置。平时在使用的时候,点开一些常见的等注解,会发现往往在一个注解上总会出现一些其他的注解,比如 @Service:

1
2
3
4
5
6
7
8
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component // @Component
public @interface Service {
@AliasFor(annotation = Component.class)
String value() default "";
}

大部分情况下,我们可以将 @Service 注解等同于 @Component 注解使用,则是因为 spring 基于其 JDK 对元注解的机制进行了扩展。

在 java 中,元注解是指可以注解在其他注解上的注解,spring 中通过对这个机制进行了扩展,实现了一些原生 JDK 不支持的功能,比如允许在注解中让两个属性互为别名,或者将一个带有元注解的子注解直接作为元注解看待,或者在这个基础上,通过 @AliasFor 或者同名策略让子注解的值覆盖元注解的值。

本文将基于 spring 源码 5.2.x 分支,解析 spring 如何实现这套功能的。

这是系列的第一篇文章,将详细介绍 Spring 是如何从 AnnotatedElement 的层级结构中完成对注解的搜索与处理的。

相关文章:

  • 深入理解Spring注解机制(一):注解的搜索与处理机制;
  • 深入理解Spring注解机制(二):元注解解析与属性映射;
  • 深入理解Spring注解机制(三):合并注解的合成;

一、层级结构

阅读全文 »

深入理解Spring别名机制

发表于 2022-07-26 | 分类于 Spring
字数统计: 2.1k

前言

在 spring 容器中,允许通过名称或别名来获取 bean ,这个能力来自于顶层接口 AliasRegistry,分析类下属的关系图,可以看到,几乎所有主要容器都直接或间接的实现了 AliasRegistry 接口。

image-20220621134756391
image-20220621134756391

AliasRegistry 的结构非常简单,主要的类就是 AliasRegistry 接口与他的实现类 SimpleAliasRegistry,后续的实现类基本都直接或间接的继承了 SimpleAliasRegistry。

本文将基于 spring 源码 5.2.x 分支,围绕 SimpleAliasRegistry 解析 spring 的别名机制是如何实现的。

一、AliasRegistry

在 spring 的容器中,一个 bean 必须至少有一个名称,而一个名称可以有多个别名,别名亦可以有别名,若我们把这个最原始的名称称为 id,则结构可以有:

1
2
id -> id's alias1 -> alias of id's alias1 ... ...
-> id's alias2 -> alias of id's alias2 ... ...

通过 bean 的 id,或与该 id 直接、间接相关的别名,都可以从容器中获取到对应的 bean。

AliasRegistry 的作用就是定义这一套规则:

阅读全文 »

深入理解Spring容器初始化(三):事件及其他配置的初始化

发表于 2022-07-05 | 分类于 Spring
字数统计: 3.5k

前言

我们知道,spring 的启动其实就是容器的启动,而一般情况下,容器指的其实就是上下文 ApplicationContext。

AbstractApplicationContext 作为整个 ApplicationContext 体系中最高级的抽象类,为除了 ComplexWebApplicationContext 和 SimpleWebApplicationContext 这两个容器外的全部容器,规定好了 refresh 的整体流程,所有的容器在完成一些自己的初始化配置后,都需要调用该 refresh 方法,依次完成指定内容的初始化。

也就是说,读懂了 AbstractApplicationContext.refresh() 方法,其实就读懂了容器的启动流程:

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {

// ================= 一、上下文的初始化 =================
// 准备上下文
prepareRefresh();
// 通知子类刷新内部工厂
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 准备bean工厂以便在当前上下文中使用
prepareBeanFactory(beanFactory);

try {
// ================= 二、BeanFactory的初始化 =================
// 对工厂进行默认后置处理
postProcessBeanFactory(beanFactory);
// 使用后置处理器对工厂进行处理
invokeBeanFactoryPostProcessors(beanFactory);
// 注册Bean后置处理器
registerBeanPostProcessors(beanFactory);

// ================= 三、事件,Bean及其他配置的初始化 =================
// 初始化此上下文的消息源
initMessageSource();
// 为此上下文初始化事件广播者
initApplicationEventMulticaster();
// 初始化特定上下文子类中的其他特殊bean
onRefresh();
// 检查侦听器bean并注册
registerListeners();
// 实例化所有非懒加载的剩余单例
finishBeanFactoryInitialization(beanFactory);
// 完成刷新
finishRefresh();
}


// ================= 异常处理 =================
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// 销毁已创建的单例
destroyBeans();
// 重置上下文的激活状态
cancelRefresh(ex);
throw ex;
}
finally {
// 重置内部的一些元数据缓存
resetCommonCaches();
}
}
}

从总体来看,该方法描述的初始化过程大概分为三步:

  • [x] 上下文的初始化;
  • [x] BeanFactory 初始化;
  • [x] 事件,Bean及其他配置的初始化;

笔者将基于 spring 源码 5.2.x 分支,分别通过五篇文章从源码分析 spring 容器的初始化过程。

本文是其中的第三篇文章,将介绍上下文中事件,Bean及其他配置的初始化。

相关文章:

阅读全文 »

深入理解Spring容器初始化(二):BeanFactory的初始化

发表于 2022-06-08 | 分类于 Spring
字数统计: 3.5k

前言

我们知道,spring 的启动其实就是容器的启动,而一般情况下,容器指的其实就是上下文 ApplicationContext。

AbstractApplicationContext 作为整个 ApplicationContext 体系中最高级的抽象类,为除了 ComplexWebApplicationContext 和 SimpleWebApplicationContext 这两个容器外的全部容器,规定好了 refresh 的整体流程,所有的容器在完成一些自己的初始化配置后,都需要调用该 refresh 方法,依次完成指定内容的初始化。

也就是说,读懂了 AbstractApplicationContext.refresh() 方法,其实就读懂了容器的启动流程:

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {

// ================= 一、上下文的初始化 =================
// 准备上下文
prepareRefresh();
// 通知子类刷新内部工厂
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 准备bean工厂以便在当前上下文中使用
prepareBeanFactory(beanFactory);

try {
// ================= 二、BeanFactory的初始化 =================
// 对工厂进行默认后置处理
postProcessBeanFactory(beanFactory);
// 使用后置处理器对工厂进行处理
invokeBeanFactoryPostProcessors(beanFactory);
// 注册Bean后置处理器
registerBeanPostProcessors(beanFactory);

// ================= 三、事件,Bean及其他配置的初始化 =================
// 初始化此上下文的消息源
initMessageSource();
// 为此上下文初始化事件广播者
initApplicationEventMulticaster();
// 初始化特定上下文子类中的其他特殊bean
onRefresh();
// 检查侦听器bean并注册
registerListeners();
// 实例化所有非懒加载的剩余单例
finishBeanFactoryInitialization(beanFactory);
// 完成刷新
finishRefresh();
}


// ================= 异常处理 =================
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// 销毁已创建的单例
destroyBeans();
// 重置上下文的激活状态
cancelRefresh(ex);
throw ex;
}
finally {
// 重置内部的一些元数据缓存
resetCommonCaches();
}
}
}

从总体来看,该方法描述的初始化过程大概分为三步:

  • [x] 上下文的初始化;
  • [x] BeanFactory 初始化;
  • [ ] 事件,Bean及其他配置的初始化;

笔者将基于 spring 源码 5.2.x 分支,分别通过五篇文章从源码分析 spring 容器的初始化过程。

本文是其中的第二篇文章,将介绍 BeanFactory 初始化。

相关文章:

阅读全文 »

深入理解Spring容器初始化(一):上下文的初始化

发表于 2022-05-19 | 分类于 Spring
字数统计: 3.3k

前言

我们知道,spring 的启动其实就是容器的启动,而一般情况下,容器指的其实就是上下文 ApplicationContext。

AbstractApplicationContext 作为整个 ApplicationContext 体系中最高级的抽象类,为除了 ComplexWebApplicationContext 和 SimpleWebApplicationContext 这两个容器外的全部容器,规定好了 refresh 的整体流程,所有的容器在完成一些自己的初始化配置后,都需要调用该 refresh 方法,依次完成指定内容的初始化。

也就是说,读懂了 AbstractApplicationContext.refresh() 方法,其实就读懂了容器的启动流程:

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {

// ================= 一、上下文的初始化 =================
// 准备上下文
prepareRefresh();
// 通知子类刷新内部工厂
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 准备bean工厂以便在当前上下文中使用
prepareBeanFactory(beanFactory);

try {
// ================= 二、BeanFactory的初始化 =================
// 对工厂进行默认后置处理
postProcessBeanFactory(beanFactory);
// 使用后置处理器对工厂进行处理
invokeBeanFactoryPostProcessors(beanFactory);
// 注册Bean后置处理器
registerBeanPostProcessors(beanFactory);

// ================= 三、事件,Bean及其他配置的初始化 =================
// 初始化此上下文的消息源
initMessageSource();
// 为此上下文初始化事件广播者
initApplicationEventMulticaster();
// 初始化特定上下文子类中的其他特殊bean
onRefresh();
// 检查侦听器bean并注册
registerListeners();
// 实例化所有非懒加载的剩余单例
finishBeanFactoryInitialization(beanFactory);
// 完成刷新
finishRefresh();
}


// ================= 异常处理 =================
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// 销毁已创建的单例
destroyBeans();
// 重置上下文的激活状态
cancelRefresh(ex);
throw ex;
}
finally {
// 重置内部的一些元数据缓存
resetCommonCaches();
}
}
}

从总体来看,该方法描述的初始化过程大概分为三步:

  • [x] 上下文的初始化;
  • [ ] BeanFactory 初始化;
  • [ ] 事件,Bean及其他配置的初始化;

笔者将基于 spring 源码 5.2.x 分支,分别通过五篇文章从源码分析 spring 容器的初始化过程。

本文是其中的第一篇文章,将介绍上下文的初始化过程。

相关文章:

阅读全文 »

深入理解Spring容器体系结构

发表于 2022-04-02 | 分类于 Spring
字数统计: 3.8k

前言

在 spring 中,任何实现了 BeanFactory 接口的类都可以视为容器,它是 IOC 功能实现的核心,用于完成类实例从加载到销毁的整个生命周期的控制,这些被 spring 所管理的实例统称为 bean。

根据抽象层级的不同,容器又分为 BeanFactory 的直接实现,与基于 BeanFactory 的扩展实现 ApplicationContext,后者在前者的基础继承了 ResourceLoader和EnvironmentCapable接口,因而具备从某类运行环境的资源中直接加载 bean 的能力。

image-20220620142154578
image-20220620142154578

ApplicationContext 最常用的两个实现 ClassPathXmlApplicationContext 和 AnnotationConfigApplicationContext ,前者用于从项目路径下根据 xml 文件加载 bean,而后者通过扫描类注解完成 bean 的加载。这两者 ApplicationContext 实际上就对应了我们所熟悉的两类配置方式,前者就是传统的 xml 配置,后者则是通过 @Component 与 @Bean 等注解对 bean 进行配置。

本文将基于 spring 源码 5.2.x 分支,基于 BeanFactory 与 ApplicationContext 两大接口,介绍 spring 的两类容器的结构体系。

一、BeanFactory 接口体系

总览 BeanFactory 体系,按照接口的抽象层次,大体可以分层四层:

  • 第一层:BeanFactory;
  • 第二层:HierarchicalBeanFactory,ListableBeanFactory,AutowireCapableBeanFactory;
  • 第三层:ConfigurableBeanFactory,此外还有一个关联性较强SingletonBeanRegistry;
  • 第四层:ConfigurableListableBeanFactory;
image-20220620151809732
image-20220620151809732
阅读全文 »
12…10

99 日志
15 分类
22 标签
RSS
© 2022 Createsequence
主题 — NexT.Gemini v5.1.4
0%