资讯专栏INFORMATION COLUMN

slf4j 的实现发现

Joyven / 465人阅读

摘要:按照官方的说法,是一个用于日志系统的简单,允许最终用户在部署其应用时使用其所希望的日志系统。这个方法里就会通过去绑定具体的日志实现。我们直接看一下中的的源码实现首先通过判断一个类是否已经被加载过。

SLF4J,即简单日志门面(Simple Logging Facade for Java),不是具体的日志解决方案,而是通过Facade Pattern提供一些Java logging API,它只服务于各种各样的日志系统。按照官方的说法,SLF4J是一个用于日志系统的简单Facade,允许最终用户在部署其应用时使用其所希望的日志系统。
实际上,SLF4J所提供的核心API是一些接口以及一个LoggerFactory的工厂类。在使用SLF4J的时候,不需要在代码中或配置文件中指定你打算使用那个具体的日志系统。SLF4J提供了统一的记录日志的接口,只要按照其提供的方法记录即可,最终日志的格式、记录级别、输出方式等通过具体日志系统的配置来实现,因此可以在应用中灵活切换日志系统。具体的日志系统可以选用log4j,log4j2,logback等。
1.SLF4J 怎么找到具体的实现?
SLF4J的实现是通过org.slf4j.impl.StaticLoggerBinder 来进行加载具体的实现的

org.slf4j.impl.StaticLoggerBinder的代码实现(log4j2):

public final class StaticLoggerBinder implements LoggerFactoryBinder {

    /**
     * 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"; // !final

    private static final String LOGGER_FACTORY_CLASS_STR = Log4jLoggerFactory.class.getName();

    /**
     * The unique instance of this class.
     */
    private static final StaticLoggerBinder SINGLETON = new StaticLoggerBinder();

    /**
     * The ILoggerFactory instance returned by the {@link #getLoggerFactory}
     * method should always be the same object
     */
    private final ILoggerFactory loggerFactory;

    /**
     * Private constructor to prevent instantiation
     */
    private StaticLoggerBinder() {
        loggerFactory = new Log4jLoggerFactory();
    }

    /**
     * Returns the singleton of this class.
     *
     * @return the StaticLoggerBinder singleton
     */
    public static StaticLoggerBinder getSingleton() {
        return SINGLETON;
    }

    /**
     * Returns the factory.
     * @return the factor.
     */
    @Override
    public ILoggerFactory getLoggerFactory() {
        return loggerFactory;
    }

    /**
     * Returns the class name.
     * @return the class name;
     */
    @Override
    public String getLoggerFactoryClassStr() {
        return LOGGER_FACTORY_CLASS_STR;
    }
}

logback 的实现也是大同小异,都是实现 LoggerFactoryBinder 接口,然后再实现里提供ILoggerFactory的实现类给SLF4J 用来做到日志框架的绑定。

private Logger logger = LoggerFactory.getLogger(DynamicProxy.class);

LoggerFactory.getLogger(DynamicProxy.class) 是静态方法,SLF4J 在这里进行具体实现的绑定。

在这里有一块同步代码,确保ILoggerFactory 只有一个。

这个方法里就会通过 StaticLoggerBinder.getSingleton(); 去绑定具体的日志实现。
2.当项目里同时存在多个日志框架(用来实现SLF4J)的时候,系统会选择哪一个
这个是通过jvm的类加载机制来控制的,会选择classpath 路径里面出现在前面的哪一个日志框架

jvm包括三种类加载器:
第一种:bootstrap classloader:加载java的核心类。
第二种:extension classloader:负责加载jre的扩展目录中的jar包。
第三种:它负责在JVM被启动时,加载来自在命令java中的-classpath或者java.class.path系统属性或者 CLASSPATH操作系统属性所指定的JAR类包和类路径。

jvm 加载包名和类名相同的类时,先加载classpath中jar路径放在前面的,包名类名都相同,那jvm没法区分了,如果使用ide一般情况下是会提示发生冲突而报错,若不报错,只有第一个包被引入(在classpath路径下排在前面的包),第二个包会在classloader加载类时判断重复而忽略。

类加载器
虚拟机设计团队把加载动作放到JVM外部实现,以便让应用程序决定如何获取所需的类,JVM提供了3种类加载器:
· 启动类加载器(Bootstrap ClassLoader):负责加载 JAVA_HOMElib 目录中的,或通过-Xbootclasspath参数指定路径中的,且被虚拟机认可(按文件名识别,如rt.jar)的类。
· 扩展类加载器(Extension ClassLoader):负责加载 JAVA_HOMElibext 目录中的,或通过java.ext.dirs系统变量指定路径中的类库。
· 应用程序类加载器(Application ClassLoader):负责加载用户路径(classpath)上的类库。
JVM通过双亲委派模型进行类的加载,当然我们也可以通过继承java.lang.ClassLoader实现自定义的类加载器。

