0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

委派模式——從SLF4J說起

OSC開源社區(qū) ? 來源:OSC開源社區(qū) ? 2023-01-31 15:12 ? 次閱讀

將某個(gè)通用解決方案包裝成成熟的工具包,是每一個(gè)技術(shù)建設(shè)工作者必須思考且必須解決的問題。本文從業(yè)內(nèi)流行的既有工具包入手,解析實(shí)現(xiàn)思路,沉淀一般方法。為技術(shù)建設(shè)的初學(xué)者提供一些實(shí)踐思路的參考。尤其是文中提倡的“去中心化”的協(xié)作模式,和“關(guān)鍵鏈路+開發(fā)接口”的開發(fā)模式,具有一定的實(shí)際落地意義。當(dāng)然本文在行文中,不可避免存在一定主觀偏見性,讀者可酌情閱讀。

一、前言

熟悉JAVA服務(wù)器開發(fā)的同學(xué)應(yīng)該都使用過日志模塊,并且大概率使用過"log4j-over-slf4j"和“slf4j-log4j”這兩個(gè)包。那么這兩個(gè)包的區(qū)別是什么?為什么會(huì)互相引用包含呢?這篇文章會(huì)解釋下這幾個(gè)概念的區(qū)別。

首先說一下SLF4J

二、從SLF4J開始

SLF4J全稱"Simple Logging Facade for Java (SLF4J)", 它誕生之初的目的,是為了針對(duì)不同的log解決方案,提供一套統(tǒng)一的接口適配標(biāo)準(zhǔn),從而讓業(yè)務(wù)代碼無須關(guān)心使用到的第三方模塊都使用了哪些log方案。

舉個(gè)例子, Apache Dubbo和RabbitMQ使用到的日志模塊便不相同。從某種意義上而言,SLF4J只是一個(gè)facade,類似于當(dāng)年的ODBC(針對(duì)不同的數(shù)據(jù)庫廠商而制定的統(tǒng)一接口標(biāo)準(zhǔn), 下文會(huì)涉及到)。而這個(gè)facade對(duì)應(yīng)的包名,是 “slf4j-api-xxx.xxx.xxx.jar”。所以,當(dāng)你應(yīng)用了"slf4j-api-xxx.jar"的包時(shí),其實(shí)只是引入了一個(gè)日志接口標(biāo)準(zhǔn),而并沒有引入日志具體實(shí)現(xiàn)。

2.1、業(yè)內(nèi)實(shí)現(xiàn)

SLF4J標(biāo)準(zhǔn)在應(yīng)用層的核心類,就是兩個(gè): org.slf4j.Logger 和 org.slf4j.LoggerFactory。其中,自版本1.6.0后,如果并沒有具體的實(shí)現(xiàn),slf4j-api會(huì)默認(rèn)提供一個(gè)啥也不干的Logger實(shí)現(xiàn)(org.slf4j.helpers.NOPLogger)。

在當(dāng)前(本稿件于2022-03-01擬制)的市面上,既有的實(shí)現(xiàn)SLF4J的方案有以下幾種:

f2eb3268-a11e-11ed-bfe3-dac502259ad0.jpg

整體層次如下圖:

f2f9e808-a11e-11ed-bfe3-dac502259ad0.jpg

綜上而言:以SLF4J-開頭的jar包,一般指的是采用某種第三方框架實(shí)現(xiàn)的slf4j解決方案。

2.2 工作機(jī)制

那么整個(gè)SLF4J的工作機(jī)制是如何運(yùn)作的呢,換句話說,系統(tǒng)是如何知道應(yīng)該使用哪個(gè)實(shí)現(xiàn)方案的呢?

對(duì)于那種不需要適配器的原生實(shí)現(xiàn)方式,直接引入對(duì)應(yīng)的包即可。

對(duì)于那種需要適配器的委托式實(shí)現(xiàn)方式,則需要通過另外的一個(gè)渠道來告知SLF4J應(yīng)該使用哪個(gè)實(shí)現(xiàn)類: SPI機(jī)制。

舉個(gè)例子,我們看一下slf4j-log4j的包結(jié)構(gòu):

f308e8bc-a11e-11ed-bfe3-dac502259ad0.png

我們先看pom文件,就包含兩個(gè)依賴:

<dependency>
<groupId>org.slf4jgroupId>
<artifactId>slf4j-apiartifactId>
dependency>


<dependency>
<groupId>log4jgroupId>
<artifactId>log4jartifactId>
dependency>

