最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

詳解log4j-over-slf4j與slf4j-log4j12共存stack overflow異常分析

 更新時(shí)間:2018年07月10日 09:59:40   作者:kxcfzyk  
這篇文章主要介紹了詳解log4j-over-slf4j與slf4j-log4j12共存stack overflow異常分析,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧

注:下文中的“橋接”、“轉(zhuǎn)調(diào)”、“綁定”等詞基本都是同一個(gè)概念。

log4j-over-slf4j和slf4j-log4j12是跟java日志系統(tǒng)相關(guān)的兩個(gè)jar包,當(dāng)它們同時(shí)出現(xiàn)在classpath下時(shí),就可能會引起堆棧溢出異常。異常信息大致如下(摘自slf4j官網(wǎng)文檔Detected both log4j-over-slf4j.jar AND slf4j-log4j12.jar on the class path, preempting StackOverflowError):

Exception in thread "main" java.lang.StackOverflowError
at java.util.Hashtable.containsKey(Hashtable.java:306)
at org.apache.log4j.Log4jLoggerFactory.getLogger(Log4jLoggerFactory.java:36)
at org.apache.log4j.LogManager.getLogger(LogManager.java:39)
at org.slf4j.impl.Log4jLoggerFactory.getLogger(Log4jLoggerFactory.java:73)
at org.slf4j.LoggerFactory.getLogger(LoggerFactory.java:249)
at org.apache.log4j.Category.<init>(Category.java:53)
at org.apache.log4j.Logger..<init>(Logger.java:35)
at org.apache.log4j.Log4jLoggerFactory.getLogger(Log4jLoggerFactory.java:39)
at org.apache.log4j.LogManager.getLogger(LogManager.java:39)
at org.slf4j.impl.Log4jLoggerFactory.getLogger(Log4jLoggerFactory.java:73)
at org.slf4j.LoggerFactory.getLogger(LoggerFactory.java:249)
at org.apache.log4j.Category..<init>(Category.java:53)
at org.apache.log4j.Logger..<init>(Logger.java:35)
at org.apache.log4j.Log4jLoggerFactory.getLogger(Log4jLoggerFactory.java:39)
at org.apache.log4j.LogManager.getLogger(LogManager.java:39)
subsequent lines omitted...

現(xiàn)有日志體系

分析這個(gè)異常出現(xiàn)的具體原因之前,有必要先快速了解一下現(xiàn)有的Java日志體系。下圖是現(xiàn)有Java日志體系的一個(gè)示意:

上圖不是非常精準(zhǔn),但是能夠比較清晰地展示現(xiàn)有Java日志體系的主體架構(gòu)。Java日志體系大體可以分為三個(gè)部分:日志門面接口、橋接器、日志框架具體實(shí)現(xiàn)。

Java日志框架有很多種,最簡單的是Java自帶的java.util.logging,而最經(jīng)典的是log4j,后來又出現(xiàn)了一個(gè)比log4j性能更好的logback,其他的日志框架就不怎么常用了。應(yīng)用程序直接使用這些具體日志框架的API來滿足日志輸出需求當(dāng)然是可以的,但是由于各個(gè)日志框架之間的API通常是不兼容的,這樣做就使得應(yīng)用程序喪失了更換日志框架的靈活性。

比直接使用具體日志框架API更合理的選擇是使用日志門面接口。日志門面接口提供了一套獨(dú)立于具體日志框架實(shí)現(xiàn)的API,應(yīng)用程序通過使用這些獨(dú)立的API就能夠?qū)崿F(xiàn)與具體日志框架的解耦,這跟JDBC是類似的。最早的日志門面接口是commons-logging,但目前最受歡迎的是slf4j。

日志門面接口本身通常并沒有實(shí)際的日志輸出能力,它底層還是需要去調(diào)用具體的日志框架API的,也就是實(shí)際上它需要跟具體的日志框架結(jié)合使用。由于具體日志框架比較多,而且互相也大都不兼容,日志門面接口要想實(shí)現(xiàn)與任意日志框架結(jié)合可能需要對應(yīng)的橋接器,就好像JDBC與各種不同的數(shù)據(jù)庫之間的結(jié)合需要對應(yīng)的JDBC驅(qū)動一樣。

