slf4j-api 1.6.99初始化源码艺术
SLF4J初始化机制深度解析
目录
核心流程概览
graph TD
A[用户调用 LoggerFactory.getLogger] --> B{检查INITIALIZATION_STATE}
B -- UNINITIALIZED --> C[获取类锁]
C --> D[执行bind绑定实现]
D --> E[版本兼容检查]
E --> F[初始化ILoggerFactory]
F --> G[返回Logger实例]
B -- ONGOING_INITIALIZATION --> H[返回SUBST_FACTORY]
B -- SUCCESSFUL_INITIALIZATION --> I[返回绑定LoggerFactory]
B -- NOP_FALLBACK_INITIALIZATION --> J[返回NOP_FACTORY]
B -- FAILED_INITIALIZATION --> K[抛出异常]
classDef critical fill:#f9f,stroke:#333;
classDef normal fill:#bbf,stroke:#333;
class A,B critical
class C,D,E,F,G normal
初始化状态机
SLF4J使用5种状态控制初始化流程:
UNINITIALIZED(0):未初始化ONGOING_INITIALIZATION(1):初始化中SUCCESSFUL_INITIALIZATION(2):成功NOP_FALLBACK_INITIALIZATION(3):降级到NOPFAILED_INITIALIZATION(4):失败
状态转换通过双重检查锁保证线程安全:
Logger logger=LoggerFactory.getLogger("xxxx");
// 在LoggerFactory中调用了 getILoggerFactory
public static Logger getLogger(String name) {
ILoggerFactory iLoggerFactory = getILoggerFactory();
return iLoggerFactory.getLogger(name);
}
绑定机制剖析
核心绑定逻辑在bind()方法中,主要处理:
- 查找可能的
StaticLoggerBinder实现 - 处理多绑定冲突
- 捕获并处理绑定异常
- 最终清理工作
关键设计点:
- 类路径扫描机制(非标准SPI):
- 通过
ClassLoader加载org.slf4j.impl.StaticLoggerBinder - 允许任意jar放置实现类
- 通过
- 编译时分离:
- api和实现独立编译
- 通过约定接口交互
- 运行时绑定:
- 动态加载实现类
- 版本兼容检查
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:
// 没有bind成功,进行降级到No Operation
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
// 初始化中时,先返回 SUBST_FACTORY,日志先暂存,后续重放
return SUBST_FACTORY;
}
throw new IllegalStateException("Unreachable code");
}
调用了performInitialization进行初始化
private final static void performInitialization() {
// 绑定日志实现
bind();
if (INITIALIZATION_STATE == SUCCESSFUL_INITIALIZATION) {
// api和日志实版本现兼容性检查
versionSanityCheck();
}
}
performInitialization内部进行了日志实现的绑定,主要看bind
private final static void bind() {
try {
Set<URL> staticLoggerBinderPathSet = null;
// skip check under android, see also
// http://jira.qos.ch/browse/SLF4J-328
if (!isAndroid()) {
// 非安卓,检查存在StaticLoggerBinder的路径
staticLoggerBinderPathSet = findPossibleStaticLoggerBinderPathSet();
// 类路径下,存在多个org/slf4j/impl/StaticLoggerBinder.class
// 会打印警告信息 Class path contains multiple SLF4J bindings
reportMultipleBindingAmbiguity(staticLoggerBinderPathSet);
}
// the next line does the binding
// 打包的时候slf4j-api会将本身的StaticLoggerBinder去掉,
// 让真正的日志实现框架提供StaticLoggerBinder的实现
// 从而获得特定的日志框架实现
// 自身的StaticLoggerBinder只是为了编译时能编译过
StaticLoggerBinder.getSingleton();
INITIALIZATION_STATE = SUCCESSFUL_INITIALIZATION;
// 打印真正的LoggerFactory实现类
reportActualBinding(staticLoggerBinderPathSet);
} catch (NoClassDefFoundError ncde) {
// 编译时类存在(即代码没有编译错误),但运行时找不到类定义。
String msg = ncde.getMessage();
if (messageContainsOrgSlf4jImplStaticLoggerBinder(msg)) {
// 如果是StaticLoggerBinder导致的NoClassDefFoundError,就降级到No Operation
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()")) {
// 如果是StaticLoggerBinder导致的NoSuchMethodError,说明版本不兼容
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);
} finally {
postBindCleanUp();
}
}
临时日志处理
初始化期间的日志处理采用生产者-消费者模式:
- SUBST_FACTORY作为临时存储
LinkedBlockingQueue缓冲日志事件- 初始化完成后重放(replay)日志
这种设计保证:
- 初始化期间不丢失日志
- 线程安全
- 最终一致性
也就是说,在初始化完成后,对初始化过程中打印的日志(多线程场景)重放给真实的最终Logger来处理
private static void postBindCleanUp() {
// 处理临时占位Logger
// 当用户代码在 SLF4J 初始化完成之前调用日志时,此时真实的日志实现尚未绑定
// 在真实Logger未就绪期间,所有日志事件被暂存在 LinkedBlockingQueue 中。
fixSubstituteLoggers();
// 重放缓冲的日志事件
replayEvents();
// 释放资源
// release all resources in SUBST_FACTORY
SUBST_FACTORY.clear();
}
private static void fixSubstituteLoggers() {
synchronized (SUBST_FACTORY) {
SUBST_FACTORY.postInitialization();
for (SubstituteLogger substLogger : SUBST_FACTORY.getLoggers()) {
Logger logger = getLogger(substLogger.getName());
// 将临时Logger的调用委托给真实Logger
substLogger.setDelegate(logger);
}
}
}
最后进行版本兼容性的检查
private final static void versionSanityCheck() {
try {
String requested = StaticLoggerBinder.REQUESTED_API_VERSION;
boolean match = false;
for (String aAPI_COMPATIBILITY_LIST : API_COMPATIBILITY_LIST) {
if (requested.startsWith(aAPI_COMPATIBILITY_LIST)) {
match = true;
}
}
if (!match) {
Util.report("The requested version " + requested + " by your slf4j binding is not compatible with " + Arrays.asList(API_COMPATIBILITY_LIST).toString());
Util.report("See " + VERSION_MISMATCH + " for further details.");
}
} catch (java.lang.NoSuchFieldError nsfe) {
// 旧版实现库(如 log4j 1.2)未声明 REQUESTED_API_VERSION 字段。
// 不报错,保持向后兼容
// given our large user base and SLF4J's commitment to backward
// compatibility, we cannot cry here. Only for implementations
// which willingly declare a REQUESTED_API_VERSION field do we
// emit compatibility warnings.
} catch (Throwable e) {
// we should never reach here
Util.report("Unexpected problem occured during version sanity check", e);
}
}
版本兼容设计
通过非final的REQUESTED_API_VERSION字段实现:
- 避免编译器优化为字面量
- 允许实现库小版本升级
- 保持api与实现的松耦合
版本检查算法:
- 前缀匹配机制
- 容忍旧版实现(
NoSuchFieldError) - 明确的不兼容提示
这个设计的关键在于REQUESTED_API_VERSION字段没有使用final修饰符。根据Java语言规范(JLS 17.5.3):
- final字段的常量折叠(Constant Folding):
- 编译器会将final基本类型和String常量直接替换为字面量
- 例如:
final String VERSION = "1.6";在编译后等同于直接使用"1.6"
- 非final字段的动态绑定:
- 运行时才会解析字段的实际值
- 允许不同的实现类提供不同的版本号
具体到SLF4J的实现:
// slf4j-api的LoggerFactory中
String requested = StaticLoggerBinder.REQUESTED_API_VERSION;
// 如果字段是final的,编译后会变成:
String requested = "1.6.99"; // 硬编码值
// 非final字段则保持为字段访问:
String requested = StaticLoggerBinder.REQUESTED_API_VERSION;
这种设计实现了:
- 编译时解耦:api和实现可以独立编译
- 运行时绑定:实际使用实现库提供的版本号
- 版本灵活性:实现库可以自由升级小版本
/**
* Declare the version of the SLF4J API this implementation is compiled against.
* The value of this field is modified with each major release.
* 不要final,final会被编译器优化为字面量!
* 如 String version = StaticLoggerBinder.REQUESTED_API_VERSION;
* 会被优化成 String version="1.6.99";
* 而不是真实实现StaticLoggerBinder的REQUESTED_API_VERSION值
* <p>
* 正是因为 StaticLoggerBinder 类被刻意从 slf4j-api 中移除(通过 org.slf4j.impl 分包),
* SLF4J 必须通过运行时动态读取实现库的版本声明,才能实现:
* 松耦合:API 和实现完全分离编译
* 动态适配:同一份 slf4j-api 可对接不同版本的日志实现
* 安全演进:当出现不兼容变更时能明确拒绝绑定
*/
// to avoid constant folding by the compiler, this field must *not* be final
public static String REQUESTED_API_VERSION = "1.6.99"; // !final

浙公网安备 33010602011771号