目录
前言
slf4j主要是为了给Java日志访问提供一个标准、规范的API框架,其主要意义在于提供接口,具体的实现可以交由其他日志框架,例如log4j和logback等。对于一般的Java项目而言,日志框架会选择slf4j-api作为门面,配上具体的实现框架(log4j、logback等),中间使用桥接器完成桥接。
一、slf4j
官网地址:https://www.slf4j.org/manual.html
项目Github地址:
https://github.com/qos-ch/slf4j
slfj4j桥接API
slf4j 项目模块描述
模块 | 描述 |
slf4j-api | 日志的统一接口 |
slf4j-jdk14 | jul到SLF4J的绑定,将强制SLF4J的调用分配给jul |
slf4j-log4j12 | log4j 到SLF4J的绑定,将强制SLF4J的调用分配给 log4j |
jcl-over-slf4j | 将commons-logging框架的日志桥接到slf4j上来 |
log4j-over-slf4j | 将 log4j 框架的日志桥接到slf4j上来 |
二、slf4j-api
简单使用
slf4j-api是slf4j的api模块,提供了日志输出的API,值得一提的是从slf4j 1.8起,slf4j使用SPI的方式寻找日志实现框架,而在此之前则是通过寻找指定类的方式发现并绑定实现框架。先通过一个简单的入门案例来分析它的实现方式。
public class TestLog {
private static final Logger logger = LoggerFactory.getLogger(TestLog.class);
public static void main(String[] args) {
logger.info("hello world");
}
}
20:56:33.344 [main] INFO com.test.TestLog - hello world
代码分析
先来分析 LoggerFactory.getLogger(TestLog.class) 这行代码,LoggerFactory.getLogger() 返回一个 Logger 对象,我们看看LoggerFactory 类的实现
public final class LoggerFactory {
public static Logger getLogger(String name) {
ILoggerFactory iLoggerFactory = getILoggerFactory();
return iLoggerFactory.getLogger(name);
}
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 https://www.slf4j.org/codes.html#loggerNameMismatch for an explanation");
}
}
return logger;
}
}
重点关注 getILoggerFactory 方法 ,它返回一个日志工厂类。ILoggerFactory 是一个slf4j 提供的一个接口,可以猜测 到 getILoggerFactory方法应该是拿到了其实现类.
public static ILoggerFactory getILoggerFactory() {
return getProvider().getLoggerFactory();
}
getProvider() 方法返回SLF4JServiceProvider,其中SLF4JServiceProvider同样是slf4j-api中提供的一个接口,那么getProvider肯定是通过某种方法拿到了该接口的实现类。
static SLF4JServiceProvider getProvider() {
if (INITIALIZATION_STATE == 0) {
Class var0 = LoggerFactory.class;
synchronized(LoggerFactory.class) {
if (INITIALIZATION_STATE == 0) {
INITIALIZATION_STATE = 1;
performInitialization();
}
}
}
switch (INITIALIZATION_STATE) {
case 1:
return SUBST_PROVIDER;
case 2:
throw new IllegalStateException("org.slf4j.LoggerFactory in failed state. Original exception was thrown EARLIER. See also https://www.slf4j.org/codes.html#unsuccessfulInit");
case 3:
return PROVIDER;
case 4:
return NOP_FALLBACK_SERVICE_PROVIDER;
default:
throw new IllegalStateException("Unreachable code");
}
}
重点关注 performInitialization 方法
private static final void performInitialization() {
bind();
if (INITIALIZATION_STATE == 3) {
versionSanityCheck();
}
}
bind 方法
private static final void bind() {
List<SLF4JServiceProvider> providersList = findServiceProviders();
}
从 findServiceProviders 方法中 可以 知道 是通过SPI的方式寻找SLF4JServiceProvider的实现类.
private static List<SLF4JServiceProvider> findServiceProviders() {
ServiceLoader<SLF4JServiceProvider> serviceLoader = ServiceLoader.load(SLF4JServiceProvider.class);
List<SLF4JServiceProvider> providerList = new ArrayList();
Iterator var2 = serviceLoader.iterator();
while(var2.hasNext()) {
SLF4JServiceProvider provider = (SLF4JServiceProvider)var2.next();
providerList.add(provider);
}
return providerList;
}
三、基础知识
外观模式
外观模式(Facade Pattern)隐藏系统的复杂性,并向客户端提供了一个客户端可以访问系统的接口。这种类型的设计模式属于结构型模式,它向现有的系统添加一个接口,来隐藏系统的复杂性。
这种模式涉及到一个单一的类,该类提供了客户端请求的简化方法和对现有系统类方法的委托调用。
适配器模式
适配器模式(Adapter Pattern)是作为两个不兼容的接口之间的桥梁。这种类型的设计模式属于结构型模式,它结合了两个独立接口的功能。
这种模式涉及到一个单一的类,该类负责加入独立的或不兼容的接口功能。举个真实的例子,读卡器是作为内存卡和笔记本之间的适配器。您将内存卡插入读卡器,再将读卡器插入笔记本,这样就可以通过笔记本来读取内存卡。
什么是桥接模式
桥接(Bridge)是用于把抽象化与实现化解耦,使得二者可以独立变化。这种类型的设计模式属于结构型模式,它通过提供抽象化和实现化之间的桥接结构,来实现二者的解耦。
这种模式涉及到一个作为桥接的接口,使得实体类的功能独立于接口实现类。这两种类型的类可被结构化改变而互不影响。
什么是SPI
SPI全名Service Provider interface,翻译过来就是“服务提供接口”,再说简单就是提供某一个服务的接口, 提供给服务开发者或者服务生产商来进行实现。
Java SPI 是JDK内置的一种动态加载扩展点的实现。
这个机制在一般的业务代码中很少用到(个人接触到的业务没有用到过),但是再底层框架中却被大量使用,包括JDBC、Dubbo、Spring框架、日志接口中都有用到,不同的是有的使用Java原生的实现,有的框架则自己实现了一套SPI机制
当接口是由服务调用方提供,并且由服务提供方进行实现时,服务调用方就可以根据自己的需要选择特定实现,而不用更改业务代码以获取相应的功能,这个就是SPI
什么是API
API
全称Application Programming Interface
, 翻译为“应用程序接口”,指的是应用程序为外部提供服务的接口,这个接口通常由服务提供者自行开发,定义好接口后很少改动。
一般应用(模块)之间通过接口进行通讯,服务提供方提供接口并进行实现后,调用方就可以通过调用这个接口拥有服务提供方提供的能力,这个就是API
API 中的接口是服务提供者给服务调用者的一个功能列表,而 SPI 中更多强调的是,服务调用者对服务实现的一种约束,服务提供者根据这种约束实现的服务,可以被服务调用者发现。
四、设计分析
了解了slf4j 的简单使用后,我们来看看它是设计的核心接口类
public interface Logger {}
public interface ILoggerFactory {
public Logger getLogger(String name);
}
public interface SLF4JServiceProvider {
/**
* Return the instance of {@link ILoggerFactory} that
* {@link org.slf4j.LoggerFactory} class should bind to.
*
* @return instance of {@link ILoggerFactory}
*/
public ILoggerFactory getLoggerFactory();
/**
* Initialize the logging back-end.
*
* <p><b>WARNING:</b> This method is intended to be called once by
* {@link LoggerFactory} class and from nowhere else.
*
*/
public void initialize();
}
我们看看Log4j 的桥接方式
Reload4jServiceProvider 实现 SLF4JServiceProvider
public class Reload4jServiceProvider implements SLF4JServiceProvider {
public static String REQUESTED_API_VERSION = "2.0.99"; // !final
private ILoggerFactory loggerFactory;
private IMarkerFactory markerFactory;
private MDCAdapter mdcAdapter;
public Reload4jServiceProvider() {
try {
@SuppressWarnings("unused")
Level level = Level.TRACE;
} catch (NoSuchFieldError nsfe) {
Util.report("This version of SLF4J requires log4j version 1.2.12 or later. See also http://www.slf4j.org/codes.html#log4j_version");
}
}
@Override
public void initialize() {
loggerFactory = new Reload4jLoggerFactory();
markerFactory = new BasicMarkerFactory();
mdcAdapter = new Reload4jMDCAdapter();
}
@Override
public ILoggerFactory getLoggerFactory() {
return loggerFactory;
}
}
在 resources/META-INF/services 路径下 创建文件org.slf4j.spi.SLF4JServiceProvider 。文件名为 slf4j中SLF4JServiceProvider 的全路径名,在文件 org.slf4j.spi.SLF4JServiceProvider 中存放实现类
org.slf4j.reload4j.Reload4jServiceProvider
系统启动会自动扫描 resources/META-INF/services 下的文件,调用实现类的构造方法,完成对象的初始化。从Reload4jServiceProvider 中 可以获取到ILoggerFactory 的 实现类了。