一、为何我决定写Spring Cloud专栏

题语:学习方法之多写:模仿、比较、超越
作者:A哥(YourBatman)
wx号:fsx641385712(备注“Java群”字样)
公众号:BAT的乌托邦(ID:BAT-utopia)

代码下载地址:https://github.com/f641385712/spring-cloud-learning

前言

各位小伙伴大家好,我是A哥。了解我文章的小伙伴应该知道,到目前为止A哥还几乎没有写过Spring Boot/Spring Cloud的文章,虽然写了不少关于Spring Framework的内容,但仍旧被不少小伙伴认为与时代脱节。都什么年代了,谁还会直接使用Spring Framework呢?是的没毛病,那么至于为何我一直坚持Spring Framework才是最重要的呢,请看下图:

Spring和Spring Boot
你对Spring Framework的了解程度决定了你对Spring Boot的了解程度,你对Spring Boot的了解程度决定了你对Spring Cloud的了解程度。


为何我决定写Spring Cloud专栏?

A哥一直迟迟不去写Spring Cloud系列,主要是“市面上”的Spring Boot/Spring Cloud相关文章实在太多了,感觉这汤饭被炒得太多次了,被玩坏了的赶脚,所以是有畏惧的。经过一段时间的思考,最终A哥还是决定要去“冒险”一波,主要有如下两方面原因促成:

  1. 通过小伙伴们的反馈,对Spring Cloud体系的学习确实有不小的诉求。这很容易理解:谁让它这么火呢~
  2. “市面上”的SB/SC系列教程,大都偏使用层面,站在使用者的角度去分析。读完了可能会Run了,可成为一个合格的API调用工程师,但背后的缘由、对于如何扩展仍举步维艰

还记得我刚入门的时我“师父”对我说过一段话,结合现在语境翻译为:如果你对SB/SC不甚了解,并且只是抱着一种just run的态度去对待,那你的工作会很累且难有积累和提升。不知道为何能run和为何不能run,做起事来心里必然会有所顾虑。Spring Boot/Spring Cloud对初学者非常的友好 ,并且兼具了极大的灵活性,因此不管是对于新手还是老手它都具有很大的学习以及可扩展、可定制空间。

熟悉A哥的小伙伴都知道,我虽然骚气,但并不会擅长写抓人眼球文章,反而更愿意沉下心去写些更为实用的文章,不求访问量,只求产生共鸣。那么为何大多数人都愿意写水文(记录自己生活、唠嗑等文),而不愿意写技术文呢?主要有如下两大原因:

  1. 技术太过于枯燥,很少人愿意静心阅读
  2. 技术文一般很难单独成篇,它对上下文(特别是上文)是有依赖的,这也是为何很多人读不下去的原因
  3. 业内共识:水文写一篇仅需1到2小时,而一篇还不错的技术文往往要花1-2天甚至更多的时间精力

技术文的阅读量一般远远不如“同级别”的水文。有点费力不讨好之嫌。利益驱使,因此坚持这么做的是比较少的,我也不知自己还能坚持几天~

在大环境非常不乐观的当下,唯有技术过硬是你的立身之本,大潮退去才知道谁在裸泳,这对真正的技术人或许是机遇勒。那么来吧,跟着A哥一起的修行之路从这启程,期待本系列做得有血、有肉、有内容、有深度…

学习去...


正文

何为Spring Cloud?摘抄自官网:Spring Cloud为开发人员提供了工具,以快速构建分布式系统中的一些常见模式(例如,配置管理,服务发现,断路器,智能路由,微代理,控制总线)。分布式系统的协调导致样板式样,并且使用Spring Cloud开发人员可以快速站起来实现这些样板的服务和应用程序。它们可以在任何分布式环境中正常工作,包括开发人员自己的笔记本电脑,裸机数据中心以及Cloud Foundry等托管平台。

A哥看到很多SC系列开门见山是教你如何去搭建应用,把SC跑起来。但作为只写“高逼格”文章的我自然不会从哪聊起喽,也费你时间不是。本系列会认为你已经对Spring Boot有一定的了解,并且基本能够正常使用SC了,这样效果最佳。

关于Spring Boot我并无打算写它,但我写了一篇“总结篇”,请移步此处参阅:不懂SpringApplication生命周期事件体系?那就等于不会Spring Boot嘛

本文单刀直入,上来就是源码分析以及对应实例讲解,嘎嘎就是干,一把嗦。所以我们学习Spring Cloud那必然先来到它的核心中的核心:Spring Cloud Context


版本约定

不约定版本的专题讲解是不负责任的,特别对于这么“浪”的SC来说尤甚(毕竟不向下兼容在它身上太常见了)。版本上为了和A哥的其它专题系列保持一致方便后续串讲,本文对相关库的版本做出如下约定:

  • Spring Boot版本:2.2.2.RELEASE
  • Spring Cloud版本:Hoxton.SR1
    • Spring Boot版本:2.2.2.RELEASE
    • spring-cloud-starter-xxx版本:2.2.1.RELEASE