slf4j-log4j同時(shí)引入了slf4j-api和log4j。那么slf4j-log4j本身的作用不言而喻:使用LOG4J的功能,實(shí)現(xiàn)SLF4J的接口標(biāo)準(zhǔn)。


整體的接口/類關(guān)系路徑如下圖:

f31f750a-a11e-11ed-bfe3-dac502259ad0.jpg

但是這仍然沒有解決本章節(jié)開始提出的問題(程序怎么知道應(yīng)該用哪個(gè)Logger)。

可以從源碼入手:(slf4j/slf4j-log4j12 at master · qos-ch/slf4j · GitHub),我們看到了以下關(guān)鍵的文件:

f32f0308-a11e-11ed-bfe3-dac502259ad0.png

也就是說:slf4j-log4j使用了java的SPI機(jī)制告知JVM在運(yùn)行時(shí)調(diào)用具體哪一個(gè)實(shí)現(xiàn)類。由于SPI機(jī)制暫不屬于本文章討論范圍,讀者可以去官網(wǎng)獲取信息。


讀者可以去GitHub - qos-ch/slf4j: Simple Logging Facade for Java看其他的實(shí)現(xiàn)方式的適配器是如何工作的。


那么本章開始的問題答案便是:

  1. SLF4J制定一套日志打印流程,然后把核心類抽象出接口給外部去實(shí)現(xiàn);

  2. 適配器使用第三方日志組件實(shí)現(xiàn)了這些核心類接口,并采用SPI機(jī)制,讓JAVA運(yùn)行時(shí)意識(shí)到核心接口的具體實(shí)現(xiàn)類。

而上述兩點(diǎn),構(gòu)成了本文接下來要講述的知識(shí)點(diǎn):委派模式。

三、委派模式

從上文中,我們從SLF4J的案例,引出了"委派模式"這個(gè)概念,下面我們就重點(diǎn)討論委派模式(delegation)。

接下來我們按照認(rèn)知流程,依次從三個(gè)問題,解釋委派模式:

  • 為什么使用委派模式

  • 什么是委派模式

  • 如何使用委派模式

然后會(huì)在下一章,用業(yè)內(nèi)的典型案例,分析委派模式的使用情況。

3.1 為什么采用委派模式?

我們回到SLF4J。為什么它會(huì)用委派模式呢?因?yàn)槿罩敬蛴」δ艽嬖诟鞣N不同的實(shí)現(xiàn)方式。對(duì)于應(yīng)用開發(fā)者而言,最好需要一個(gè)標(biāo)準(zhǔn)的打印流程,其他第三方組件可以在某些地方有些不同,但是核心流程是最好不要變。對(duì)于標(biāo)準(zhǔn)制定者 而言,他無法控制每一個(gè)第三方組件的所有細(xì)節(jié),所以只能暴露出有限的自定制能力。

而我們放大到軟件領(lǐng)域,或者在互聯(lián)網(wǎng)開發(fā)領(lǐng)域,不同的開發(fā)者的協(xié)作模式,主要靠jar包應(yīng)用:第三方開發(fā)一個(gè)工具包,放在中心倉庫中(maven, gradle), 使用者從其他信息渠道(csdn, stackoverflow等等)根據(jù)問題定位到這個(gè)jar包,然后在代碼工程中引用。理論上,如果這個(gè)第三方j(luò)ar包很穩(wěn)定(例如c3p0),那么該jar包的維護(hù)者就很少甚至幾乎不會(huì)和使用者建立聯(lián)系。如果某些中間件開發(fā)者覺得不滿足自己公司/部門的需求,會(huì)根據(jù)該jar包再做一次自定義封裝。

縱觀上述整個(gè)過程,不難發(fā)現(xiàn)兩點(diǎn):

  1. 工具包開發(fā)者和使用者沒有建立穩(wěn)定的協(xié)同渠道

  2. 工具包開發(fā)者對(duì)自己成品的發(fā)展掌控很薄弱

那么如果有人想要建立一套標(biāo)準(zhǔn)呢?比如log標(biāo)準(zhǔn),比如數(shù)據(jù)庫連接標(biāo)準(zhǔn),那么只能有幾個(gè)大公司聯(lián)盟,或者著名的開發(fā)團(tuán)隊(duì)聯(lián)盟,制定一個(gè)標(biāo)準(zhǔn),并實(shí)現(xiàn)其中核心鏈路部分。至于為什么不實(shí)現(xiàn)全部鏈路,原因也很簡單:軟件領(lǐng)域的協(xié)同本身就是弱中心化的 ,否則你不帶別人玩,別人也不會(huì)采用你的標(biāo)準(zhǔn)(參考當(dāng)年IBM推廣的COBOL)。

