微服務學習筆記01

第一節 SpringApplication

本文是在預習和學習后,想要盡自己所能將學到的知識以自己的理解記錄下來。當然,有些地方還是一知半解,所以是有很多內容是小馬哥所講所書,權當引經據典,還望小馬哥見到不要怪罪。

雖然今年項目上已經接觸過SpringBoot,目前仍在使用,但了解到的只是常用的用法。這篇文章主要講一下微服務的概念,以SpringApplication啟動類為切入點,講解Spring注解機制,SpringApplication啟動類的寫法和程序類型推斷,以及SpringBoot事件和事件監聽。

一、微服務是什么?能做什么?

1、概念

微服務是一種細粒度(Fine-Grain)的SOA

SOA = Service-Oriented Architecture 面向服務架構

SOA的特征:

面向服務( Service-Oriented )

松耦合(Loose-Coupling)

模塊化(Modular)

分布式計算(Distributed Computing)

平臺無關性(Independent Platform)

集中管理(Center Government)

用到的技術:

Web Services

關于Web Services,小馬哥文章中講到下面一段話,因個人技術原因,暫時沒有特別讀懂,暫且放在這里供大家參考,也歡迎各位大牛指點迷津。

Web Services 技術演進的目的在于解決分布式計算中,統一異構系統的服務調用的通訊協議。前期的Web Services有XML-PRC、WSDL、SOAP等技術,不但解決了Windows平臺COM+以及Java 平臺RMI無法跨平臺的問題,而且使用了可讀性強的本文協議替代了復雜的二進制協議,如CORBA技術?,F代的WebServices 技術主要代表有REST等。

此外,還提到微服務并非等同于單體應用,我所理解的意思是單體應用只有一個核心,是中心化的,任務是應用整體來完成的,有較高的耦合度。而微服務是去中心化的,多核的,將任務拆解,由各個模塊分工協作完成的,耦合度是較前者要低的。

2、微服務的優勢和適用場景

優勢:

(1)效率:應用微服務化后,變得更加輕量級,在編譯、打包、分發、部署等環節節約時間,提升效率;

(2)質量:面向持續集成友好,自動化編譯、單元和集成測試用例執行和回歸,提高整體質量。

這里講的所謂“持續集成”,個人不是很清楚這個概念。

(3)穩定:當應用大而全時,一個服務的問題可能會導致整體受到影響。而微服務在服務A調用服務B出現問題時,可以選擇降級或熔斷的方式進行處理,等待服務B正常運行后,恢復正常。

(4)運維:微服務應用具備自動化編譯、打包、分發、部署和運維的能力。

(5)成長:對新技術的適配能力,例如:Apache Kafka。在重要等級較低的微服務應用上,可以更好的做新技術的嘗試。

3、不適用微服務的應用場景

(1)場景單一:如果應用本身規模就不大,就沒必要使用微服務,因為本身就是一種微服務。舉例如:一些靜態通告頁面。

(2)邏輯簡單:微服務是為了解決業務邏輯復雜性問題,因此此場景無需微服務。

(3)業務漸逝:如果業務將要消逝或停用,就不需要實施了。

(4)“老成持重”:老--服役三年以上的應用;成--應用規模已經成型;持--應用場景還需要長時間維持;重--應用屬于系統中較重要的模塊。

二、微服務用到的技術

技術上,在阿里微服務的實踐過程中也不能免俗,基本上也是以下三個套路:

Docker

DDD

Middleware(Java)

此處雖然有所講解,但因主次問題,本文不多做介紹。

大家可以做一下擴展閱讀:

《2016.11.19 微服務實踐之路(廈門) 演講稿》, 次凌均閣(小馬哥微信公眾號)

另外,由于小馬哥提到Thymeleaf,所以網上查閱一些相關資料。在查閱時,在知乎上發現了一個beetl和Thymeleaf互懟的帖子,感覺很有意思。雖然只接觸過framemark和vue,以上兩種都沒有用到過,但是感覺這樣的討論能夠讓自己對一些技術有一些大致的印象。道理未必懂得,但學習都是由淺入深,是有一個過程的。

三、SpringApplication是什么?

SpringApplication 是 Spring Boot 驅動 Spring 應用上下文的引導類。

示例:

@SpringBootConfiguration

@EnableAutoConfiguration

@ComponentScan(excludeFilters = {

? ? ? ? @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),

? ? ? ? @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })

public @interface SpringBootApplication {

? ? ...

}

涉及到的注解:

@ComponentScan: 它是版本引入的? Spring Framework 3.1。

這里強調一種版本意識,版本差異所帶來的問題有時是很嚴重的。

