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
版权声明:本文为博主原创文章,转载请附上博文链接!