綜上而言:委派模式是基于當(dāng)前軟件領(lǐng)域的協(xié)作特性,采取的較好的軟件結(jié)構(gòu)模式。

f33b8f74-a11e-11ed-bfe3-dac502259ad0.jpg

所以啥時(shí)候采用委派模式呢?

  • 存在設(shè)定某個(gè)標(biāo)準(zhǔn)并由中心化團(tuán)隊(duì)負(fù)責(zé)的必要

  • 使用者有強(qiáng)烈的需求自定制某些局部實(shí)現(xiàn)

這里就舉一個(gè)硬件領(lǐng)域的反例:快充標(biāo)準(zhǔn)。在2018年甚至更早,消費(fèi)者就需要一個(gè)快充的功能。但是快充需要定制很多硬件才能實(shí)現(xiàn),所以此時(shí)就具備了條件一,但是當(dāng)時(shí)并沒有任何一個(gè)團(tuán)隊(duì)或者公司能夠掌控安卓手機(jī)硬件整個(gè)生態(tài),無法共同推出一個(gè)中心化團(tuán)隊(duì)去負(fù)責(zé),從而導(dǎo)致各個(gè)手機(jī)廠商的快充功能百花齊放:A公司的快充線,無法給B公司的手機(jī)快充。

3.2 什么是委派模式?

基于上述的討論,委派模式的核心構(gòu)成就顯而易見了:核心鏈路,開放接口。

核心鏈路指的是:為了達(dá)到某個(gè)目的,特定的一組構(gòu)件,按照特定的順序,特定的協(xié)同標(biāo)準(zhǔn),共同執(zhí)行計(jì)算的邏輯。

開放接口指的是:給定特定的輸入和輸出,將實(shí)現(xiàn)細(xì)節(jié)交給外部的功能接口。

舉個(gè)比較現(xiàn)實(shí)的例子:傳統(tǒng)汽車。

幾乎每一輛傳統(tǒng)汽車,都按照三大件進(jìn)行集成和協(xié)作:發(fā)動(dòng)機(jī),變速器,底盤。發(fā)動(dòng)機(jī)做功, 通過變速器將動(dòng)力傳輸給底盤(這么說并不標(biāo)準(zhǔn),甚至在汽車工業(yè)的工人眼中,這種描述幾乎是謬論,但是大致是這樣)。也基于此,發(fā)動(dòng)機(jī)的接口, 變速箱的接口,底盤的接口都已經(jīng)固定,剩下的就各個(gè)廠商去實(shí)現(xiàn)了:三菱的發(fā)動(dòng)機(jī), 日產(chǎn)的發(fā)動(dòng)機(jī),愛信的變速箱,采埃孚的變速箱,倫福德的底盤,天合的底盤等等。甚至連輪胎的接口都制定好了:大陸的輪胎,普利司通的輪胎,固特異的輪胎。

不同的汽車廠商,選擇不同公司的組件,集成出某個(gè)汽車型號(hào)。當(dāng)然也有公司自己去實(shí)現(xiàn)某個(gè)標(biāo)準(zhǔn):比如大眾自己生產(chǎn)EA888發(fā)動(dòng)機(jī),PSA自己生產(chǎn)并調(diào)教的底盤并引以為傲。

如果大家覺得不夠熟悉,那么可以舉一個(gè)tomcat的例子。

經(jīng)歷過00年代的軟件開發(fā)者,應(yīng)該知道當(dāng)時(shí)開發(fā)一個(gè)web應(yīng)用是多么的困難:如何監(jiān)聽socket, 如何編碼解碼,如何處理并發(fā),如何管理進(jìn)程等等。但是有一點(diǎn)是共通的:每一個(gè)Web開發(fā)者都想要一個(gè)框架去管理整個(gè)http服務(wù)的協(xié)議層和內(nèi)核層。于是出現(xiàn)了JBoss, WebSphere, Tomcat(笑到了最后)。

這些產(chǎn)品,都是指定了核心的鏈路:監(jiān)聽socket → 讀數(shù)據(jù)包→ 封裝成http報(bào)文→ 派發(fā)給處理池子→ 處理池的線程調(diào)用處理邏輯去處理→ 編碼返回的報(bào)文→ 編組成tcp包→ 調(diào)用內(nèi)核函數(shù)→ 發(fā)出數(shù)據(jù)。

基于這個(gè)核心鏈路,制定標(biāo)準(zhǔn):業(yè)務(wù)處理邏輯的輸入是什么,輸出是什么,如何讓web框架識(shí)別到業(yè)務(wù)處理模塊。

