首页 > 其他分享 >Spring Boot核心原理《二》Spring Boot的核心拓展点

Spring Boot核心原理《二》Spring Boot的核心拓展点

时间:2023-05-09 10:32:12浏览次数:45  
标签:context Spring boot springframework Boot 核心 org



文章结构

  • 1. 概述
  • 2. Spring Boot 的核心拓展点
  • 1.1 聊 Spring Boot 的 3 大拓展接口
  • 2.1.1 Spring 核心拓展接口回顾
  • 2.1.2 Spring Boot 的 3 大拓展接口
  • 2.2 聊 Spring Boot 启动流程的 4 大核心方法
  • 2.3 聊 Spring Boot 引入的 5 种事件
  • 3. 以 Nacos 为例子看下 Nacos 是如何拓展的
  • 4. 总结


1. 概述

前提:最好了解 Spring Boot 的启动流程 Spring Boot核心原理《一》,Spring Boot的启动流程

不夸张的说,下面的内容在帮助理解 Spring Boot 核心原理、各种组件与 Spring Boot 的集成原理的理解上(如 Nacos、Sentinal 等等)有事半功倍的特效!如果你觉得没有你来 diss 我!

口诀就是口诀就是 3+4+5,3大核心拓展 + 4大核心方法 + 5大核心事件

作者几乎对 Spring 所有核心源码做了非常详细的注释, 保姆式Spring5源码解析 ,本文系 FireFish 原创作品,欢迎转载,觉得不错的小伙伴可以帮助 Gitee 点个 Star 鼓励一下

2. Spring Boot 的核心拓展点

3 大核心拓展接口 + 4 大核心方法 + 5 大核心事件,但是太多了记不住怎么办?知道 3 个核心拓展接口即可

1.1 聊 Spring Boot 的 3 大拓展接口

2.1.1 Spring 核心拓展接口回顾

了解过 Spring 的同学可能会知道 Spring 有 3大重要拓展接口 ,之所以说是 3 个而不是 4 个不是空穴来风的,在 官方文档 中介绍了这 3 个重要的接口分别是:

  • BeanPostProcessor

作用:用来对实例化后的 Bean 做功能增强

举例: AutowiredAnnotationBeanPostProcessor

  • BeanFactoryPostProcessor

作用:用来操作或修改 Bean 的元数据,元数据即是 configuration metadata ;简单点说就是可以修改 Bean 的 BeanDefinition

举例: PropertySourcesPlaceholderConfigurer

  • FactoryBean

作用:FactoryBean 接口主要用于与第三方接口的集成

举例:Spring 与 Mybatis 集成中的 SqlSessionFactoryBean

这 3 个拓展接口支撑了 Spring 的很多拓展功能,我们这里只是对接口功能的简单介绍, 详细内容在 Spring 核心拓展接口 专门有介绍

上面这 3 个接口非常非常非常重要!作为 Java 开发人员应该了解原理

2.1.2 Spring Boot 的 3 大拓展接口

接上文下面继续聊 Spring Boot 的 3 大拓展接口,先给出本人独家总结 3 大拓展接口如下:

  • ApplicationContextInitializer
  • ApplicationListener
  • EnableAutoConfiguration

之所以说是 3 个接口而不是 4 个接口当然也不是空穴来风有观点佐证,您听我接着说,在我们的 Spring Boot 项目中一般都会引入 spring-boot-starter-parentspring-boot-dependencies ,不管哪一个本身没有太大区别,都间接引入了 spring-boot-starter ,这个 starter 几乎只要是 Spring Boot 项目都会引入进来
重点是 spring-boot-starter 引入了 2 个关键的依赖 spring-bootspring-boot-autoconfigure ,依赖定义如下:

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot</artifactId>
        <version>2.6.4</version>
        <scope>compile</scope>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-autoconfigure</artifactId>
        <version>2.6.4</version>
        <scope>compile</scope>
    </dependency>
</dependencies>

这2个依赖包就是 Spring Boot 的核心内容

  • spring-boot依赖包

spring-boot 包中的 spring.factories 文件中的内容的作用是在 Spring Boot 的核心流程中发挥作用的(观众老爷们是不是觉得有点抽象,总之意思就是在 Spring Boot 启动流程中发挥重要作用)

