SLF4J简介

The Simple Logging Facade for Java (SLF4J) serves as a simple facade or abstraction for various logging frameworks, such as java.util.logging, logback and log4j. SLF4J allows the end-user to plug in the desired logging framework at deployment time.

简单来说,SLF4J提供了一套日志接口(还是面向接口编程的思想),具体的接口实现是用logback还是log4j或者其他日志框架,要看你部署时使用的jar包。
SLF4J使用

import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
     
    public class HelloWorld {
      public static void main(String[] args) {
        Logger logger = LoggerFactory.getLogger(HelloWorld.class);
        logger.info("Hello World");
      }
    }

要把这个程序跑起来需要到SLF4J官网下载jar包,当前最新的是slf4j-api-1.7.7.jar,然后需要再选择以下jar包来绑定具体的日志框架:

slf4j-log4j12-1.7.7.jar
    slf4j-jdk14-1.7.7.jar
    slf4j-nop-1.7.7.jar
    slf4j-simple-1.7.7.jar
    slf4j-jcl-1.7.7.jar
    logback-classic-1.0.13.jar (非SLF4J项目提供,不推荐)

需要切换到不同日志框架时,只需替换上述jar包即可。

贴上一张SLF4J框架图帮助理解,

实现原理

这么碉堡的功能,是怎么做到的呢?说来简单,但又很奇葩,先看一下LoggerFactory.getLogger,

/**
       * Return a logger named according to the name parameter using the statically
       * bound {@link ILoggerFactory} instance.
       *
       * @param name The name of the logger.
       * @return logger
       */
      public static Logger getLogger(String name) {
        ILoggerFactory iLoggerFactory = getILoggerFactory();
        return iLoggerFactory.getLogger(name);
      }

调用了getILoggerFactory,

/**
       * Return the {@link ILoggerFactory} instance in use.
       * <p/>
       * <p/>
       * ILoggerFactory instance is bound with this class at compile time.
       *
       * @return the ILoggerFactory instance in use
       */
      public static ILoggerFactory getILoggerFactory() {
        if (INITIALIZATION_STATE == UNINITIALIZED) {
          INITIALIZATION_STATE = ONGOING_INITIALIZATION;
          performInitialization();
        }
        switch (INITIALIZATION_STATE) {
          case SUCCESSFUL_INITIALIZATION:
            return StaticLoggerBinder.getSingleton().getLoggerFactory();
          case NOP_FALLBACK_INITIALIZATION:
            return NOP_FALLBACK_FACTORY;
          case FAILED_INITIALIZATION:
            throw new IllegalStateException(UNSUCCESSFUL_INIT_MSG);
          case ONGOING_INITIALIZATION:
            // support re-entrant behavior.
            // See also http://bugzilla.slf4j.org/show_bug.cgi?id=106
            return TEMP_FACTORY;
        }
        throw new IllegalStateException("Unreachable code");
      }

继续跟下去可以看到最后是通过StaticLoggerBinder绑定到具体的日志框架,但是这个StaticLoggerBinder很神奇,看下图

通过对比SLF4J的jar包跟源码包发现,源码包竟然多出了一个impl目录,此目录有以下几个文件,

package.html
    StaticLoggerBinder.java
    StaticMarkerBinder.java
    StaticMDCBinder.java

看一下StaticLoggerBinder.java,

public class StaticLoggerBinder {
     
      /**
       * The unique instance of this class.
       */
      private static final StaticLoggerBinder SINGLETON = new StaticLoggerBinder();
      
      /**
       * Return the singleton of this class.
       *
       * @return the StaticLoggerBinder singleton
       */
      public static final StaticLoggerBinder getSingleton() {
        return SINGLETON;
      }
      
      /**
       * Declare the version of the SLF4J API this implementation is compiled against.
       * The value of this field is usually modified with each release.
       */
      // to avoid constant folding by the compiler, this field must *not* be final
      public static String REQUESTED_API_VERSION = "1.6.99";  // !final
      
      private StaticLoggerBinder() {
        throw new UnsupportedOperationException("This code should have never made it into slf4j-api.jar");
      }
     
      public ILoggerFactory getLoggerFactory() {
        throw new UnsupportedOperationException("This code should never make it into slf4j-api.jar");
      }
     
      public String getLoggerFactoryClassStr() {
        throw new UnsupportedOperationException("This code should never make it into slf4j-api.jar");
      }
    }

"This code should have never made it into slf4j-api.jar",想来想去,只有一种可能,就是打完jar包后,手工将impl这个目录从jar包中删除了。而在上述slf4j-log4j12-1.7.7.jar等包中,你都可以找到org.slf4j.impl.StaticLoggerBinder这个类。

到这里应该可以看出来,这是一种很奇葩的扩展方式,先使用了一个类,将它打进jar包,然后从jar包中剔除,通过在其他jar包中使用同样类名新建一个类(至于这个类具体用来做啥并无限制,也没有接口来约束)来达到扩展的效果。目前看来这样的方式有两个小问题:

需要防止一些编译器优化。例如上面看到的StaticLoggerBinder.REQUESTED_API_VERSION,为防止编译器的constant folding,不能声明为final。
    对于被扩展的类约束太少。为了解决这个问题,SLF4J专门定义了一个org.slf4j.spi.LoggerFactoryBinder,具体实现的org.slf4j.impl.StaticLoggerBinde都遵循了这个接口,但这其实只是一种约定,而不是约束。