Tomcat的方案就是web.xml。開發(fā)者只要遵從web.xml標(biāo)準(zhǔn)去實(shí)現(xiàn)servlet即可。也就是說,在整個(gè)http服務(wù)器鏈路中,Tomcat將特定的幾個(gè)流程處理構(gòu)件(listener, filter, interceptor, servlet)委派給了業(yè)務(wù)開發(fā)者去實(shí)現(xiàn)。

3.3 如何使用委派模式

在使用委派模式之前,先根據(jù)上文的模式匹配條件進(jìn)行自我判斷:

  • 存在設(shè)定某個(gè)標(biāo)準(zhǔn)并由中心化團(tuán)隊(duì)負(fù)責(zé)的必要

  • 使用者有強(qiáng)烈的需求自定制某些局部實(shí)現(xiàn)

如果并不符合條件一,那么就不需要考慮使用委派模式;如果符合條件一但是不符合條件二,那就先預(yù)留好接口,采用依賴注入的方式,自己開發(fā)接口實(shí)現(xiàn)類并注入到主流程中。這個(gè)做法在很多的第三方依賴包中能夠看到,比如spring的BeanFactory, BeanAware等等,還有各個(gè)公司開發(fā)SSO時(shí)預(yù)留的一些hook和filter等等。

在確定使用委派模式后,第一件事就是“確定核心鏈路”,這一步最難,因?yàn)橥褂谜叨加心撤N期望,但是讓他們具體描述出來,卻又經(jīng)常不夠精準(zhǔn),甚至有時(shí)候后主次顛倒。筆者的建議是:直接讓他們說出原始的需求/痛點(diǎn),然后自己嘗試給出方案,再對(duì)比他們的方案,進(jìn)行溝通,并逐漸將兩個(gè)方案統(tǒng)一。統(tǒng)一的過程也就是不斷試探和確定的過程。

f349846c-a11e-11ed-bfe3-dac502259ad0.jpg

上述的過程是筆者自己的經(jīng)驗(yàn),僅當(dāng)借鑒。

在確定核心流程后,再將流程中的一些需要自定制的功能抽象成接口暴露出去。接口的定義中,盡量減少對(duì)整個(gè)流程中其他類的調(diào)用依賴。

所以整體的流程分為三步:確認(rèn)使用該模式;提取核心流程;抽象開放接口。

至于是采用SPI機(jī)制還是像TOMCAT一樣使用XML配置識(shí)別,需要看具體情況,在此不做涉及。

四、 業(yè)內(nèi)案例

4.1 JDBC

JDBC的誕生很大程度上是借鑒了ODBC的思想,為JAVA設(shè)計(jì)了專用的數(shù)據(jù)庫連接規(guī)范JDBC(JAVA Database Connectivity)。JDBC期望的目標(biāo)是讓Java開發(fā)人員在編寫數(shù)據(jù)庫應(yīng)用程序時(shí),可以有統(tǒng)一的接口,無須依賴特定數(shù)據(jù)庫API,達(dá)到“ 一次開發(fā),適用所有數(shù)據(jù)庫”。雖然實(shí)際開發(fā)中,經(jīng)常會(huì)因?yàn)槭褂昧藬?shù)據(jù)庫特定的語法、數(shù)據(jù)類型或函數(shù)等而無法達(dá)到目標(biāo),但JDBC的標(biāo)準(zhǔn)還是大大簡化了開發(fā)工作。

整體而言,JDBC的接入結(jié)構(gòu)大致如下圖:

f3593b14-a11e-11ed-bfe3-dac502259ad0.jpg

但是實(shí)際上,在JDBC誕生之初,市面上并未有很多的廠家響應(yīng)SUN公司(那時(shí)候SUN還并未被ORACLE收購), 于是SUN公司就使用了本文介紹的橋接模式,如下圖:

f3706b18-a11e-11ed-bfe3-dac502259ad0.jpg

也是說,形式上,出現(xiàn)了初步委派的結(jié)構(gòu)形式。

下文會(huì)只針對(duì)單次委托的JDBC層級(jí)做分析。

按照上文所言,每一個(gè)委派結(jié)構(gòu),必然存在兩個(gè)要素:核心路徑和開放接口。我們從這兩個(gè)維度開始分析JDBC。

JDBC的核心路徑分為六步, 包含委托機(jī)制需要的兩步(引入包,聲明委托承接人),總共八步,如下:

  1. 引入JDBC實(shí)現(xiàn)包

  2. 注冊(cè)JDBC Driver

  3. 和數(shù)據(jù)庫建立連接

  4. 發(fā)起transaction(必要的話),創(chuàng)建statement

  5. 執(zhí)行statement并讀取返回,塞入ResultSet

  6. 處理ResultSet

  7. 關(guān)閉ResultSet, 關(guān)閉Statement

  8. 關(guān)閉Connection