spring.factories 文件众多的配置中,有且只有 2 个重要配置在 Spring Boot 启动流程(启动流程本文后文会讲)中发挥重要作用,就是 ApplicationContextInitializerApplicationListener ,这 2 个重要的配置内容举例如下:

# 代码位置:spring-boot-2.0.2.RELEASE.jar 的 spring.factories 文件
# Application Context Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\
org.springframework.boot.context.ContextIdApplicationContextInitializer,\
org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\
org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer

# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.ClearCachesApplicationListener,\
org.springframework.boot.builder.ParentContextCloserApplicationListener,\
org.springframework.boot.context.FileEncodingApplicationListener,\
org.springframework.boot.context.config.AnsiOutputApplicationListener,\
org.springframework.boot.context.config.ConfigFileApplicationListener,\
org.springframework.boot.context.config.DelegatingApplicationListener,\
org.springframework.boot.context.logging.ClasspathLoggingApplicationListener,\
org.springframework.boot.context.logging.LoggingApplicationListener,\
org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener
  • spring-boot-autoconfigure自动配置类依赖包

spring-boot-autoconfigure 包中的 spring.factories 文件是专门用来存放 自动配置类 的,自动配置类是自动装配的核心以至于专门有一个依赖包用来存放自动配置的内容,自动配置类的 key 是 EnableAutoConfiguration ,代码举例如下:

# 代码位置:spring-boot-autoconfigure-2.0.2.RELEASE.jar 的 spring.factories 文件
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration
...... # 还有很多,略

嘿嘿,截止此处已经把 Spring Boot 的 3 大拓展接口说完了哦,记住了莓

2.2 聊 Spring Boot 启动流程的 4 大核心方法

Spring Boot 主体的启动流程的代码如下,其中我们只讨论核心方法(像 printBannerafterRefreshcallRunners 非重点方法直接忽略!)

listeners.starting(bootstrapContext, this.mainApplicationClass);
try {
    // <1> 把args参数封装为一个对象
    ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
    // <2> 核心方法:准备环境
    ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
    configureIgnoreBeanInfo(environment);
    // <3> 打印 banner 图
    Banner printedBanner = printBanner(environment);
    // <4> 核心方法:创建容器上下文
    context = createApplicationContext();
    context.setApplicationStartup(this.applicationStartup);
    // <5> 核心方法:在 refresh 前准备好必要的东西
    prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
    // <6> 核心方法:刷新上下文,也就是执行 ApplicationContext 的 onfresh 方法
    refreshContext(context);
    afterRefresh(context, applicationArguments);
    Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
    if (this.logStartupInfo) {
        new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), timeTakenToStartup);
    }
    // <7> 触发重点事件
    listeners.started(context, timeTakenToStartup);
    // <8> 调用2个接口的方法
    callRunners(context, applicationArguments);
}

对上面注释中的 4 大核心方法做如下说明:

  • <2> 处,prepareEnvironment
  • 创建或准备环境 environment

别问我环境是什么兄弟,来这里 什么是Spring 的 Environment(环境) 呢 。简单理解为一个有所有配置的容器

  • 触发环境准备好的监听事件 Application PreparedEnvironmentEvent 事件
  • <4> 处,createApplicationContext

作用:创建容器,根据不同的环境创建不同的容器

  • <5> 处,prepareContext
  • 执行 applyInitializers 方法也就是调用上文中的 ApplicationContextInitializer 接口(记得否?)

作用:很多的第三方组件基于这个做了拓展实现了组件功能与 Spring Boot 集成

  • 触发了 Application ContextInitializedEvent 事件 和 触发了 Application PreparedEvent 事件
  • <6> 处,refreshContext

作用:刷新容器,其实就是调用 ApplicationContextonfresh 方法。别问来这里看Spring5官方源码注释 Spring5核心源码解析 ,安排得明明白白

2.3 聊 Spring Boot 引入的 5 种事件

在 Spring Boot 容器的启动过程中会触发这 5 个事件。Spring Framework 的事件机制是一种低耦合的拓展机制,比如第三方应用如 Nacos 就监听了这几个事件实现了自己的功能与 Spring Boot 的整合

注意:这几个事件是需要引入 Spring Boot 才会有的;事件机制还是使用的 Spring Framework 事件机制,只不过事件不是 Spring Framework 中的事件

直接看代码,在注释中说明了事件的触发位置、触发事件的名称等