需要注意的是,前面說過,上圖并不精準(zhǔn),這只是主要部分,實(shí)際情況并不總是簡單的“日志門面接口-->橋接器-->日志框架”這一條單向線。實(shí)際上,獨(dú)立的橋接器有時(shí)候是不需要的,而且也并不是只有將日志門面API轉(zhuǎn)調(diào)到具體日志框架API的橋接器,也存在將日志框架API轉(zhuǎn)調(diào)到日志門面API的橋接器。

說白了,所謂“橋接器”,不過就是對某套API的偽實(shí)現(xiàn)。這種實(shí)現(xiàn)并不是直接去完成API所聲明的功能,而是去調(diào)用有類似功能的別的API。這樣就完成了從“某套API”到“別的API”的轉(zhuǎn)調(diào)。如果同時(shí)存在A-to-B.jar和B-to-A.jar這兩個(gè)橋接器,那么可以想象當(dāng)應(yīng)用程序開始調(diào)用A或者B的API時(shí),會發(fā)生什么事。這就是最開始引出的那個(gè)stack overflow異常的基本原理。

slf4j的轉(zhuǎn)接綁定

上面只是從整體上大概說了下Java現(xiàn)有日志體系,還看無法詳細(xì)說明問題所在,需要進(jìn)一步了解一下slf4j與具體日志框架的橋接情況。

slf4j橋接到具體日志框架

下圖來自slf4j官網(wǎng)文檔Binding with a logging framework at deployment time

可以看到slf4j與具體日志框架結(jié)合的方案有很多種。當(dāng)然,每種方案的最上層(綠色的應(yīng)用層)都是統(tǒng)一的,它們向下都是直接調(diào)用slf4j提供的API(淺藍(lán)色的抽象API層),依賴slf4j-api.jar。然后slf4j API向下再怎么做就非常自由了,幾乎可以使用所有的具體日志框架。注意圖中的第二層是淺藍(lán)色的,看左下角的圖例可知這代表抽象日志API,也就是說它們不是具體實(shí)現(xiàn)。如果像左邊第一種方案那樣下層沒有跟任何具體日志框架實(shí)現(xiàn)相結(jié)合,那么日志是無法輸出來的(這里不確定是否可能會默認(rèn)輸出到標(biāo)準(zhǔn)輸出)。

圖中第三層明顯就不如第一、二層那么整齊劃一了,因?yàn)檫@里已經(jīng)開始涉及到了具體的日志框架。

首先看第三層中間的兩個(gè)湖藍(lán)色塊,這是適配層,也就是橋接器。左邊的slf4j-log4j12.jar橋接器看名字就知道是slf4j到log4j的橋接器,同樣,右邊的slf4j-jdk14.jar就是slf4j到Java原生日志實(shí)現(xiàn)的橋接器了。它們的下一層分別是對應(yīng)的日志框架實(shí)現(xiàn),log4j的實(shí)現(xiàn)代碼是log4j.jar,而jul實(shí)現(xiàn)代碼已經(jīng)包含在了JVM runtime中,不需要單獨(dú)的jar包。

再看第三層其余的三個(gè)深藍(lán)色塊。它們?nèi)齻€(gè)也是具體的日志框架實(shí)現(xiàn),但是卻不需要橋接器,因?yàn)樗鼈儽旧砭鸵呀?jīng)直接實(shí)現(xiàn)了slf4j API。slf4j-simple.jar和slf4j-nop.jar這兩個(gè)不用多說,看名字就知道一個(gè)是slf4j的簡單實(shí)現(xiàn),一個(gè)是slf4j的空實(shí)現(xiàn),平時(shí)用處也不大。而logback之所以也實(shí)現(xiàn)了slf4j API,據(jù)說是因?yàn)閘ogback和slf4j出自同一人之手,這人同時(shí)也是log4j的作者。

第三層所有的灰色jar包都帶有紅框,這表示它們都直接實(shí)現(xiàn)了slf4j API,只是湖藍(lán)色的橋接器對slf4j API的實(shí)現(xiàn)并不是直接輸出日志,而是轉(zhuǎn)去調(diào)用別的日志框架的API。

其它日志框架API轉(zhuǎn)調(diào)回slf4j