縱觀整個(gè)過程,核心的參與者為:Driver, Connection, Statement, ResultSet。transaction實(shí)際上是基于Connection的三個(gè)方法(setAutoCommit, commit, rollback)包裝而成的會(huì)話層,理論上不屬于標(biāo)準(zhǔn)層。

以mysql-connector-java為例,具體實(shí)現(xiàn)JDBC接口的情況如下:

f37d12e6-a11e-11ed-bfe3-dac502259ad0.jpg

通過Java自帶的overriding機(jī)制,只要使用com.mysql.jdbc.Driver,那么其他組件的實(shí)現(xiàn)類便直接被應(yīng)用實(shí)現(xiàn)。具體細(xì)節(jié)不做討論。那么mysql-connector-java是如何告知JVM應(yīng)該使用com.mysql.jdbc.Driver呢?

兩種模式

  1. 明文模式——在業(yè)務(wù)代碼中明文使用Class.forName("com.mysql.jdbc.Driver")

  2. SPA機(jī)制

其實(shí)上述的兩種方法,核心就是初始化com.mysql.jdbc.Driver,執(zhí)行以下類初始化邏輯。

try {
    DriverManager.registerDriver(new Driver());
} catch (SQLException var1) {
    throw new RuntimeException("Can't register driver!");
}

也就是說,JDBC通過DriveManager維護(hù)委托承接者的信息。讀者如果有興趣查看DriverManager的源碼,會(huì)發(fā)現(xiàn)JDBC的另一種實(shí)現(xiàn)類發(fā)現(xiàn)方式。不過考慮行文長度,筆者在此不表。

4.2 Apach Dubbo

Dubbo的核心路徑大致如下(不考慮服務(wù)管理那一套):

consumer調(diào)用→參數(shù)序列化網(wǎng)絡(luò)請(qǐng)接收請(qǐng)求→ 參數(shù)反序列化→ provider計(jì)算并返回→結(jié)果序列化→ 網(wǎng)絡(luò)返回consumer方接收結(jié)果反序列化

(斜體代表consumer方的dubbo職責(zé),下劃線代表provider方的dubbo職責(zé))

Dubbo的可定制接口有很多,整體大量采用了“類SPI”機(jī)制,為整個(gè)RPC流程的很多環(huán)節(jié),提供了自定制的注入機(jī)制。相較于傳統(tǒng)的Java SPI, Dubbo SPI在封裝性和實(shí)現(xiàn)類發(fā)現(xiàn)性上做了很多的擴(kuò)展和自定制。

Dubbo SPI整體實(shí)現(xiàn)機(jī)制及工作機(jī)制不在本文范圍,但為了行文方便,在此做一些必要說明。整體的Dubbo SPI機(jī)制可以分為三部分:

  • @SPI注解——聲明當(dāng)前接口類為可擴(kuò)展接口。

  • @Adaptive注解——聲明當(dāng)前接口類(或者當(dāng)前接口類的當(dāng)前方法)能根據(jù)特定條件(注解中的value),動(dòng)態(tài)調(diào)用具體實(shí)現(xiàn)類實(shí)現(xiàn)方法。

  • @Activate注解——聲明當(dāng)前類/方法實(shí)現(xiàn)了某個(gè)可擴(kuò)展接口(或者可擴(kuò)展接口的某個(gè)具體方法的實(shí)現(xiàn)),并注明被激活的條件,以及所有的被激活實(shí)現(xiàn)類中的排序信息。

我們以Dubbo-Auth

(dubbo/dubbo-plugin/dubbo-auth at 3.0 · apache/dubbo · GitHub)為例,從核心路徑和開放接口兩個(gè)維度進(jìn)行分析。

Dubbo-Auth的實(shí)現(xiàn)邏輯,是基于Dubbo-filter的原理,也就是說:Dubbo-Auth本身就是Dubbo整體流程中的某一個(gè)環(huán)節(jié)的委派實(shí)現(xiàn)方。

Dubbo-Auth的核心入口(也就是核心路徑的起始點(diǎn)), 是ProviderAuthFilter,

是org.apache.dubbo.auth.filter的具體實(shí)現(xiàn), 也就是說:

  1. org.apache.dubbo.auth.filter是dubbo核心鏈路中對(duì)外暴露的一個(gè)開發(fā)接口(類定義上標(biāo)注了@SPI)。

  2. ProviderAuthFilter是實(shí)現(xiàn)了dubbo核心鏈路中對(duì)外暴露的開發(fā)接口Filter(ProviderAuthFilter實(shí)現(xiàn)類定義上標(biāo)注了@Activate)。

