zoukankan      html  css  js  c++  java
  • spring集成SLF4J时的误区及延展

    Spring Framework 所使用的日志接口一直都是 commons-logging,Apache Commons Logging是一个通用的日志接口,与slf4j简单日志门面类似。如果它搜索到应用添加了log4j的引用,那么将直接使用log4j,如果你想用现在越来越流行的SLF4J来接管日志接口,则需要使用SLF4J提供的 jcl-over-slf4j 把 commons-logging API 转接到 SLF4J API 上。

    问题

    一般的教程在介绍maven配置时除了让添加 slf4j-api 和 jcl-over-slf4j 的jar包外,还会提醒移除spring中对 commons-logging 的依赖(如下)。然而诸位有没发现就算你不移除该依赖,依然可以识别到SLF4J。其实这个也不能完全称之为误区,但 jcl-over-slf4j 到底是如何实现接管的呢?

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>${spring-version}</version>
        <scope>runtime</scope>
        <exclusions>
        <exclusion>
            <artifactId>commons-logging</artifactId>
            <groupId>commons-logging</groupId>
        </exclusion>
        </exclusions>
    </dependency>

    分析

    Apache Commons Logging (原名 Jakarta Commons Logging,JCL) 只提供 log 接口,具体的实现则在运行时动态寻找,如果找不到外部实现那么将使用自带的实现类。

    我们先从调用开始说起,JCL的调用如下:

    import org.apache.commons.logging.Log;
    import org.apache.commons.logging.LogFactory;
    Log log = LogFactory.getLog(Main.class.getName());

    getLog()方法的实现如下:

     public static Log getLog(Class clazz) {
         return (getFactory().getInstance(clazz));
     }

    也就是说JCL先通过getFactory()搜索工厂实现类,然后使用它来获取Log实例。getFactory()方法很长,我大致说下加载的过程(markdown自动生成的流程图有点问题):

    Created with Raphaël 2.1.0开始寻找Factory无法找到Factory缓存无法找到环境变量org.apache.commons.logging.LogFactory设置的类无法找到日志实现服务(service)无法找到commons-logging.properties文件中org.apache.commons.logging.LogFactory设置的类使用自带的LogFactoryImpl类缓存Factory寻找完成返回nullyesnoyesnoyesnoyesnoyesno

    通常环境变量和配置文件都是空的,关键步骤在于第三步中的“找到日志实现服务”。JDK中关于该步骤的注解如下:

    尝试使用JDK1.3定义的服务加载机制来搜寻服务,它需要读取META-INF/services目录下的文件,该文件的内容指定了实现所需接口的类。

    这其实就是Java SPI(Service Provider Interface)的约定。一个服务通常指的是已知的接口或者抽象类,服务提供方就是对这个接口或者抽象类的实现。按照SPI制定的标准提供方需要在资源路径META-INF/services目录下放置一个文本文件,文件的命名为该服务接口的全限定名,内容为实现类的全限定名。

    好,这时候我们打开jcl-over-slf4j.jar文件,可以看到在META-INF/services目录下有一个org.apache.commons.logging.LogFactory文件,内容为org.apache.commons.logging.impl.SLF4JLogFactory,该实现类就在本jar包类。通过这种SPI约定的加载方式,SLF4J就接管了Apache Commons Logging,然后只需要引入SLF4J针对log4j或logback提供的桥接包,就可以让spring表面上使用的是Apache Commons Logging,实际上用的却是你提供的日志框架。

    延展

    如果你不想接入SLF4J,那么最后使用的自带LogFactoryImpl类将会依次按照以下顺序查找,若找到则直接加载该实现类(实现类也是自带的commons-logging.jar中):

    1. org.apache.commons.logging.impl.Log4JLogger
    2. org.apache.commons.logging.impl.Jdk14Logger
    3. org.apache.commons.logging.impl.Jdk13LumberjackLogger
    4. org.apache.commons.logging.impl.SimpleLog

    可以看到如果你引入了log4j的jar包,优先也将使用log4j,那么为什么要用SLF4代替Commons Logging呢?大致原因有:

    1. SLF4J是编译时绑定到具体的日志框架,性能优于采用运行时搜寻的方式的commons-logging。但其代价便是如果你需要使用某一种日志实现,那么你必须手动引入该日志实现的SLF4J桥接包(见本文末尾)。
    2. SLF4J提供了更好的日志记录方式,带来下这几方面的好处:
      1. 更好的可读性;
      2. 不需要使用logger.isDebugEnabled()来解决日志因为字符拼接产生的性能问题。
    3. Apache Common Logging使用的类加载机制无法在OSGI环境下工作。

    SLF4和Commons Logging其实都是对不同日志框架提供的一个门面封装,可以在部署的时候不修改任何配置即可接入一种日志实现方案。也就是说上述的方法其实就是双层门面,一层套一层。

    此外,在不用SLF4J时,Apache Common Logging默认的Log4JLogger使用的1.x版本的Log4J,若要使用2.x版本需要加入log4j提供的Apache Commons Logging 桥接器log4j-jcl.jar,其原理也是在META-INF/services目录下配置了自己的实现类org.apache.logging.log4j.jcl.LogFactoryImpl

    最后附上一张SLF4J提供的所有桥接方案,开发者须针对引入的日志实现来选择对应的桥接包。
    SLF4J桥接方案

  • 相关阅读:
    域名ICP备案个人备案写网站名称注意事项
    关于域名备案的注意事项
    MySQL里默认的几个库是干啥的?
    Python 1.3 元组与文件
    PTA(BasicLevel)-1006换个格式输出整数
    数据结构与算法-图的最短路径Dijkstra
    PTA(Basic Level)-1002 写出这个数
    PTA(Basic Level)-1076 Wifi密码
    C程序设计语言笔记-第一章
    谁能笑到最后,约瑟夫环-Josephus问题求解
  • 原文地址:https://www.cnblogs.com/yangcheng33/p/6557314.html
Copyright © 2011-2022 走看看