📷 题解插图一起学Java(12)-[日志篇]教你分析SLF4J源码,掌握SLF4J如何与Logback无缝集成的原理
继续完成上篇(一起学Java(11)-[日志篇]教你分析SLF4J源码,掌握Logger接口实现类加载原理)留给自己的任务,研究Logback是如何和SLF4J无缝集成的。
在之前的SLF4J源码研究中(教你分析SLF4J源码,掌握Logger接口实现类加载原理)我们已经知道SLF4J中利用java.util.ServiceLoader 机制寻找SLF4JServiceProvider的实现类,从而得到构造Logger实例的工厂类。那么我们就先看看java.util.ServiceLoader是个什么东西。
一、java.util.ServiceLoader定位和原理
java.util.ServiceLoader 是 Java 提供的一个服务发现机制,它允许程序在运行时动态地加载和使用实现了某个接口或抽象类的服务实现。这个机制在 Java 的模块化系统和插件系统中非常有用。正好符合Log框架的场景定位。
ServiceLoader 通过一种称为服务提供者接口(SPI, Service Provider Interface的机制工作。服务提供者接口是一个接口或抽象类,而服务实现类是该接口或抽象类的具体实现。
1. 服务声明
服务提供者接口在 JAR 包中的 META-INF/services 目录下声明。该目录下的每个文件名对应一个服务接口,全路径名,文件内容是该接口的一个或多个实现类的全路径名。
例如,假设你有一个接口 com.example.MyService,那么你需要在 META-INF/services 目录下创建一个名为 com.example.MyService 的文件,文件内容如下:
com.example.impl.MyServiceImpl1
com.example.impl.MyServiceImpl2
这表示 MyService 有两个实现类:MyServiceImpl1 和 MyServiceImpl2。
2. 加载服务
使用 ServiceLoader 加载服务的代码通常如下:
ServiceLoader<MyService> serviceLoader = ServiceLoader.load(MyService.class);
for (MyService service : serviceLoader) {
service.doSomething(); // doSomething是MyService接口中定义的方法
}
ServiceLoader.load() 方法会查找并实例化所有在 META-INF/services/com.example.MyService 文件中列出的服务实现类,后面就是实现类的使用了,也就不难理解了。
以上述理论为基础,来看看Logback中的实现。
二、Logback代码解析,如何实现SLF4JServiceProvider接口
展开logback-classic源码包,找到META-INF/services文件夹,果然有名为org.slf4j.spi.SLF4JServiceProvider的文件

内容如下:
ch.qos.logback.classic.spi.LogbackServiceProvider
即Logback提供的SLF4JServiceProvider接口的实现类。这样,根据上文的分析(教你分析SLF4J源码,掌握Logger接口实现类加载原理),在SLF4J的LoggerFactory中便可以通过java.util.ServiceLoader获取到Logback的SLF4JServiceProvider实现,并在bind函数中执行后续的Logger初始化操作。
private final static void bind() {
try {
List<SLF4JServiceProvider> providersList = findServiceProviders();
reportMultipleBindingAmbiguity(providersList);
if (providersList != null && !providersList.isEmpty()) {
PROVIDER = providersList.get(0);
// SLF4JServiceProvider.initialize() is intended to be called here and nowhere else.
PROVIDER.initialize();
INITIALIZATION_STATE = SUCCESSFUL_INITIALIZATION;
reportActualBinding(providersList);
} else {
INITIALIZATION_STATE = NOP_FALLBACK_INITIALIZATION;
Reporter.warn("No SLF4J providers were found.");
Reporter.warn("Defaulting to no-operation (NOP) logger implementation");
Reporter.warn("See " + NO_PROVIDERS_URL + " for further details.");
Set<URL> staticLoggerBinderPathSet = findPossibleStaticLoggerBinderPathSet();
reportIgnoredStaticLoggerBinders(staticLoggerBinderPathSet);
}
postBindCleanUp();
} catch (Exception e) {
failedBinding(e);
throw new IllegalStateException("Unexpected initialization failure", e);
}
}
在bind函数源码中可以看到,如果项目中发现了多个
SLF4JServiceProvider的实现类,SLF4J只会取第一个进行初始化。
至此,SLF4J如何与Logback无缝集成的原理也就大致了解清楚了。对于Logback而言,原生实现了SLF4JServiceProvider接口,自然也就不需要桥接层了。对于像Log4j 2这种没有原生实现SLF4JServiceProvider的框架,其实我们也能大致猜到其桥阶层的实现原理:按照java.util.ServiceLoader原理向上实现SLF4J的SLF4JServiceProvider接口,向下调用对应的日志实现层框架,下一步我们就看一下Log4j 2的桥接方式,来印证一下我们的猜想。
所有代码开源上传至 GitHub:yummy-code 仓库 · GESP 专题站:GESP WIKI
欢迎加入:C++ GESP/CSP 考级答疑群(688906745) 与 Java/Python交流群(982860385),点击可直接加群。
猜你想读 · 相关文章推荐
一起学Java(10)-[日志篇]教你为本项目引入SLF4J接口层框架
在上一节一起学Java(9)-为项目引入Log框架(Log篇一-框架演进和设计逻辑中,我们已经理清了Java日志框架的演进过程、设计思想和核心框架。从这节开始,进入实战研究环节。
一起学Java(11)-[日志篇]教你分析SLF4J源码,掌握Logger接口实现类加载原理
最近各种事情很忙,今天继续。在第十篇(一起学Java(10)-为项目引入Log框架(Log篇二-引入SLF4J接口层框架))中,我们为项目(https://github.com/lihongzheshuai/java-all-in-one)引入了SLF4J和Logback框架,按计划通过阅读源码研究下SLF4J的实现原...
一起学Java(13)-[日志篇]教你分析SLF4J和Log4j2源码,掌握SLF4J与Log4j2桥接集成原理
研究完SLF4J和Logback这种无缝集成的方式(一起学Java(12)-[日志篇]教你分析SLF4J源码,掌握SLF4J如何与Logback无缝集成的原理),继续研究Log4j2和SLF4J这种需要桥接集成的方式。
OneCoder (lihongzheshuai)
一个中年人的自留地,记录学习 C++、GESP/NOI、Java、Python 与算法架构的心得体会。本站唯一网址:coderli.com