ProviderAuthFilter的核心路徑比較簡單:獲取Authenticator對(duì)象,使用Authenticator對(duì)象進(jìn)行auth驗(yàn)證。

具體代碼如下:

@Activate(group = CommonConstants.PROVIDER, order = -10000)
public class ProviderAuthFilter implements Filter {
 
    @Override
    public Result invoke(Invoker invoker, Invocation invocation) throws RpcException {
        URL url = invoker.getUrl();
        boolean shouldAuth = url.getParameter(Constants.SERVICE_AUTH, false);
        if (shouldAuth) {
            Authenticator authenticator = ExtensionLoader.getExtensionLoader(Authenticator.class)
                    .getExtension(url.getParameter(Constants.AUTHENTICATOR, Constants.DEFAULT_AUTHENTICATOR));
            try {
                authenticator.authenticate(invocation, url);
            } catch (Exception e) {
                return AsyncRpcResult.newDefaultAsyncResult(e, invocation);
            }
        }
        return invoker.invoke(invocation);
    }
}

注意,上文代碼中

url.getParameter(Constants.AUTHENTICATOR, Constants.DEFAULT_AUTHENTICATOR)

是dubbo spi 的Adaptive機(jī)制中的選擇條件,讀者可以深究,本文在此略過。

由于核心路徑包含了Authenticator ,那么Authenticator 自然就很可能是對(duì)外暴露的開發(fā)接口了。也就是說,Authenticator 的聲明類中,必然是注解了@SPI。

@SPI("accessKey")
public interface Authenticator {
 
    /**
     * give a sign to request
     *
     * @param invocation
     * @param url
     */
    void sign(Invocation invocation, URL url);
 
 
    /**
     * verify the signature of the request is valid or not
     * @param invocation
     * @param url
     * @throws RpcAuthenticationException when failed to authenticate current invocation
     */
    void authenticate(Invocation invocation, URL url) throws RpcAuthenticationException;
}

上述代碼證明了筆者的猜想。

在Dubbo-Auth中,提供了一個(gè)默認(rèn)的Authenticator :AccessKeyAuthenticator。在這個(gè)實(shí)現(xiàn)類中,核心路徑被再次具體化:

  1. 獲取accessKeyPai;

  2. 使用accessKeyPair, 計(jì)算簽名;

  3. 對(duì)比請(qǐng)求中的簽名和計(jì)算的簽名是否相同。

在此核心路徑中,由于引入了accessKeyPair概念,于是就引出一個(gè)環(huán)節(jié):如何獲取accessKeyPair, 針對(duì)此, dubbo-auth又定義了一個(gè)開放接口:AccessKeyStorage。

@SPI
public interface AccessKeyStorage {
 
    /**
     * get AccessKeyPair of this request
     *
     * @param url
     * @param invocation
     * @return
     */
    AccessKeyPair getAccessKey(URL url, Invocation invocation);
}

4.3 LOG4J

最后一個(gè)案例,我們又回到了日志組件,而之所以介紹LOG4J, 是由于它使用了非常規(guī)的“反向委派”機(jī)制。

LOG4J借鑒了SLF4J的思想(或者LOG4J在前?SLF4J借鑒的LOG4J ?), 也采用了 接口標(biāo)準(zhǔn)+ 適配器+第三方方案的思路來實(shí)現(xiàn)委派。

f38a2a62-a11e-11ed-bfe3-dac502259ad0.jpg

那么顯然,這里就有個(gè)問題:SLF4J確認(rèn)了自己的核心路徑,然后暴露出待實(shí)現(xiàn)接口,SLF4J-LOG4J在嘗試實(shí)現(xiàn)SLF4J的待實(shí)現(xiàn)接口時(shí),又使用了委托機(jī)制,把相關(guān)的路徑細(xì)節(jié)外包了出去,從而形成了一個(gè)環(huán)。

f39b7664-a11e-11ed-bfe3-dac502259ad0.jpg

所以說,如果我同時(shí)引入了"log4j-over-slf4j"和"slf4j-log4j",會(huì)造成stackoverflow。

這個(gè)問題非常典型, google一下就可能看到很多的案例,比如Analysis of stack overflow exception of log4j-over-slf4j and slf4j-log4j12 coexistence - actorsfit等等, 官方也給出了警告(SLF4J Error Codes)。