说明:你阅读到本文时Spring Cloud版本可能已经到了Hoxton.SR4,Spring Boot版本也可能已经到了2.2.6.RELEASE。但是像这种专栏式的学习,你完全可忽略小版本之间的差异,姑且认为他们是一样的即可

工程pom文件的大致内容如下:

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.2.2.RELEASE</version>
    <relativePath/>
</parent>

...

<properties>
    <java.version>1.8</java.version>
    <spring-cloud.version>Hoxton.SR1</spring-cloud.version>
</properties>

...

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>${spring-cloud.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

关于Spring Cloud Context

也许你从没听过SC的Context上下文概念,或者从没听过spring-cloud-context这个工程,这都是非常正常的,毕竟实际Just Run条件下确实无需知晓,对初学者非常友好嘛,这是必须做到的。但你应该在pom里见过or导入过spring-cloud-starter这个jar:

在这里插入图片描述

  • spring-cloud-context:SC的context上下文、配置、生命周期管理、健康检查等等
  • spring-cloud-commons:大名鼎鼎的SC的commons抽象。含有如:服务注册/发现、熔断器、负载均衡等公共抽象,面向该抽象编程可以无需关心底层实现,带来一致的编程感受,这是Spring家族最擅长干的事

很明显,本系列将把它拆散开来逐个解释其作用,目的是深入理解,从本质上感受SC的设计以及它的抽象方式和实现方式。那么首先就来到spring-cloud-context这个工程喽。

GAV如下:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-context</artifactId>
</dependency>

在这里插入图片描述

说明:实际生产中,此jar你一般不会单独导入,而是伴随着Spring Cloud starter一起带入的。但“讲课”就得基于最小化原则进行讲解嘛,因此我需要最小化的依赖,我的工程里只导入此jar


本jar的spring.factories文件解释

这个jar很“单纯”,并不拖泥带水,它里面存在一个spring.factories文件,内容如下:

到这里了可别问spring.factories是什么?有什么作用之类的话了哈。它是Spring SPI机制(并非Spring Boot提供的功能哟)的实现,具体参考这个类SpringFactoriesLoader

# AutoConfiguration 自动配置类(一般情况下只有SB容器去执行它,但是不是确定的)
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.autoconfigure.ConfigurationPropertiesRebinderAutoConfiguration,\
org.springframework.cloud.autoconfigure.LifecycleMvcEndpointAutoConfiguration,\
org.springframework.cloud.autoconfigure.RefreshAutoConfiguration,\
org.springframework.cloud.autoconfigure.RefreshEndpointAutoConfiguration,\
org.springframework.cloud.autoconfigure.WritableEnvironmentEndpointAutoConfiguration


# Application Listeners 监听器们
org.springframework.context.ApplicationListener=\
org.springframework.cloud.bootstrap.BootstrapApplicationListener,\  -> 监听ApplicationEnvironmentPreparedEvent事件
org.springframework.cloud.bootstrap.LoggingSystemShutdownListener,\ -> 监听ApplicationEnvironmentPreparedEvent事件
org.springframework.cloud.context.restart.RestartListener -> 监听ApplicationPreparedEvent/ContextRefreshedEvent事件


# Bootstrap components 由Spring Cloud容器负责加载的配置类们
org.springframework.cloud.bootstrap.BootstrapConfiguration=\
org.springframework.cloud.bootstrap.config.PropertySourceBootstrapConfiguration,\
org.springframework.cloud.bootstrap.encrypt.EncryptionBootstrapConfiguration,\
org.springframework.cloud.autoconfigure.ConfigurationPropertiesRebinderAutoConfiguration,\
org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration

此配置文件内容一共分为三个部分,会涉及到执行顺序的问题:

再次有必要强调一次,建议你已经掌握了此文的知识效果更佳:不懂SpringApplication生命周期事件体系?那就等于不会Spring Boot嘛

  1. 自动配置类。有的小伙伴认为自动配置类只能是Spring Boot容器才去执行它,其实这是不对的。确切的说:只要容器内开启了自动配置(有Bean标注了@EnableAutoConfiguration注解,实际是向容器导入了AutoConfigurationImportSelector)就都会执行执行spring.factories里的自动配置类
    1. Spring Boot自然不用说,@SpringBootApplication里就默认开启了自动配置
    2. Spring Cloud里,虽然也使用SpringApplication去启动容器,但是因为SC容器并没有开启自动配置,所以它不会执行
    3. 强调说明:自动配置的执行时机是refresh()容器启动时(执行AutoConfigurationImportSelector#selectImports)。另外,如果你的容器并不需要开启自动配置(就像SC一样),请不要开启,挺耗时的(前提是你对SB、SC有足够了解,不然容易出bug)。或者你也可以使用ImportAutoConfigurationImportSelector来手动接管
  2. 监听器。如上标注,这几个监听器都是监听的SpringApplication的生命周期事件,因此它们在SB的容器启动时的不同生命周期里会按顺序执行,特别是最重要的BootstrapApplicationListener它负责Spring Cloud容器的启动工作
    1. 监听器是由SpringApplication应用去加载的,而SC也是由SpringApplication去启动的,所以SC也会让这些监听器挨个执行哦,若有必要,请注意“防范”
  3. Spring Cloud的启动/引导配置。这些配置是SC专属,详见下面的说明

Spring Cloud专属配置类

也叫Bootstrap引导容器的专属配置类。通过上面3步的第一步解释可知:Spring Cloud它并不会去执行/加载你配置的EnableAutoConfiguration自动配置类,那么如果SC它在引导期间需要属于自己的配置怎么办呢???

这就到了SC它专为自己开发出来的一套配置机制,姑且把它叫做Spring Cloud专属配置类。不同于Spring Boot自动配置使用的是org.springframework.boot.autoconfigure.EnableAutoConfiguration,它作为SC专属的,自然key也就不能一样喽,使用的是:org.springframework.cloud.bootstrap.BootstrapConfiguration@BootstrapConfiguration注解的全类名)。通过这个key配置的配置类们(可以是普通配置类,也可以是自动配置类),就是只能被SC容器在启动时加载的专属配置类,是SC父容器专属。

SB的自动配置类使用AutoConfigurationImportSelector负载最后加载,此处SC的专属配置类使用的是BootstrapImportSelector负责加载,它俩的共同点是:都是一个DeferredImportSelector,所以执行时机是最晚的

通过以上的了解,我们可以知道,SC的专属配置类有如下几个特点:

  1. 该配置只能被Spring Cloud容器/Bootstrap引导上下文读取/加载
  2. 该配置的加载时机最早:优先于SB的普通配置、自动配置之前加载,所以它是具有最高优先级的配置类
  3. 该配置存在于父容器(SC容器)内,而非SB容器内
    1. 当然喽,SB容器也可以访问

Spring Boot容器 vs Spring Cloud容器

我们知道,Spring Cloud容器是Spring Boot容器的父容器。为了让你更直观的看到父子容器内的Bean情况(个数 + 详情),了解其区别,此处A哥写个最简案例比较一波:

public static void main(String[] args) {
    ConfigurableApplicationContext bootContext = SpringApplication.run(Application.class, args);
    System.out.println("boot容器类型" + bootContext.getClass());
    System.out.println("boot容器Bean定义总数:" + bootContext.getBeanFactory().getBeanDefinitionCount());
    System.out.println("boot容器Bean实例总数:" + bootContext.getBeanFactory().getSingletonCount());

    ConfigurableApplicationContext cloudContext = (ConfigurableApplicationContext) bootContext.getParent();
    System.out.println("cloud容器类型" + cloudContext.getClass());
    System.out.println("cloud容器Bean定义总数:" + cloudContext.getBeanFactory().getBeanDefinitionCount());
    System.out.println("cloud容器Bean实例总数:" + cloudContext.getBeanFactory().getSingletonCount());
}

控制台输出:

boot容器类型class org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext
boot容器Bean定义总数:272
boot容器Bean实例总数:283

cloud容器类型class org.springframework.context.annotation.AnnotationConfigApplicationContext
cloud容器Bean定义总数:22
cloud容器Bean实例总数:34

SC容器(父容器)里的“内容”是远远少于SB容器(子容器的)。由于父容器里内容较少,可以详细看看。A哥这里帮你截图让你更直观的感受一把喽:

context.getBeanFactory().getBeanDefinitionNames()(共22个):

在这里插入图片描述
context.getBeanFactory().getSingletonNames()(共34个):

在这里插入图片描述
看完了这个结果,相信你有个感官的认识了。那么A哥给小伙伴们提3个小问题哈,你可以自行思考:

  1. 为何一个是34,一个是22,这两个值为毛不等呢?差异在哪儿呢?
  2. SB容器为毛只是单单启动完,就有这么多Bean了呢?为何它如此重呢?
  3. SB应用有几个context上下文?SC应用呢?

总结

关于Spring Cloud系列的第一篇内容就写到这喽。本文先解释了为何我要写这个系列,以及简单的介绍了下Spring Cloud Context工程,通过本文你是能够大概清楚A哥后面将如何展开的,比如下文将是本系列的主菜之一:BootstrapApplicationListener监听器负责Spring Cloud容器的引导/启动/初始化,敬请关注。

分隔线

关注我

本号所有“享学xxx”系列文章/视频,和什么“享学课堂”、“享学科技”无任何关系。只因笔者名字和其重名,仅此而已

  • 原创不易,码字更不易,请勿白嫖,你的三连是对A哥的最大支持
  • 关注我吧:一个前25年还不会写Hallo World的半残程序猿,中文名:A哥,英文名:YourBatman

在这里插入图片描述
往期精选

原创文章 410 获赞 992 访问量 63万+

猜你喜欢

转载自blog.csdn.net/f641385712/article/details/105753518
今日推荐