// <1> Application StartingEvent(应用启动事件,在应用启动前触发)
listeners.starting(bootstrapContext, this.mainApplicationClass);
try {
    ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
    // <2> Application EnvironmentPreparedEvent(环境准备好事件,在environment准备好后触发)
    ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
    configureIgnoreBeanInfo(environment);
    Banner printedBanner = printBanner(environment);
    context = createApplicationContext();
    context.setApplicationStartup(this.applicationStartup);
    // <3> Application ContextInitializedEvent(应用 上下文初始化好了事件,在执行完上下文初始化器初始化后触发)
    // <4> Application PreparedEvent(应用准备好事件,在onfresh前的基础工作做好了后触发)
    prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
    // <5> 调用 Spring Framework 实现了容器的主要功能,说明了 Spring Boot 是对 Spring Framework 的拓展而不是代替
    refreshContext(context);
    afterRefresh(context, applicationArguments);
    Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
    if (this.logStartupInfo) {
        new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), timeTakenToStartup);
    }
    // <6> Application StartedEvent(应用启动好了事件,在应用启动完成后触发)
    listeners.started(context, timeTakenToStartup);
    callRunners(context, applicationArguments);
}

对上面注释中的 5 大核心事件做如下说明:(注意用空格隔开是为了强调事件的关键词):

  • <1> 处, Application StartingEvent

作用:应用启动事件,在应用启动前触发

  • <2> 处, Application EnvironmentPreparedEvent

作用:环境准备好事件,在environment准备好后触发

  • <3> 处, Application ContextInitializedEvent

作用:应用 上下文初始化好了事件,在执行完上下文初始化器初始化后触发

  • <4> 处, Application PreparedEvent

作用:应用准备好事件,在onfresh前的基础工作做好了后触发

  • <6> 处, Application StartedEvent

作用:应用启动好了事件,在应用启动完成后触发

3. 以 Nacos 为例子看下 Nacos 是如何拓展的

这里以 Nacos 为例简单说下步骤:

  1. 有没有实现 ApplicationContextInitializer 接口的。 Nacos 并没有使用这个拓展
  2. 有没有实现 ApplicationListener 接口的。实现了,如 Nacos 的 NacosContextRefresher
// 代码位置:xxxx
// 虽然是实现了 ApplicationListener 接口,但是是通过@Bean注解配置的Listener
// 有多种方式都可以配置Listener但是@Bean方式可能不能监听到某些事件,除非您很明确知道您要监听的事件的触发时机否则不是很建议这种配置方式

// 定义 NacosContextRefresher 监听器
@Bean
public NacosContextRefresher nacosContextRefresher(
    NacosConfigProperties nacosConfigProperties,
    NacosRefreshProperties nacosRefreshProperties,
    NacosRefreshHistory refreshHistory) {
    return new NacosContextRefresher(nacosRefreshProperties, refreshHistory,
    nacosConfigProperties.configServiceInstance());
}

// NacosContextRefresher 的类定义,监听特定事件 ApplicationReadyEvent
public class NacosContextRefresher implements ApplicationListener<ApplicationReadyEvent> {

	@Override
	public void onApplicationEvent(ApplicationReadyEvent event) {
		// many Spring context
		if (this.ready.compareAndSet(false, true)) {
			this.registerNacosListenersForApplications();
		}
	}
}
  1. 看下nacos依赖引入了哪些自动配置类
# 如:spring-cloud-alibaba-nacos-config-2.1.0.RELEASE.jar!/META-INF/spring.factories文件
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.alibaba.cloud.nacos.NacosConfigAutoConfiguration

然后就需要耐心的顺藤摸瓜,慢慢摸索了!

4. 总结

相信大家从上面代码中的 Spring Boot 启动流程中也看出来了(特别是调用了 Spring 的 onfresh 方法),Spring Boot 是在 Spring 的基础上做了拓展,Spring Boot 的本质还是 Spring,所以只有把 Spring 的底子打牢固了才能更好的理解 Spring Boot!

虽然上面总结了 3大核心拓展 + 4大核心方法 + 5大核心事件 帮助在理解 Spring Boot 原理上撕开了一个大口子,但是不得不说的是 Spring Boot 的 一站式自动配置 隐藏了很多细节,所以对于想理解组件如何实现自动集成也不是很容易,所以上文也以 Nacos 为例做了简单说明

