1. 整合需要的Jar包
1.1 代碼使用SLF4J-API時
1.2 代碼使用其他API時
1.3 一個樣板例子
Spring全家桶使用得commons-logging API,當我們自己代碼想要使用或者切換到SLF4J時;可以使用下面得插件
<!--目前存在commons-logging 及 slf4j兩套log api-->
<dependency>
<groupId>org.slf4j</groupId><!--jcl這個插件也是利用了commons-logging載入具體實現時利用得SPI機制來搞得-->
<artifactId>jcl-over-slf4j</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency><!--logback 實現了SLF4J-API接口-->
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
2. SLF4J源代碼分析(bind的過程及涉及到的JVM機制)
2.1 ver-1.7.25---ClassLoader檢測
起始:
public static Logger getLogger(Class<?> clazz) {
Logger logger = getLogger(clazz.getName());
if (DETECT_LOGGER_NAME_MISMATCH) {
Class<?> autoComputedCallingClass = Util.getCallingClass();
if (autoComputedCallingClass != null && nonMatchingClasses(clazz, autoComputedCallingClass)){
Util.report(String.format("Detected logger name mismatch. Given name: \"%s\"; computed name: \"%s\".", logger.getName(),autoComputedCallingClass.getName()));
Util.report("See " + LOGGER_NAME_MISMATCH_URL + " for an explanation");
}
}
return logger;
}
//從類命獲取
public static Logger getLogger(String name) {
ILoggerFactory iLoggerFactory = getILoggerFactory();
return iLoggerFactory.getLogger(name);
}
//獲取日誌工廠
public static ILoggerFactory getILoggerFactory() {
if (INITIALIZATION_STATE == UNINITIALIZED) {
synchronized (LoggerFactory.class) {
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://jira.qos.ch/browse/SLF4J-97
return SUBST_FACTORY;
}
throw new IllegalStateException("Unreachable code");
}
//日誌工廠初始化
private final static void performInitialization() {
bind();
if (INITIALIZATION_STATE == SUCCESSFUL_INITIALIZATION) {
versionSanityCheck();
}
}
//具體實現綁定
private final static void bind() {
try {
Set<URL> staticLoggerBinderPathSet = null;
if (!isAndroid()) {//不是安卓系統得時候,需要檢查是否有多個org/slf4j/impl/StaticLoggerBinder.class文件;保證SLF4J-API下沒有多個兼容插件(1.8後以SPI機制替換)
staticLoggerBinderPathSet = findPossibleStaticLoggerBinderPathSet();
reportMultipleBindingAmbiguity(staticLoggerBinderPathSet);
}
// 真正執行綁定,StaticLoggerBinder不同兼容包實現不一樣,但目的一樣這個類是單例實例持有一個LoggerFactory得引用;而這個loggerfatory裏面都是具體框架logger得適配器類實例
StaticLoggerBinder.getSingleton();
INITIALIZATION_STATE = SUCCESSFUL_INITIALIZATION;
reportActualBinding(staticLoggerBinderPathSet);
fixSubstituteLoggers();
replayEvents();
// release all resources in SUBST_FACTORY
SUBST_FACTORY.clear();
} catch (NoClassDefFoundError ncde) {
String msg = ncde.getMessage();
if (messageContainsOrgSlf4jImplStaticLoggerBinder(msg)) {
INITIALIZATION_STATE = NOP_FALLBACK_INITIALIZATION;
Util.report("Failed to load class \"org.slf4j.impl.StaticLoggerBinder\".");
Util.report("Defaulting to no-operation (NOP) logger implementation");
Util.report("See " + NO_STATICLOGGERBINDER_URL + " for further details.");
} else {
failedBinding(ncde);
throw ncde;
}
} catch (java.lang.NoSuchMethodError nsme) {
String msg = nsme.getMessage();
if (msg != null && msg.contains("org.slf4j.impl.StaticLoggerBinder.getSingleton()")) {
INITIALIZATION_STATE = FAILED_INITIALIZATION;
Util.report("slf4j-api 1.6.x (or later) is incompatible with this binding.");
Util.report("Your binding is version 1.5.5 or earlier.");
Util.report("Upgrade your binding to version 1.6.x.");
}
throw nsme;
} catch (Exception e) {
failedBinding(e);
throw new IllegalStateException("Unexpected initialization failure", e);
}
}
//獲取
2.2 ver-1.8 SPI機制
起始:
public static Logger getLogger(Class<?> clazz) {
Logger logger = getLogger(clazz.getName());
if (DETECT_LOGGER_NAME_MISMATCH) {
Class<?> autoComputedCallingClass = Util.getCallingClass();
if (autoComputedCallingClass != null && nonMatchingClasses(clazz, autoComputedCallingClass)) {
Util.report(String.format("Detected logger name mismatch. Given name: \"%s\"; computed name: \"%s\".", logger.getName(), autoComputedCallingClass.getName()));
Util.report("See " + LOGGER_NAME_MISMATCH_URL + " for an explanation");
}
}
return logger;
}
//轉從name獲取
public static Logger getLogger(String name) {
ILoggerFactory iLoggerFactory = getILoggerFactory();
return iLoggerFactory.getLogger(name);
}
//先獲取具體得實現
public static ILoggerFactory getILoggerFactory() {
return getProvider().getLoggerFactory();
}
//先check當前LoggerFactory得狀態;如果是未初始化則進行初始化
static SLF4JServiceProvider getProvider() {
if (INITIALIZATION_STATE == UNINITIALIZED) {
synchronized (LoggerFactory.class) {
if (INITIALIZATION_STATE == UNINITIALIZED) {
INITIALIZATION_STATE = ONGOING_INITIALIZATION;
performInitialization();
}
}
}
switch (INITIALIZATION_STATE) {
case SUCCESSFUL_INITIALIZATION:
return PROVIDER;
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://jira.qos.ch/browse/SLF4J-97
return SUBST_PROVIDER;
}
throw new IllegalStateException("Unreachable code");
}
//封裝一下免得難看
private final static void performInitialization() {
bind();
if (INITIALIZATION_STATE == SUCCESSFUL_INITIALIZATION) {
versionSanityCheck();
}
}
//接口實現者的綁定
private final static void bind() {
try {
List<SLF4JServiceProvider> providersList = findServiceProviders();//SPI機制
reportMultipleBindingAmbiguity(providersList);//如果有多個實現類,則BugReport
if (providersList != null && !providersList.isEmpty()) {
PROVIDER = providersList.get(0);//獲取第一個實現類,注意Linux和windows都是獲取磁盤node值最小的Jar裏面的實現類
PROVIDER.initialize();//接口初始化,不同日誌框架的兼容包實現不一樣Log4J或者logback或者jul等等,
INITIALIZATION_STATE = SUCCESSFUL_INITIALIZATION;
reportActualBinding(providersList);
fixSubstituteLoggers();
replayEvents();
// release all resources in SUBST_FACTORY
SUBST_PROVIDER.getSubstituteLoggerFactory().clear();
} else {
INITIALIZATION_STATE = NOP_FALLBACK_INITIALIZATION;
Util.report("No SLF4J providers were found.");
Util.report("Defaulting to no-operation (NOP) logger implementation");
Util.report("See " + NO_PROVIDERS_URL + " for further details.");
Set<URL> staticLoggerBinderPathSet = findPossibleStaticLoggerBinderPathSet();
reportIgnoredStaticLoggerBinders(staticLoggerBinderPathSet);
}
} catch (Exception e) {
failedBinding(e);
throw new IllegalStateException("Unexpected initialization failure", e);
}
}
//獲取實現類List,SPI機制
private static List<SLF4JServiceProvider> findServiceProviders() {
//SPI機制,主要由ServiceLoader這個工具類負責實施
//主要工作流程:從每個Jar包的META-INFO目錄下的services文件夾下面獲取指定接口的實現類
//要去文件名和接口的全類名一致;
//返回值是所有的Jar包內的所有的實現類Class實例;
ServiceLoader<SLF4JServiceProvider> serviceLoader=ServiceLoader.load(SLF4JServiceProvider.class);
List<SLF4JServiceProvider> providerList = new ArrayList<SLF4JServiceProvider>();
for (SLF4JServiceProvider provider : serviceLoader) {
providerList.add(provider);
}
return providerList;
}
3 吐槽
- Java技術生態裏最讓人蛋疼得一點是,JDK制定得行業標準時不但慢而且還不一定好使;
- 比如日誌標準(先有了Log4J等等JDK纔出日誌得API還不好使),比如JavaEE標準(被Spring懟成了傻子)等等;
- 等JDK標準出來了,別人已經寫了一些框架佔了相當大部分市場;
- 而且同一個標準或者工具有很多種實現,不同實現還要加上自己得特定API。。。
- 還有JDK也缺少包管理機制,只有靠maven和gradle是JDK體系外實現解決這個問題。。。