16

在 slf4j 的文档中,它说绑定发生在编译时间:

“SLF4J 不依赖于任何特殊的类加载器机制。事实上,每个 SLF4J 绑定在编译时都是硬连线的,以使用一个且只有一个特定的日志框架。例如,slf4j-log4j12-1.7.5.jar 绑定绑定在编译时使用 log4j。在您的代码中,除了 slf4j-api-1.7.5.jar 之外,您只需将一个且只有一个您选择的绑定放到适当的类路径位置。不要在您的类路径。这是一般想法的图形说明。http://www.slf4j.org/manual.html

这是如何运作的?

4

6 回答 6

9

这是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;
}
于 2016-10-11T07:23:03.450 回答
6

这也是我的问题,我想添加我的答案,因为发现其他两个答案不够清楚(尽管完全正确)。

LoggerFactory.bind()首先,在slf4j-api链接)的执行中检查这一行

// 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执行绑定的类。

于 2016-08-04T14:17:36.770 回答
4

据我所见,它通过期望类 StaticLoggingBinder 位于同一个包(org.slf4j.impl)中来实现这一点,而不管实现如何 - 所以它总是在同一个地方找到它。

于 2014-02-18T16:11:07.073 回答
2

从技术上讲,编译时不会发生神奇的“绑定”。“绑定”发生在 SLF4J 开发人员创建库来处理最流行的 Java 日志框架时。

当文档说“绑定在编译时是硬连线的”时,这意味着 SLF4J 开发人员已经为特定的 Java 日志框架创建了一个目标库。SLF4J 有专门用于 Java Logging、Jakarta Commons Logging、Log4J 和控制台输出的库。您只需要在运行时包含其中一个库,SLF4J 才能成功创建日志消息。

有关 SLF4J 如何工作的更多信息:了解 SLF4J 的更直观的方式

于 2014-08-05T17:25:39.343 回答
1

正如@Rad 所说。

我想补充的是,如果您在运行时环境中有多个实现, slf4jStaticLoggerBinder随机选择其中一个,如multiple_bindings中所述:

SLF4J 选择绑定的方式是由 JVM 决定的,并且出于所有实际目的应该被认为是随机的。从 1.6.6 版本开始,SLF4J 将命名它实际绑定到的框架/实现类。

另一个需要注意的是,如果您的项目计划成为其他项目的库,则只slf4j-api应包含在内,slf4j-api不允许任何实现:

诸如库或框架之类的嵌入式组件不应声明对任何 SLF4J 绑定的依赖,而应仅依赖于 slf4j-api。当库声明对 SLF4J 绑定的编译时依赖时,它会将绑定强加给最终用户,从而否定 SLF4J 的目的。

于 2018-12-28T05:39:12.577 回答
1

该实现在编译时未绑定(它不是静态/早期绑定),或者换句话说,它在编译时未知的实现

实际上,在运行时绑定实现是反之亦然,这意味着实现是通过动态/运行时绑定发现的。Slf4j 人实际上已经在他们的手册https://www.slf4j.org/manual.html中声明了绑定是如何发生的:

SLF4J 允许最终用户在部署时插入所需的日志框架。

于 2019-09-24T08:44:35.260 回答