在slf4j的文档中,它说绑定发生在compile时间:
"SLF4J不依赖于任何特殊的类装载机。事实上,每个SLF4J绑定在编译时都是硬连接的,只使用一个特定的日志框架。例如,SLF4J-log4j12-1.7.5.jar绑定是在编译时绑定的,只需使用log4j。在您的代码中,除了SLF4J-api-1.7.5.jar之外,您只需将自己选择的一个绑定拖放到适当的类路径loca上tion。不要在类路径上放置多个绑定。这是一个总体思路的图解说明。"http://www.slf4j.org/manual.html
这是怎么回事?
这是slf4j的源代码。Slf4j将在路径为org/slf4j/impl/StaticLoggerBinder.class
的类路径中找到所有类。如果有多个,jvm将只随机选取一个。有关更多详细信息,您可以在此处查看:http://www.slf4j.org/codes.html#multiple_bindings
// We need to use the name of the StaticLoggerBinder class, but we can't
// reference
// the class itself.
private static String STATIC_LOGGER_BINDER_PATH = "org/slf4j/impl/StaticLoggerBinder.class";
static Set<URL> findPossibleStaticLoggerBinderPathSet() {
// use Set instead of list in order to deal with bug #138
// LinkedHashSet appropriate here because it preserves insertion order
// during iteration
Set<URL> staticLoggerBinderPathSet = new LinkedHashSet<URL>();
try {
ClassLoader loggerFactoryClassLoader = LoggerFactory.class.getClassLoader();
Enumeration<URL> paths;
if (loggerFactoryClassLoader == null) {
paths = ClassLoader.getSystemResources(STATIC_LOGGER_BINDER_PATH);
} else {
paths = loggerFactoryClassLoader.getResources(STATIC_LOGGER_BINDER_PATH);
}
while (paths.hasMoreElements()) {
URL path = paths.nextElement();
staticLoggerBinderPathSet.add(path);
}
} catch (IOException ioe) {
Util.report("Error getting resources from path", ioe);
}
return staticLoggerBinderPathSet;
}
这也是我的问题,我想添加我的答案,因为我发现另外两个答案不够清楚(尽管完全正确)。
首先,在slf4j-api
(链路)中LoggerFactory.bind()
的实现中检查这一行
// the next line does the binding
StaticLoggerBinder.getSingleton();
有一个类叫做org.slf4j.impl.StaticLoggerBinder
。检查它在github上的实现。
现在继续从中央maven存储库下载slf4j-api.jar,提取它并找到StaticLoggerBinder.class
文件。
不要尝试!你不能。事实上,整个org.slf4j.impl
已经从包装中移除。检查项目的pom.xml
:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<executions>
<execution>
<phase>process-classes</phase>
<goals>
<goal>run</goal>
</goals>
</execution>
</executions>
<configuration>
<tasks>
<echo>Removing slf4j-api's dummy StaticLoggerBinder and StaticMarkerBinder</echo>
<delete dir="target/classes/org/slf4j/impl"/>
</tasks>
</configuration>
</plugin>
最后,检查SLF4j的绑定包之一,例如slf4j-simple
。你能找到org.slf4j.impl.StaticLoggerBinder
班吗?
总之,当您在运行时环境中有slf4j-api.jar
和一个(并且只有一个)绑定包时,您就只有一个org.slf4j.impl.StaticLoggerBinder
类来执行绑定。
根据我所看到的,它通过期望类StaticLoggingBinder在同一个包(org.slf4j.impl)中来实现这一点,而不管实现是什么,所以它总是在同一位置找到它。
从技术上讲,编译时没有神奇的"绑定"。"绑定"发生在SLF4J开发人员创建库来处理最流行的Java日志框架时。
当文档说"绑定在编译时是硬连接的"时,这意味着SLF4J开发人员已经为特定的Java日志框架创建了一个有针对性的库。SLF4J有专门用于Java日志记录、Jakarta Commons日志记录、Log4J和控制台输出的库。为了使SLF4J成功创建日志消息,您需要在运行时仅包含这些库中的一个。
有关SLF4J如何工作的更多信息:了解SLF4J的更直观的方式。
正如@Rad所说。
我想补充的是,如果您在运行时环境中有多个StaticLoggerBinder
实现,则slf4j会随机选择其中一个,如multiple_bindings:中所述
SLF4J选择绑定的方式由JVM决定,出于所有实际目的,应该被视为随机的。从1.6.6版本开始,SLF4J将命名它实际绑定到的框架/实现类。
另一个注意事项是,如果您的项目计划成为其他项目的库,则只应包括slf4j-api
,不允许slf4j-api
的任何实现:
嵌入式组件(如库或框架)不应声明对任何SLF4J绑定的依赖,而应仅依赖于SLF4J-api。当库声明编译时依赖于SLF4J绑定时,它会将该绑定强加给最终用户,从而否定SLF4J的目的。
实现在编译时没有绑定(它不是静态/早期绑定),或者换句话说,实现在编译时是未知的。
事实上,反之亦然,实现在运行时绑定,这意味着实现是通过动态/运行时绑定发现的。Slf4j的人实际上已经在他们的手册中声明了绑定是如何发生的https://www.slf4j.org/manual.html:
SLF4J允许最终用户在部署时插入所需的日志框架。