由于此文的關(guān)注點(diǎn)在委派模式,所以關(guān)于此問題并不詳細(xì)討論。而此案例的重點(diǎn),是說明了一件事:委派模式的缺點(diǎn),就是對(duì)于開放接口的實(shí)現(xiàn)邏輯不可控。如果第三方實(shí)現(xiàn)存在重大機(jī)制性隱患,會(huì)導(dǎo)致整體核心流程出現(xiàn)問題。

五、總結(jié)

綜上所述,

委派模式的使用場景是:

  • 存在設(shè)定某個(gè)標(biāo)準(zhǔn)并由中心化團(tuán)隊(duì)負(fù)責(zé)的必要;

  • 使用者有強(qiáng)烈的需求自定制某些局部實(shí)現(xiàn)。

委派模式的核心點(diǎn) 核心路徑, 開放接口

委派模式的隱藏機(jī)制實(shí)現(xiàn)方式的注冊(cè)/發(fā)現(xiàn)。

審核編輯 :李倩


聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 模塊
    +關(guān)注

    關(guān)注

    7

    文章

    2626

    瀏覽量

    47211
  • 適配器
    +關(guān)注

    關(guān)注

    8

    文章

    1899

    瀏覽量

    67766
  • 服務(wù)器
    +關(guān)注

    關(guān)注

    12

    文章

    8842

    瀏覽量

    84945

原文標(biāo)題:委派模式——從SLF4J說起