@EnableAutoConfiguration: 激活自動裝配

這里擴展了一下,由@Enable,提到@Enable 開頭的:

@EnableWebMvc: 是使用Java 注解快捷配置Spring Webmvc的一個注解。

在使用該注解后配置一個繼承于WebMvcConfigurerAdapter的配置類即可配置好Spring Webmvc。

通過查看@EnableWebMvc的源碼,可以發現該注解就是為了引入一個DelegatingWebMvcConfiguration Java 配置類。并翻看DelegatingWebMvcConfiguration的源碼會發現該類似繼承于WebMvcConfigurationSupport的類。

其實不使用@EnableWebMvc注解也是可以實現配置Webmvc,只需要將配置類繼承于WebMvcConfigurationSupport類即可。

@EnableTransactionManagement: 開啟注解事務管理,等同于xml配置文件中的

@EnableAspectJAutoProxy: 表示開啟AOP代理自動配置。

如果配@EnableAspectJAutoProxy表示使用cglib進行代理對象的生成;設置@EnableAspectJAutoProxy(exposeProxy=true)表示通過aop框架暴露該代理對象,aopContext能夠訪問。

從@EnableAspectJAutoProxy的定義可以看得出,它引入AspectJAutoProxyRegister.class對象,該對象是基于注解@EnableAspectJAutoProxy注冊一個AnnotationAwareAspectJAutoProxyCreator,該對象通過調用AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);注冊一個aop代理對象生成器。

@EnableAsync: 表示開啟異步調用支持,配合@Async使用。

@SpringBootConfiguration: 等價于 @Configuration -> Configuration Class 注解

此處提個小疑問:

以下關系,大家應該都了解。

@SpringBootApplication= (默認屬性)@Configuration+@EnableAutoConfiguration+@ComponentScan

但是,我在實際項目使用中,使用@SpringBootApplication注解后,無法掃描到包,還需要使用@ComponentScan注解,是因為沒有在SpringApplication后指定掃描路徑?啟動類在外層,就算是掃描自身目錄和子包也應該可以找到,不知道大家有沒有遇到過類似的問題。

四、Spring?注解編程模型@Component

@Component

示例:

@Service

@Component

public @interface Service {

? ? ...

}

@Repository

@Component

public @interface Repository {

? ? ...

}

@Controller

@Component

public @interface Controller {

? ? ...

}

@Configuration

@Component

public @interface Configuration {

? ? ...

}

因為注解是沒有繼承關系的,所以小馬哥講之稱為@Component的“派生性”。

五、Spring?模式注解(Stereotype?Annotations)

Spring 注解驅動示例

注解驅動上下文AnnotationConfigApplicationContext,Spring Framework 3.0開始引入的

示例:

@Configuration

public class SpringAnnotationDemo {

? ? public static void main(String[] args) {

? ? ? ? //? XML 配置文件驅動? ? ? ClassPathXmlApplicationContext

? ? ? ? // Annotation 驅動

? ? ? ? // 找 BeanDefinition

? ? ? ? // @Bean @Configuration

? ? ? ? AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();

? ? ? ? // 注冊一個 Configuration Class = SpringAnnotationDemo

? ? ? ? context.register(SpringAnnotationDemo.class);

? ? ? ? // 上下文啟動

? ? ? ? context.refresh();

? ? ? ? System.out.println(context.getBean(SpringAnnotationDemo.class));

? ? }

}

這里講解了兩種Spring注解驅動方式,XML配置文件驅動和Annotation注解驅動,現在用的比較多的是Annotation驅動,經常聽到XML配置方式被群里的大佬們詬病。感覺小項目使用XML配置還可以,如果項目規模較大,一堆配置文件就會很頭疼了。

@SpringBootApplication 標注當前一些功能

下列列出的好像是層級關系,這里記的有些不清楚。

@SpringBootApplication

@SpringBootConfiguration

@Configuration

@Component

下面看一下 SpringApplication 也就是 Spring Boot 應用的引導方法。

基本寫法

SpringApplication springApplication = new SpringApplication(MicroservicesProjectApplication.class);

? ? ? ? Map properties = new LinkedHashMap<>();

? ? ? ? properties.put("server.port",0);

? ? ? ? springApplication.setDefaultProperties(properties);

? ? ? ? springApplication.run(args);

SpringApplicationBuilder

new SpringApplicationBuilder(MicroservicesProjectApplication.class) // Fluent API

? ? ? ? ? ? ? ? // 單元測試是 PORT = RANDOM

? ? ? ? ? ? ? ? .properties("server.port=0")? // 隨機向 OS 要可用端口

