什么时候SLF4J的“动态绑定”功能适合使用?

时间:2015-07-24 13:51:55

标签: java binding classpath classloader slf4j

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

通过这个,我的意思是如果你在编译类路径中包含slf4j-api,你现在可以访问该JAR中包含的所有API类(LoggersLoggerFactories等) ,但它们的实际运行时行为是无操作(不执行任何操作)除非在运行时类路径中包含“ SLF4J绑定”,例如slf4j-simple(其中将日志语句发送到STDOUTSTDERR),或slf4j-log4j,然后期望Log4J配置等。

如上所述,这种类型的动态绑定行为似乎是SLF4J项目所独有的。

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

所以我问:这个解决方案是否唯一能够解决日志记录问题?如果是这样,为什么?如果没有,那么还有哪些其他问题需要这种方法作为解决方案?

1 个答案:

答案 0 :(得分:5)

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

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

  1. 默认实现是无操作。对于大多数API,您需要实现执行有意义的操作,因此如果没有提供程序,API会将其视为致命错误,但SLF4J选择将其默认实现设置为无操作。我本人会选择将默认实现最小化地记录到System.err。

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

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

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