文章出處:【微信號(hào):OSC開源社區(qū),微信公眾號(hào):OSC開源社區(qū)】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏

    評(píng)論

    相關(guān)推薦

    配置bq275054-J4數(shù)據(jù)閃存

    電子發(fā)燒友網(wǎng)站提供《配置bq275054-J4數(shù)據(jù)閃存.pdf》資料免費(fèi)下載
    發(fā)表于 10-17 11:32 ?0次下載
    配置bq275054-<b class='flag-5'>J4</b>數(shù)據(jù)閃存

    bq27505-J3到bq27505-J4變更列表

    電子發(fā)燒友網(wǎng)站提供《bq27505-J3到bq27505-J4變更列表.pdf》資料免費(fèi)下載
    發(fā)表于 10-17 11:23 ?0次下載
    bq27505-<b class='flag-5'>J</b>3到bq27505-<b class='flag-5'>J4</b>變更列表

    PCM1808的主模式模式在應(yīng)用上有什么區(qū)別?用模式可以測量MIC獲取到的音量是多少分貝嗎?

    PCM1808的主模式模式在應(yīng)用上有什么區(qū)別?用模式可以測量MIC獲取到的音量是多少分
    發(fā)表于 10-11 08:00

    DAC39J84使用插值模式遇到的疑問求解

    我在使用DAC39J84這款芯片時(shí),目前使用插值模式,使用8411模式,外部pll時(shí)鐘輸入2.4G;遇到的問題是在2插值模式下,配置芯片是有DAC輸出信號(hào)的,但在是用
    發(fā)表于 08-14 08:28

    日志框架簡介-Slf4j+Logback入門實(shí)踐

    前言 隨著互聯(lián)網(wǎng)和大數(shù)據(jù)的迅猛發(fā)展,分布式日志系統(tǒng)和日志分析系統(tǒng)已廣泛應(yīng)用,幾乎所有應(yīng)用程序都使用各種日志框架記錄程序運(yùn)行信息。因此,作為工程師,了解主流的日志記錄框架非常重要。雖然應(yīng)用程序的運(yùn)行結(jié)果不受日志的有無影響,但沒有日志的應(yīng)用程序是不完整的,甚至可以說是有缺陷的。優(yōu)秀的日志系統(tǒng)可以 記錄操作軌跡 、 監(jiān)控系統(tǒng)運(yùn)行狀態(tài) 和 解決系統(tǒng)故障 。 Java 日志框架進(jìn)化史 早期 Java 日志框架沒有制定統(tǒng)一的標(biāo)準(zhǔn),使得很
    的頭像 發(fā)表于 07-30 10:00 ?969次閱讀
    日志框架簡介-<b class='flag-5'>Slf4j</b>+Logback入門實(shí)踐

    ADC32J4x模數(shù)轉(zhuǎn)換器數(shù)據(jù)表

    電子發(fā)燒友網(wǎng)站提供《ADC32J4x模數(shù)轉(zhuǎn)換器數(shù)據(jù)表.pdf》資料免費(fèi)下載
    發(fā)表于 07-18 10:39 ?0次下載
    ADC32<b class='flag-5'>J4</b>x模數(shù)轉(zhuǎn)換器數(shù)據(jù)表

    系統(tǒng)側(cè)阻抗軌道燃油表儀器bq27505-J4系統(tǒng)數(shù)據(jù)表

    電子發(fā)燒友網(wǎng)站提供《系統(tǒng)側(cè)阻抗軌道燃油表儀器bq27505-J4系統(tǒng)數(shù)據(jù)表.pdf》資料免費(fèi)下載
    發(fā)表于 03-28 10:17 ?0次下載
    系統(tǒng)側(cè)阻抗軌道燃油表儀器bq27505-<b class='flag-5'>J4</b>系統(tǒng)數(shù)據(jù)表

    具有D-CAP3?控制模式和0.9V基準(zhǔn)電壓的TPS54J060 4VIN至16VIN、6A同步降壓轉(zhuǎn)換器數(shù)據(jù)表

    電子發(fā)燒友網(wǎng)站提供《具有D-CAP3?控制模式和0.9V基準(zhǔn)電壓的TPS54J060 4VIN至16VIN、6A同步降壓轉(zhuǎn)換器數(shù)據(jù)表.pdf》資料免費(fèi)下載
    發(fā)表于 03-22 10:31 ?0次下載
    具有D-CAP3?控制<b class='flag-5'>模式</b>和0.9V基準(zhǔn)電壓的TPS54<b class='flag-5'>J</b>060 <b class='flag-5'>4</b>VIN至16VIN、6A同步降壓轉(zhuǎn)換器數(shù)據(jù)表

    具有D-CAP3?控制模式和0.6V基準(zhǔn)電壓的TPS54J061 4VIN至16VIN、6A同步降壓轉(zhuǎn)換器數(shù)據(jù)表

    電子發(fā)燒友網(wǎng)站提供《具有D-CAP3?控制模式和0.6V基準(zhǔn)電壓的TPS54J061 4VIN至16VIN、6A同步降壓轉(zhuǎn)換器數(shù)據(jù)表.pdf》資料免費(fèi)下載
    發(fā)表于 03-21 14:11 ?0次下載
    具有D-CAP3?控制<b class='flag-5'>模式</b>和0.6V基準(zhǔn)電壓的TPS54<b class='flag-5'>J</b>061 <b class='flag-5'>4</b>VIN至16VIN、6A同步降壓轉(zhuǎn)換器數(shù)據(jù)表

    CKS32F4xx系列低功耗模式STOP模式

    CKS32F4xx系列低功耗模式STOP模式
    的頭像 發(fā)表于 11-06 17:08 ?557次閱讀
    CKS32F<b class='flag-5'>4</b>xx系列低功耗<b class='flag-5'>模式</b>STOP<b class='flag-5'>模式</b>

    CKS32F4xx系列低功耗模式SLEEP模式

    CKS32F4xx系列低功耗模式SLEEP模式
    的頭像 發(fā)表于 11-06 16:59 ?614次閱讀
    CKS32F<b class='flag-5'>4</b>xx系列低功耗<b class='flag-5'>模式</b>SLEEP<b class='flag-5'>模式</b>

    CKS32F4xx系列低功耗模式STANDBY模式

    CKS32F4xx系列低功耗模式STANDBY模式
    的頭像 發(fā)表于 11-06 16:57 ?420次閱讀
    CKS32F<b class='flag-5'>4</b>xx系列低功耗<b class='flag-5'>模式</b>STANDBY<b class='flag-5'>模式</b>

    AT32F4xx I2C使用輪詢模式進(jìn)行主機(jī)發(fā)送機(jī)接收

    AT32F4xx I2C使用輪詢模式進(jìn)行主機(jī)發(fā)送機(jī)接收演示AT32F403Axx I2C使用輪詢模式進(jìn)行主機(jī)發(fā)送機(jī)接收,其余系列的使用方
    發(fā)表于 10-27 08:14

    AT32F4xx I2C使用輪詢模式進(jìn)行主機(jī)接收機(jī)發(fā)送

    AT32F4xx I2C使用輪詢模式進(jìn)行主機(jī)接收機(jī)發(fā)送演示AT32F403Axx I2C使用輪詢模式進(jìn)行主機(jī)接收機(jī)發(fā)送,其余系列使用方式
    發(fā)表于 10-27 06:07

    使用MM32F0270 LPTIMSTOP模式喚醒

    使用MM32F0270 LPTIMSTOP模式喚醒
    的頭像 發(fā)表于 10-26 16:46 ?885次閱讀
    使用MM32F0270 LPTIM<b class='flag-5'>從</b>STOP<b class='flag-5'>模式</b>喚醒