如果只存在上面這些從sfl4j到其他日志框架的橋接器,可能還不會出什么問題。但是實(shí)際上還有另外一類橋接器,它們的作用跟上面的恰好相反,它們將其它日志框架的API轉(zhuǎn)調(diào)到slf4j的API上。下圖來自slf4j官網(wǎng)文檔Bridging legacy APIs:

上圖展示了目前為止能安全地從別的日志框架API轉(zhuǎn)調(diào)回slf4j的所有三種情形。

以左上角第一種情形為例,當(dāng)slf4j底層橋接到logback框架的時(shí)候,上層允許橋接回slf4j的日志框架API有l(wèi)og4j和jul。jcl雖然不是什么日志框架的具體實(shí)現(xiàn),但是它的API仍然是能夠被轉(zhuǎn)調(diào)回slf4j的。要想實(shí)現(xiàn)轉(zhuǎn)調(diào),方法就是圖上列出的用特定的橋接器jar替換掉原有的日志框架jar。需要注意的是這里不包含logback API到slf4j API的轉(zhuǎn)調(diào),因?yàn)閘ogback本來就是slf4j API的實(shí)現(xiàn)。

看完三種情形以后,會發(fā)現(xiàn)幾乎所有其他日志框架的API,包括jcl的API,都能夠隨意的轉(zhuǎn)調(diào)回slf4j。但是有一個(gè)唯一的限制就是轉(zhuǎn)調(diào)回slf4j的日志框架不能跟slf4j當(dāng)前橋接到的日志框架相同。這個(gè)限制就是為了防止A-to-B.jar跟B-to-A.jar同時(shí)出現(xiàn)在類路徑中,從而導(dǎo)致A和B一直不停地互相遞歸調(diào)用,最后堆棧溢出。目前這個(gè)限制并不是通過技術(shù)保證的,僅僅靠開發(fā)者自己保證,這也是為什么slf4j官網(wǎng)上要強(qiáng)調(diào)所有合理的方式只有上圖的三種情形。

到這里,在開始所展示的那個(gè)異常的原理基本已經(jīng)清楚了。此外,通過上圖還可以看出可能會出現(xiàn)類似異常的組合不僅僅是log4j-over-slf4j和slf4j-log4j12,slf4j官網(wǎng)還指出了另外一對:jcl-over-slf4j.jar和slf4j-jcl.jar

代碼示例

前面的分析都是理論上的,實(shí)際代碼中即便同時(shí)使用了log4j-over-slf4j和slf4j-log4j12,也未必一定會出現(xiàn)異常。下面的代碼調(diào)用slf4j的API輸出日志,slf4j底層橋接到log4j:

package test;

public class HelloWorld {
public static void main(String[] args) {
org.apache.log4j.BasicConfigurator.configure();
org.slf4j.Logger logger = org.slf4j.LoggerFactory
.getLogger(HelloWorld.class);
logger.info("Hello World");
}
}

配置clas配置classpath上的jar包為(注意log4j在log4j-over-slf4j之前):

在這種情況下運(yùn)行測試程序是能夠正常輸出日志的,不會出現(xiàn)stack overflow異常。但是如果調(diào)整classpath上的jar順序?yàn)椋?/p>

再運(yùn)行測試程序就出現(xiàn)類似于本文最開始的stack overflow異常了,可以看到明顯的周期性重復(fù):

序列圖分析

上圖是堆棧溢出的詳細(xì)調(diào)用過程序列圖。從調(diào)用1開始,依次調(diào)用1.1、1.1.1……最后到了1.1.1.1.1.1(圖中最后一個(gè)調(diào)用)的時(shí)候,發(fā)現(xiàn)它跟1是完全一樣的,那么后續(xù)的過程就是完全一樣的重復(fù)了。