一般的,把握了3大拓展点,特别是 自动配置类引入了哪些组件 ,顺着这个线慢慢梳理也就能了解原理了,结束了哦!


标签:context,Spring,boot,springframework,Boot,核心,org
From: https://blog.51cto.com/u_16096603/6257452

相关文章

  • 一站式统一返回值封装、异常处理、异常错误码解决方案—最强的Sping Boot接口优雅响应
    作者:京东物流 覃玉杰1.简介GracefulResponse是一个SpringBoot体系下的优雅响应处理器,提供一站式统一返回值封装、异常处理、异常错误码等功能。使用GracefulResponse进行web接口开发不仅可以节省大量的时间,还可以提高代码质量,使代码逻辑更清晰。强烈推荐你花3分钟学会它!......
  • 核心银行系统正在加速分布式技术转型
    随着数字化时代的迭变,中国商业银行的经营环境正在发生着深刻而复杂的变化。在新机遇不断涌现的同时,商业银行也开始面临前所未有的挑战。在中电金信副总经理、研究院院长况文川看来,数字化转型成为了我国商业银行普遍认同、广泛采用的战略性举措,并已经在保障稳健经营、改善客户体验、......
  • 使用IDEA创建第一个SpringBoot项目并进行一些基础配置的详细教程
    1.打开IDEA,新建newproject,填写项目信息。 2.如上图所示,设置serverURL为阿里云服务器为:https://start.aliyun.com/下面的Java版本选择必须和ProjectSDK版本相对应,不然不能进行下一步。3.选择springboot版本和开发会使用到的组件,最后点finish即可。 4.等待IDEA创建并......
  • 原来Spring能注入集合和Map的computeIfAbsent是这么好用!
    大家好,我是3y,今天继续来聊我的开源项目austin啊,但实际内容更新不多。这文章主是想吹下水,主要聊聊我在更新项目中学到的小技巧。今天所说的小技巧可能有很多人都会,但肯定也会有跟我一样之前没用过的。消息推送平台......
  • Spring_day02
    Spring_day02今日目标掌握IOC/DI配置管理第三方bean掌握IOC/DI的注解开发掌握IOC/DI注解管理第三方bean完成Spring与Mybatis及Junit的整合开发1,IOC/DI配置管理第三方bean前面所讲的知识点都是基于我们自己写的类,现在如果有需求让我们去管理第三方jar包中的类,该如何管理......
  • 【Spring】循环依赖
    参考:阿里开发者-Spring循环依赖那些事(有完整流程图)  https://mp.weixin.qq.com/s/cqkZEvmmh7jnNt2K5r_lXg   问题:1、什么是循环依赖?2、为什么会产生循环依赖?3、循环依赖有哪些场景?4、Spring如何解决循环依赖的?5、Spring为什么使用三级缓存?6、Spring支持AOP循环依赖,为何......
  • Spring学习笔记专题二
    (1)注解1,注解的作用:给Java结构添加标记;2,注解的使用:使用注解一般需要三方面参与:1,注解类;2,需要标记的目标类型;3,用于处理目标类型的处理程序;3,Retention:把注解保留的时机1,SOURCE:保留在源代码级别,一般供编辑器级别使用2,CLASS:保留到字节码级别,一般用编译器使用3,RUNTIME:保留到运行......
  • 关于 mybatis-spring-boot-starter 的版本适配问题
    写在前面:本人就读于某不知名二本计科专业,目前大二,正在自学SpringBoot。博客中难免出现谬误,请大家批评指正,不喜勿喷,键盘侠手下留情。开发环境:IDEA2022.3.2JDK1.8SpringBoot2.7.11Maven3.9.0问题描述:最近在写一个SpringBoot项目,整合了Mybatis,在程序运行时出现如下报错......
  • java netty socket实例:报文长度+报文内容,springboot
    前言说实话,javanetty方面的资料不算多,尤其是自定义报文格式的,少之又少自己写了个简单的收发:报文长度+报文内容发送的话,没有写自动组装格式,自己看需求吧,需要的话,自己完善服务端启动可以直接用类文件启动,也可以通过springboot。我这里写的是用springboot启动的,可以自己按照需求自......
  • SpringBoot全局异常处理
    @ControllerAdvice:使用该注解表示开启了全局异常的捕获; 参考链接[1]https://www.cnblogs.com/xuwujing/p/10933082.html[2]https://gitee.com/bruce6213/global-exception-handler......