使用反射?

今天某同学说,为嘛不直接反射出一个接口,例如LoggerFactoryBinder,我想了想,ServiceLoader机制不就是使用反射的吗,SLF4J真是不走寻常路^_^
参考资料

http://www.slf4j.org/manual.html

---------------------
作者:kisimple
来源:CSDN
原文:https://blog.csdn.net/kisimple/article/details/38664717
版权声明:本文为博主原创文章,转载请附上博文链接!

Java平台扩展机制#3:SLF4J怪招相关推荐

  1. Java扩展机制可加载所有JAR

    Java扩展机制在Java教程中被描述为"一种标准的,可伸缩的方式,以使自定义API可供Java平台上运行的所有应用程序使用." 如了解扩展类加载中所述 ,"扩展框架利用 ...

  2. Java 动态代理机制分析及扩展--转

    http://www.ibm.com/developerworks/cn/java/j-lo-proxy1/#icomments http://www.ibm.com/developerworks/c ...

  3. Java 动态代理机制分析及扩展,第 1 部分

    引言 Java 动态代理机制的出现,使得 Java 开发人员不用手工编写代理类,只要简单地指定一组接口及委托类对象,便能动态地获得代理类.代理类会负责将所有的方法调用分派到委托对象上反射执行,在分派执 ...

  4. Java 动态代理机制分析及扩展

    简介: 本文通过分析 Java 动态代理的机制和特点,解读动态代理类的源代码,并且模拟推演了动态代理类的可能实现,向读者阐述了一个完整的 Java 动态代理运作过程,希望能帮助读者加深对 Java 动 ...

  5. 【转载】Java JVM 运行机制及基本原理

    原博地址:https://zhuanlan.zhihu.com/p/25713880 JVM的基础概念 JVM的中文名称叫Java虚拟机,它是由软件技术模拟出计算机运行的一个虚拟的计算机. JVM也充 ...

  6. 高级开发必须理解的Java中SPI机制

    本文通过探析JDK提供的,在开源项目中比较常用的Java SPI机制,希望给大家在实际开发实践.学习开源项目提供参考. 1 SPI是什么 SPI全称Service Provider Interface ...

  7. Java 面试经典题解析:谈谈你对 Java 平台的理解?

    作者|杨晓峰出处|极客时间<Java技术核心 36讲>专栏 从你接触 Java开发到现在,你对 Java最直观的印象是什么呢?是它宣传的 "Compile once, run a ...

  8. 介绍 Java 平台的 Jazzy:一种新的拼写检查器 API

    计算机擅长执行快速搜索操作,可以根据给定的搜索词,对大量存储的信息快速进行搜索.但是,拼写检查应用程序所要求的搜索能力,不仅仅是正确的字符串匹配.在这篇文章中,我将介绍搜索算法的一些历史,包括语音匹配 ...

  9. 36.JVM内存分哪几个区,每个区的作用是什么、如和判断一个对象是否存活、java垃圾回收机制、垃圾收集的方法有哪些、java类加载过程、类加载机制、双亲委派、Minor GC和Major GC

    36.JVM内存分哪几个区,每个区的作用是什么? 37.如和判断一个对象是否存活?(或者GC对象的判定方法) 38.简述java垃圾回收机制? 39.java中垃圾收集的方法有哪些? 40.java类 ...

最新文章

  1. 10行Python代码自动清理电脑内重复文件,释放双手!
  2. 如何破解安卓手机上的图形锁(九宫格锁)
  3. Tool之Git:Git的简介、安装、使用方法之详细攻略
  4. Matplotlib 中文用户指南 4.6 编写数学表达式
  5. sum服务器操作系统,SUM服务器监控软件
  6. 【实习之T100开发】T100 基础架构、命名原则
  7. 1,3+–二苯基胍行业调研报告 - 市场现状分析与发展前景预测
  8. Android平台程序崩溃的类型及原因列举
  9. 深度学习菜鸟的信仰地︱Supervessel超能云服务器、深度学习环境全配置
  10. MATLAB矩阵计算大全
  11. 裴礼文3.2.34解答
  12. VMLogin反指纹超级浏览器-模拟真人输入-使用方法
  13. 回忆2018年高教杯数学建模大赛
  14. 鬼谷八荒流派收集(4)核弹指
  15. AFNetworkReachabilityManager检测网络状态
  16. 【Pyecharts50例】GEO图中忽略不存在的位置
  17. 用百度进行站内搜索源代码
  18. 认知BLE中多个MAC地址类型:Public Device Address、Random Device Address、Non-resolvable Private Address
  19. USB CDC 程序架构分析
  20. 从心认识“完美小助手”(推荐)

热门文章

  1. python 自定义模块的发布和安装
  2. Docker服务器的图形显示方案
  3. 罗杰斯:做你喜欢的工作,你会变成个有钱人
  4. VIJOS 1052贾老二算算术 (高斯消元)
  5. RDIFramework.NET 中多表关联查询分页实例
  6. 12款响应式的 jQuery 旋转木马(传送带)插件
  7. JavaEE6入门02—Myeclipse8.5+GlassFish
  8. 如何看待 Apache Log4j 2 远程代码执行漏洞?
  9. 9个用来爬取网络站点的 Python 库
  10. Nacos配置管理-微服务配置拉取