? ? ? ? ? ? ? ? .run(args);

小馬哥提到了兩種書寫風格,其中一種叫做Fluent API風格,另外一種沒有聽清。有沒有大佬知道上面寫法的名稱?

六、Spring?Boot?引導過程

示例:

@SpringBootApplication

public class MicroservicesProjectApplication {

? ? public static void main(String[] args) {

? ? ? ? SpringApplication springApplication = new SpringApplication(MicroservicesProjectApplication.class);

? ? ? ? Map properties = new LinkedHashMap<>();

? ? ? ? properties.put("server.port",0);

? ? ? ? springApplication.setDefaultProperties(properties);

? ? ? ? ConfigurableApplicationContext context = springApplication.run(args);

? ? ? ? // 有異常?

? ? ? ? System.out.println(context.getBean(MicroservicesProjectApplication.class));

? ? }

}

這個示例運行結果是正常的,springApplication.run(args)和context.getBean(MicroservicesProjectApplication.class)并不會有沖突異常。

如果沒有記錯,通過控制臺可以看出,運行了兩次。實際結果,有條件后運行一下代碼再下定論。

調整示例為 非 Web 程序

示例:

@SpringBootApplication

public class MicroservicesProjectApplication {

? ? public static void main(String[] args) {

? ? ? ? SpringApplication springApplication = new SpringApplication(MicroservicesProjectApplication.class);

? ? ? ? Map properties = new LinkedHashMap<>();

? ? ? ? properties.put("server.port", 0);

? ? ? ? springApplication.setDefaultProperties(properties);

? ? ? ? // 設置為 非 web 應用

? ? ? ? springApplication.setWebApplicationType(WebApplicationType.NONE);

? ? ? ? ConfigurableApplicationContext context = springApplication.run(args);

? ? ? ? // 有異常?

? ? ? ? System.out.println(context.getBean(MicroservicesProjectApplication.class));

? ? ? ? // 輸出當前 Spring Boot 應用的 ApplicationContext 的類名

? ? ? ? System.out.println("當前 Spring 應用上下文的類:" + context.getClass().getName());

? ? }

}

輸出結果:

當前 Spring 應用上下文的類:org.springframework.context.annotation.AnnotationConfigApplicationContext

配置 Spring Boot 源

SpringAppliation 類型推斷

當不加以設置 Web 類型,那么它采用推斷:

->SpringAppliation()->deduceWebApplicationType() 第一次推斷為WebApplicationType.SERVLET

WebApplicationType.NONE: 非 Web 類型

Servlet 不存在

Spring Web 應用上下文ConfigurableWebApplicationContext不存在

spring-boot-starter-web不存在

spring-boot-starter-webflux不存在

WebApplicationType.REACTIVE: Spring WebFlux

DispatcherHandler

spring-boot-starter-webflux存在

Servlet 不存在

spring-boot-starter-web不存在

WebApplicationType.SERVLET: Spring MVC

spring-boot-starter-web存在

人工干預 Web 類型

設置 webApplicationType 屬性 為WebApplicationType.NONE

七、SpringBoot事件

Spring 事件

(1)Spring 內部發送事件

事件發布:

ContextRefreshedEvent

ApplicationContextEvent

ApplicationEvent

refresh() ->finishRefresh()->publishEvent(new ContextRefreshedEvent(this));

事件關閉:

ContextClosedEvent

ApplicationContextEvent

ApplicationEvent

close()->doClose()->publishEvent(new ContextClosedEvent(this));

(2)自定義事件

PayloadApplicationEvent

Spring 事件 都是ApplicationEvent類型

發送 Spring 事件通過ApplicationEventMulticaster#multicastEvent(ApplicationEvent, ResolvableType)

Spring 事件的類型ApplicationEvent

Spring 事件監聽器ApplicationListener

Spring 事件廣播器ApplicationEventMulticaster

實現類:SimpleApplicationEventMulticaster

Spring 事件理解為消息

ApplicationEvent相當于消息內容

ApplicationListener相當于消息消費者、訂閱者

ApplicationEventMulticaster相當于消息生產者、發布者

注意:不能只發布事件,沒有監聽。否則,啟動程序是獲取不到任何事件的。

這里提到了“監聽者模式”,大家應該都會聯想到“觀察者模式”,兩者很相似,所以我去查閱了一些參考資料:

設計模式之監聽模式(觀察者模式與監聽模式區別)

設計模式之觀察者模式, 個人感覺相當的重要(七)

個人理解:

