slf4j如何绑定到实现?它真的在编译时这样做吗



在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允许最终用户在部署时插入所需的日志框架。

最新更新