需要特別說明的是最開始的導(dǎo)火索并不只有圖中所示的LoggerFactory.getLogger()一種,應(yīng)用程序中能夠觸發(fā)堆棧溢出異常的直接調(diào)用還有好幾種其它的,比如前面示例代碼中觸發(fā)異常的實(shí)際上是第一條語句org.apache.log4j.BasicConfigurator.configure(),但后續(xù)的互相無限遞歸調(diào)用過程基本都是跟上圖相同的過程。

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • Netty分布式NioEventLoop優(yōu)化selector源碼解析

    Netty分布式NioEventLoop優(yōu)化selector源碼解析

    這篇文章主要介紹了Netty分布式NioEventLoop優(yōu)化selector源碼解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-03-03
  • Java @PostMapping和@GetMapping方法使用詳解

    Java @PostMapping和@GetMapping方法使用詳解

    這篇文章主要介紹了Java @PostMapping和@GetMapping方法使用,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)吧
    2023-03-03
  • Java中Instant的使用及轉(zhuǎn)換

    Java中Instant的使用及轉(zhuǎn)換

    Instant是java.time包中的一個(gè)類,本文主要介紹了Java中Instant的使用及轉(zhuǎn)換,具有一定的參考價(jià)值,感興趣的可以了解一下
    2024-06-06
  • Spring框架 注解配置事務(wù)控制的流程

    Spring框架 注解配置事務(wù)控制的流程

    這篇文章主要介紹了Spring框架 注解配置事務(wù)控制的流程,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-09-09
  • Spring AOP快速入門及開發(fā)步驟

    Spring AOP快速入門及開發(fā)步驟

    Spring AOP(面向切面編程)核心概念包括切面(Aspect)、連接點(diǎn)(JoinPoint)、切點(diǎn)(Pointcut)、通知(Advice)等,通過在不改變原代碼的情況下,對方法進(jìn)行增強(qiáng),實(shí)現(xiàn)了代碼的解耦和功能擴(kuò)展,本文帶來大家掌握Spring 中 AOP 的開發(fā)步驟,感興趣的朋友一起看看吧
    2024-10-10
  • Java字符串編碼解碼性能提升的技巧分享

    Java字符串編碼解碼性能提升的技巧分享

    這篇文章主要是和大家分享幾個(gè)Java中提升字符串編碼解碼性能的小技巧,文中的示例代碼講解詳細(xì),對我們學(xué)習(xí)有一定的幫助,需要的可以參考一下
    2022-05-05
  • spring boot 圖片上傳與顯示功能實(shí)例詳解

    spring boot 圖片上傳與顯示功能實(shí)例詳解

    這篇文章主要介紹了spring boot 圖片上傳與顯示功能實(shí)例詳解,需要的朋友可以參考下
    2017-04-04
  • Spring Boot 核心模塊詳解(12 個(gè)模塊詳解及作用說明)

    Spring Boot 核心模塊詳解(12 個(gè)模塊詳解及作用說明)

    和 Spring 框架一樣,Spring Boot 框架也是由許多核心模塊組成的,每個(gè)模塊負(fù)責(zé)不同的功能點(diǎn),本文講著重于介紹 Spring Boot 相關(guān)的 12 個(gè)模塊的作用和功能,感興趣的朋友一起看看吧
    2025-04-04
  • Java 中 getClass() 方法的使用與原理深入分析(對象類型信息)

    Java 中 getClass() 方法的使用與原理深入分析(對象類型信息)

    在 Java 編程中,getClass() 是一個(gè)非常重要的方法,它用于獲取對象的運(yùn)行時(shí)類信息,無論是調(diào)試代碼、反射操作,還是類型檢查,getClass() 都扮演著關(guān)鍵角色,本文將深入探討 getClass() 的使用方法、底層原理以及實(shí)際應(yīng)用場景,感興趣的朋友一起看看吧
    2024-12-12
  • Java中Lambda表達(dá)式之Lambda語法與作用域解析

    Java中Lambda表達(dá)式之Lambda語法與作用域解析

    這篇文章主要介紹了Java中Lambda表達(dá)式之Lambda語法與作用域解析重點(diǎn)介紹Lambda表達(dá)式基礎(chǔ)知識,需要的朋友可以參考下
    2017-02-02

最新評論

临清市| 渝北区| 久治县| 九龙城区| 拉萨市| 莱州市| 巴中市| 易门县| 辽阳市| 临夏县| 清原| 凌云县| 监利县| 勐海县| 晋宁县| 建水县| 南城县| 来安县| 临江市| 珠海市| 调兵山市| 鲁甸县| 和林格尔县| 弥勒县| 连江县| 赤水市| 商丘市| 广平县| 樟树市| 左贡县| 浠水县| 五原县| 澄迈县| 双柏县| 喀什市| 清流县| 大余县| 大田县| 鹤壁市| 应用必备| 云梦县|