監聽者模式包含事件源、事件和監聽器,像是半自動化的監聽,需要手動或者說是人工對事件源和事件做處理,然后提供給監聽器;觀察者模式包含觀察者和被觀察者(也就是事件),像是自動化監聽,只需要對事件負責即可。

監聽者模式更加靈活,比較適用于需要自我把控的場景,比如底層框架的實現:spring框架的ApplicationEvent,ApplicationListener。

觀察者模式更加輕便,比較適用于約束比較固定的場景,比如前臺按鈕的點擊事件,至于,后臺用到觀察者模式的場景暫時沒有想到。

Spring Boot 事件監聽示例

示例:

@EnableAutoConfiguration

public class SpringBootEventDemo {

? ? public static void main(String[] args) {

? ? ? ? new SpringApplicationBuilder(SpringBootEventDemo.class)

? ? ? ? ? ? ? ? .listeners(event -> { // 增加監聽器

? ? ? ? ? ? ? ? ? ? System.err.println("監聽到事件 : " + event.getClass().getSimpleName());

? ? ? ? ? ? ? ? })

? ? ? ? ? ? ? ? .run(args)

? ? ? ? ? ? ? ? .close();

? ? ? ? ; // 運行

? ? }

}

通過運行程序可知,事件的運行順序(括號中是官方文檔中標注的順序,好像官方文檔中只提到了Application開頭的事件,沒有啟動過程中看到的Context和Servlet事件):

ApplicationStartingEvent(1) 啟動

ApplicationEnvironmentPreparedEvent(2) 環境配置

ApplicationPreparedEvent(3) 這里應該是

ContextRefreshedEvent 創建上下文context

ServletWebServerInitializedEvent

ApplicationStartedEvent(4)

ApplicationReadyEvent(5)

ContextClosedEvent

ApplicationFailedEvent (特殊情況)(6)

最后一種是啟動失敗的情況,所以做了特殊標注。

此外,這里提到了lambda表達式:

event -> { // 增加監聽器

? ? ? ? ? ? ? ? ? ? System.err.println("監聽到事件 : " + event.getClass().getSimpleName());

? ? ? ? ? ? ? ? }

之前沒有接觸過,在此貼上兩篇文章:

Java中Lambda表達式的使用

Lambda表達式詳細總結

Spring Boot 事件監聽器

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

ConfigFileApplicationListener監聽ApplicationEnvironmentPreparedEvent事件從而加載 application.properties 或者 application.yml 文件。

Spring Boot 很多組件依賴于 Spring Boot 事件監聽器實現,本質是 Spring Framework 事件/監聽機制。

SpringApplication利用:

Spring 應用上下文(ApplicationContext)生命周期控制 注解驅動 Bean

Spring 事件/監聽(ApplicationEventMulticaster)機制加載或者初始化組件

擴展問題:

q1:webApplicationType分為三種,都有什么實用地方

個人鄙見:

類型為SERVLET時,就是常用的web服務,受眼界所限,我了解的大部分項目都可以使用這種類型。是不是因為常用,所以才會將SERVLET設置為默認類型?

類型為NONE時,意味著該應用啟動后,不會加載任何東西。是不是在啟動時需要做情況判斷的時候可以用到?

類型為REACTIVE時,支持響應式編程,響應式編程是基于異步和事件驅動的非阻塞程序。

響應式編程技術上有RxJava等,可以提高代碼的抽象程度,讓開發者能夠更加專注于業務邏輯。我覺得響應式編程可以將業務拆解成細粒度的事件,一方面代碼維護性好(專注于業務邏輯,忽略具體實現),另一方面能夠更好地應對業務邏輯的更改(有可能只需要改變部分環節的事件邏輯)。

參考資料:

響應式編程介紹

響應式編程,是明智的選擇

響應式設計的優勢有哪些?

Rxjava(3.響應式編程好處)

q2:框架底層的事件是單線程么?業務實現是否可以使用事件去實現?如果使用事件實現是不是會有性能問題?

public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster {

? ? @Nullable

? ? private Executor taskExecutor;

? ? ...

}

個人鄙見:

從SpringBoot事件運行示例來看,是單線程、順序執行。至于使用事件來實現業務,這方面確實不甚清楚。

總結

本人野生碼農一枚,今日新手上路,不敢太過造次,也不想因自己不知所謂的看法誤導大家。文中的問題,待我證實以后,再做修改。

謹此,止筆。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,533評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,055評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 175,365評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,561評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,346評論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,889評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,978評論 3 439
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,118評論 0 286
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,637評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,558評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,739評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,246評論 5 355
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 43,980評論 3 346
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,362評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,619評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,347評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,702評論 2 370

推薦閱讀更多精彩內容