Java平台扩展机制#3:SLF4J怪招
SLF4J簡介
The Simple Logging Facade for Java (SLF4J) serves as a simple facade or abstraction for various logging frameworks, such as java.util.logging, logback and log4j. SLF4J allows the end-user to plug in the desired logging framework at deployment time.
簡單來說,SLF4J提供了一套日志接口(還是面向接口編程的思想),具體的接口實現是用logback還是log4j或者其他日志框架,要看你部署時使用的jar包。
SLF4J使用
??? import org.slf4j.Logger;
??? import org.slf4j.LoggerFactory;
??? ?
??? public class HelloWorld {
????? public static void main(String[] args) {
??????? Logger logger = LoggerFactory.getLogger(HelloWorld.class);
??????? logger.info("Hello World");
????? }
??? }
要把這個程序跑起來需要到SLF4J官網下載jar包,當前最新的是slf4j-api-1.7.7.jar,然后需要再選擇以下jar包來綁定具體的日志框架:
??? slf4j-log4j12-1.7.7.jar
??? slf4j-jdk14-1.7.7.jar
??? slf4j-nop-1.7.7.jar
??? slf4j-simple-1.7.7.jar
??? slf4j-jcl-1.7.7.jar
??? logback-classic-1.0.13.jar (非SLF4J項目提供,不推薦)
需要切換到不同日志框架時,只需替換上述jar包即可。
貼上一張SLF4J框架圖幫助理解,
實現原理
這么碉堡的功能,是怎么做到的呢?說來簡單,但又很奇葩,先看一下LoggerFactory.getLogger,
????? /**
?????? * Return a logger named according to the name parameter using the statically
?????? * bound {@link ILoggerFactory} instance.
?????? *
?????? * @param name The name of the logger.
?????? * @return logger
?????? */
????? public static Logger getLogger(String name) {
??????? ILoggerFactory iLoggerFactory = getILoggerFactory();
??????? return iLoggerFactory.getLogger(name);
????? }
調用了getILoggerFactory,
????? /**
?????? * Return the {@link ILoggerFactory} instance in use.
?????? * <p/>
?????? * <p/>
?????? * ILoggerFactory instance is bound with this class at compile time.
?????? *
?????? * @return the ILoggerFactory instance in use
?????? */
????? public static ILoggerFactory getILoggerFactory() {
??????? if (INITIALIZATION_STATE == UNINITIALIZED) {
????????? INITIALIZATION_STATE = ONGOING_INITIALIZATION;
????????? performInitialization();
??????? }
??????? switch (INITIALIZATION_STATE) {
????????? case SUCCESSFUL_INITIALIZATION:
??????????? return StaticLoggerBinder.getSingleton().getLoggerFactory();
????????? case NOP_FALLBACK_INITIALIZATION:
??????????? return NOP_FALLBACK_FACTORY;
????????? case FAILED_INITIALIZATION:
??????????? throw new IllegalStateException(UNSUCCESSFUL_INIT_MSG);
????????? case ONGOING_INITIALIZATION:
??????????? // support re-entrant behavior.
??????????? // See also http://bugzilla.slf4j.org/show_bug.cgi?id=106
??????????? return TEMP_FACTORY;
??????? }
??????? throw new IllegalStateException("Unreachable code");
????? }
繼續跟下去可以看到最后是通過StaticLoggerBinder綁定到具體的日志框架,但是這個StaticLoggerBinder很神奇,看下圖
通過對比SLF4J的jar包跟源碼包發現,源碼包竟然多出了一個impl目錄,此目錄有以下幾個文件,
??? package.html
??? StaticLoggerBinder.java
??? StaticMarkerBinder.java
??? StaticMDCBinder.java
看一下StaticLoggerBinder.java,
??? public class StaticLoggerBinder {
??? ?
????? /**
?????? * The unique instance of this class.
?????? */
????? private static final StaticLoggerBinder SINGLETON = new StaticLoggerBinder();
???? ?
????? /**
?????? * Return the singleton of this class.
?????? *
?????? * @return the StaticLoggerBinder singleton
?????? */
????? public static final StaticLoggerBinder getSingleton() {
??????? return SINGLETON;
????? }
???? ?
????? /**
?????? * Declare the version of the SLF4J API this implementation is compiled against.
?????? * The value of this field is usually modified with each release.
?????? */
????? // to avoid constant folding by the compiler, this field must *not* be final
????? public static String REQUESTED_API_VERSION = "1.6.99";? // !final
???? ?
????? private StaticLoggerBinder() {
??????? throw new UnsupportedOperationException("This code should have never made it into slf4j-api.jar");
????? }
??? ?
????? public ILoggerFactory getLoggerFactory() {
??????? throw new UnsupportedOperationException("This code should never make it into slf4j-api.jar");
????? }
??? ?
????? public String getLoggerFactoryClassStr() {
??????? throw new UnsupportedOperationException("This code should never make it into slf4j-api.jar");
????? }
??? }
"This code should have never made it into slf4j-api.jar",想來想去,只有一種可能,就是打完jar包后,手工將impl這個目錄從jar包中刪除了。而在上述slf4j-log4j12-1.7.7.jar等包中,你都可以找到org.slf4j.impl.StaticLoggerBinder這個類。
到這里應該可以看出來,這是一種很奇葩的擴展方式,先使用了一個類,將它打進jar包,然后從jar包中剔除,通過在其他jar包中使用同樣類名新建一個類(至于這個類具體用來做啥并無限制,也沒有接口來約束)來達到擴展的效果。目前看來這樣的方式有兩個小問題:
??? 需要防止一些編譯器優化。例如上面看到的StaticLoggerBinder.REQUESTED_API_VERSION,為防止編譯器的constant folding,不能聲明為final。
??? 對于被擴展的類約束太少。為了解決這個問題,SLF4J專門定義了一個org.slf4j.spi.LoggerFactoryBinder,具體實現的org.slf4j.impl.StaticLoggerBinde都遵循了這個接口,但這其實只是一種約定,而不是約束。
使用反射?
今天某同學說,為嘛不直接反射出一個接口,例如LoggerFactoryBinder,我想了想,ServiceLoader機制不就是使用反射的嗎,SLF4J真是不走尋常路^_^
參考資料
??? http://www.slf4j.org/manual.html
?
---------------------
作者:kisimple
來源:CSDN
原文:https://blog.csdn.net/kisimple/article/details/38664717
版權聲明:本文為博主原創文章,轉載請附上博文鏈接!
總結
以上是生活随笔為你收集整理的Java平台扩展机制#3:SLF4J怪招的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: log4j 2.x 架构(源码)
- 下一篇: Javal动态代理