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

sme*_*eeb 3 java binding classpath classloader slf4j

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

通过这个,我的意思是,如果你slf4j-api在编译类路径中包含,你现在可以访问该JAR中包含的所有API类(LoggersLoggerFactories等),但它们的实际运行时行为是no-op(什么都不做),除非你包含一个运行时类路径上的" SLF4J绑定 ",例如slf4j-simple(将日志语句发送到STDOUTSTDERR),或者slf4j-log4j,然后期望Log4J配置等.

就像我上面所说的,这种类型的动态绑定行为似乎是SLF4J项目所特有的.

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

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

Bre*_*ail 5

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

SLF4J的方法有一些独特的方面:

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

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

    这种方法的缺点是你只能有一个实现(vs反射你可以加载多个实现类),你必须将实现打包在与接口相同的类路径上(vs反射,你可以从上下文类加载实现)装载机).但是,后者实际上被认为是一个优势,因为应用程序服务器倾向于错误处理接口/实现拆分,如果应用程序服务器和应用程序都包含同一个库的副本,这会导致问题,这是commons-logging,log4j的一个非常常见的问题,等等

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