当一个类加载器收到类加载任务,会先交给其父类加载器去完成,因此最终加载任务都会传递到顶层的启动类加载器,只有当父类加载器无法完成加载任务时,才会尝试执行加载任务。
采用双亲委派的一个好处是比如加载位于rt.jar包中的类java.lang.Object,不管是哪个加载器加载这个类,最终都是委托给顶层的启动类加载器进行加载,这样就保证了使用不同的类加载器最终得到的都是同样一个Object对象。
在有些情境中可能会出现要我们自己来实现一个类加载器的需求,由于这里涉及的内容比较广泛,我想以后多带带写一篇文章来讲述,不过这里我们还是稍微来看一下。我们直接看一下jdk中的ClassLoader的源码实现:

protected synchronized Class loadClass(String name, boolean resolve)
        throws ClassNotFoundException {
    // First, check if the class has already been loaded
    Class c = findLoadedClass(name);
    if (c == null) {
        try {
            if (parent != null) {
                c = parent.loadClass(name, false);
            } else {
                c = findBootstrapClass0(name);
            }
        } catch (ClassNotFoundException e) {
            // If still not found, then invoke findClass in order
            // to find the class.
            c = findClass(name);
        }
    }
    if (resolve) {
        resolveClass(c);
    }
    return c;
}

· 首先通过Class c = findLoadedClass(name);判断一个类是否已经被加载过。
· 如果没有被加载过执行if (c == null)中的程序,遵循双亲委派的模型,首先会通过递归从父加载器开始找,直到父类加载器是Bootstrap ClassLoader为止。
· 最后根据resolve的值,判断这个class是否需要解析。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/76961.html

相关文章

  • 在云环境上使用SLF4J对Java程序进行日志记录

    摘要:我开发了一个应用,部署到云环境上之后,用测试发现不能按照我期望的工作,但是返回的消息对我没有任何帮助。的使用非常简单,在您的应用代码里将的和导入然后在引用代码里用获得实例然后用进行日志记录。 我开发了一个Java应用,部署到云环境上之后,用postman测试发现不能按照我期望的工作,但是返回的消息对我没有任何帮助。 showImg(https://segmentfault.com/im...

    TigerChain 评论0 收藏0
  • 聊一聊Java日志之一:slf4j

    摘要:一引言程序员都知道,日志对于一个应用系统来说至关重要,现在应该也没有无日志的系统了吧。具体的日志实现还是需要使用不同的日志框架来完成。然后如下使用测试测试测试测试运行结果发现并没有打印出日志,这是因为缺少了配置文件。 一、引言   程序员都知道,日志对于一个应用系统来说至关重要,现在应该也没有无日志的系统了吧。很多程序员都知道 Log4j,Logback,java.util.loggi...

    church 评论0 收藏0
  • 长文慎入-探索Java并发编程与高并发解决方案

    摘要:所有示例代码请见下载于基本概念并发同时拥有两个或者多个线程,如果程序在单核处理器上运行多个线程将交替地换入或者换出内存这些线程是同时存在的,每个线程都处于执行过程中的某个状态,如果运行在多核处理器上此时,程序中的每个线程都 所有示例代码,请见/下载于 https://github.com/Wasabi1234... showImg(https://upload-images.jians...

    SimpleTriangle 评论0 收藏0
  • Spring Boot日志框架实践

    摘要:概述应用中,日志一般分为以下个级别错误信息警告信息一般信息调试信息跟踪信息使用的作为内部的日志框架,其仅仅是一个日志接口,在实际应用中需要为该接口来指定相应的日志实现。默认的日志实现是,是自带的日志包,此外当然也支持这类很流行的日志实现。 showImg(https://segmentfault.com/img/remote/1460000014055501); 概述 Java应用中...

    inapt 评论0 收藏0
  • 如何配置SLF4J不同日志实现

    摘要:想要看级别的信息,你需要在启动时传入这个系统属性使用与日志现在我们可以试验并更换不同的日志实现,但你的程序代码可以保持不变。我们要做的是用另一个流行的日志实现来替换掉,比如。又一次,我们必须对我们选的每一个日志实现做配置。 使用slf4j库作为你的Java应用日志API层有很多好处,这里我会展示一小部分关于如何使用和配置它的例子。 你可以把slf4j想成一个Java的接口,然后你需要实...

    bovenson 评论0 收藏0

发表评论

0条评论

Joyven

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<