java - SLF4J 的 “dynamic binding” 功能什么时候适合使用?

标签 java binding classpath classloader slf4j

我对 SLF4J 很感兴趣,因为它似乎是唯一一个在运行时使用类的所谓“动态绑定(bind)”来定义行为的 Java 库(至少我可以解释) .

我的意思是,如果您在编译类路径中包含 slf4j-api,您现在可以访问所有 API 类(LoggersLoggerFactories 等)包含在该 JAR 中,但它们的实际运行时行为是无操作(什么都不做)除非您在运行时包含“SLF4J 绑定(bind)”类路径,例如 slf4j-simple(将日志语句发送到 STDOUTSTDERR)或 slf4j-log4j ,然后需要 Log4J 配置等。

正如我上面所说,这种类型的动态绑定(bind)行为似乎是 SLF4J 项目所独有的。

我想知道为什么?一般来说,在日志记录之外,什么样的场景需要这种动态绑定(bind)作为解决方案?对我来说,它似乎是经典依赖注入(inject)(Spring、Guice)的替代方案,几乎将注入(inject)推迟到运行时类路径上可用的匹配类的即时(“JIT”)确定。

所以我问:这个解决方案是否唯一保证只能解决日志记录问题?如果是这样,为什么?如果不是,那么还有哪些其他问题值得采用这种方法作为他们的解决方案?

最佳答案

使用动态定位实现提供者的工厂类分离接口(interface)和实现并不是 SLF4J 独有的。 Java 和 Java EE 中的许多 API 使用该模式。例如,javax.xml.parsers.SAXParserFactory 和 javax.json.Json。

SLF4J 的方法有一些独特之处:

  1. 默认实现是空操作。对于大多数 API,您需要实现来做一些有意义的事情,因此如果没有提供者,API 会将其视为 fatal error ,但 SLF4J 选择将其默认实现设为空操作。我个人会选择让默认实现最少记录到 System.err。

  2. 工厂实现使用static 类名来查找实现类(因此所有实现都具有相同的类名)而不是使用反射。您可以通过转到 https://github.com/qos-ch/slf4j/find/master 来观察这一点。并在页面任意位置键入 t 字符以打开文件查找器并输入 StaticLoggerBinder:您会发现所有绑定(bind)实现都使用相同的类名。

    这种方法的缺点是你只能有一个实现(相对于你可以加载多个实现类的反射),并且你必须将实现打包在与接口(interface)相同的类路径上(相对于你可以从中加载实现的反射)上下文类加载器)。然而,后者实际上被认为是一个优势,因为应用服务器往往会错误地处理接口(interface)/实现拆分,如果应用服务器和应用程序都包含同一库的副本,这会导致问题,这是 commons-logging、log4j 的一个非常常见的问题,等等。这个问题并不是日志记录所独有的,因为当应用程序服务器尝试进行 XML 解析但意外发现应用程序已经打包了自己的 XML 解析 API 时,人们经常(或曾经)遇到 XML 解析库的 ClassCastException。

    这种方法的另一个优势是性能。由于工厂 API 静态引用实现,因此没有反射开销来定位和提供者,也没有虚拟方法开销来调用提供者。在实践中,JIT 通常最终会认识到工厂 API 无论如何只会调用一个实现,因此它会对其进行优化。然而,这也许就是日志记录与其他 API 不同的原因:对于日志记录,您通常从类静态初始化器调用工厂 API,这意味着您在应用程序首次启动时多次调用工厂 API,然后再也不会调用。这对 JIT 来说是最糟糕的情况:当它意识到需要优化对提供者的调用时,您的应用程序已经完成了对记录器工厂 API 的调用。

关于java - SLF4J 的 “dynamic binding” 功能什么时候适合使用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31612335/

相关文章:

java命令行错误

OR比较的Java顺序

binding - ember.js 中的 bind-attr 是如何工作的?

binding - 将 Mvvmcross 绑定(bind)与 MonoTouch.Dialog(列表和命令)结合使用

java - java中的类路径变量

java - 类路径问题

java - 没有属性的 getter 方法...错误

WPF ViewModel 命令可以执行问题

android - 将引用的 jar 添加到 android build.xml

Java - 获取 StackOverflowError