浅谈mybatis的日志适配模式

原创
2019/03/18 23:53
阅读数 1.7K

Java开发中经常用到的日志框架有很多,Log4j、Log4j2、slf4j等等,Mybatis定义了一套统一的日志接口供上层使用,并为上述常用的日志框架提供了相应的适配器。有关适配器模式例子可以参考 设计模式整理

适配器模式中有接口适配和委托适配两种,当然我们可以把他们统一成又有接口又有委托,而不使用抽象类。

首先Mybatis中有一个日志框架的接口,这个接口可能代表以上的各种日志框架。

package org.apache.ibatis.logging;

public interface Log {
    boolean isDebugEnabled();

    boolean isTraceEnabled();

    void error(String var1, Throwable var2);

    void error(String var1);

    void debug(String var1);

    void trace(String var1);

    void warn(String var1);
}

以Slf4j为例来看它的适配器,这是一个典型的委托适配器,委托了Slf4j本身的对象,以及对该对象的调用。

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package org.apache.ibatis.logging.slf4j;

import org.apache.ibatis.logging.Log;
import org.slf4j.Logger;

class Slf4jLoggerImpl implements Log {
    private final Logger log;

    public Slf4jLoggerImpl(Logger logger) {
        this.log = logger;
    }

    public boolean isDebugEnabled() {
        return this.log.isDebugEnabled();
    }

    public boolean isTraceEnabled() {
        return this.log.isTraceEnabled();
    }

    public void error(String s, Throwable e) {
        this.log.error(s, e);
    }

    public void error(String s) {
        this.log.error(s);
    }

    public void debug(String s) {
        this.log.debug(s);
    }

    public void trace(String s) {
        this.log.trace(s);
    }

    public void warn(String s) {
        this.log.warn(s);
    }
}

调用以上适配器,并初始化一个真正的Slf4j的实例来使用该适配器。可以看成是以上适配器的一个延伸,主要是为了转化成一个String类型的单参构造器,方便统一调用。

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package org.apache.ibatis.logging.slf4j;

import org.apache.ibatis.logging.Log;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.Marker;
import org.slf4j.spi.LocationAwareLogger;

public class Slf4jImpl implements Log {
    private Log log;

    public Slf4jImpl(String clazz) {
        Logger logger = LoggerFactory.getLogger(clazz);
        if(logger instanceof LocationAwareLogger) {
            try {
                logger.getClass().getMethod("log", new Class[]{Marker.class, String.class, Integer.TYPE, String.class, Object[].class, Throwable.class});
                this.log = new Slf4jLocationAwareLoggerImpl((LocationAwareLogger)logger);
                return;
            } catch (SecurityException var4) {
                ;
            } catch (NoSuchMethodException var5) {
                ;
            }
        }

        this.log = new Slf4jLoggerImpl(logger);
    }

    public boolean isDebugEnabled() {
        return this.log.isDebugEnabled();
    }

    public boolean isTraceEnabled() {
        return this.log.isTraceEnabled();
    }

    public void error(String s, Throwable e) {
        this.log.error(s, e);
    }

    public void error(String s) {
        this.log.error(s);
    }

    public void debug(String s) {
        this.log.debug(s);
    }

    public void trace(String s) {
        this.log.trace(s);
    }

    public void warn(String s) {
        this.log.warn(s);
    }
}

Mybatis日志工厂使用这些适配器,从它的静态代码块来看,是启用线程任务来尝试加载各种日志框架。

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package org.apache.ibatis.logging;

import java.lang.reflect.Constructor;
import org.apache.ibatis.logging.commons.JakartaCommonsLoggingImpl;
import org.apache.ibatis.logging.jdk14.Jdk14LoggingImpl;
import org.apache.ibatis.logging.log4j.Log4jImpl;
import org.apache.ibatis.logging.log4j2.Log4j2Impl;
import org.apache.ibatis.logging.nologging.NoLoggingImpl;
import org.apache.ibatis.logging.slf4j.Slf4jImpl;
import org.apache.ibatis.logging.stdout.StdOutImpl;

public final class LogFactory {
    public static final String MARKER = "MYBATIS";
    //日志框架接口的构造器
    private static Constructor<? extends Log> logConstructor;
    //日志工厂的私有构造器,即它不能被实例化
    private LogFactory() {
    }

    public static Log getLog(Class<?> aClass) {
        return getLog(aClass.getName());
    }

    public static Log getLog(String logger) {
        try {
            return (Log)logConstructor.newInstance(new Object[]{logger});
        } catch (Throwable var2) {
            throw new LogException("Error creating logger for logger " + logger + ".  Cause: " + var2, var2);
        }
    }

    public static synchronized void useCustomLogging(Class<? extends Log> clazz) {
        setImplementation(clazz);
    }

    public static synchronized void useSlf4jLogging() {
        setImplementation(Slf4jImpl.class);
    }

    public static synchronized void useCommonsLogging() {
        setImplementation(JakartaCommonsLoggingImpl.class);
    }

    public static synchronized void useLog4JLogging() {
        setImplementation(Log4jImpl.class);
    }

    public static synchronized void useLog4J2Logging() {
        setImplementation(Log4j2Impl.class);
    }

    public static synchronized void useJdkLogging() {
        setImplementation(Jdk14LoggingImpl.class);
    }

    public static synchronized void useStdOutLogging() {
        setImplementation(StdOutImpl.class);
    }

    public static synchronized void useNoLogging() {
        setImplementation(NoLoggingImpl.class);
    }
    //如果前面的日志框架没有被加载,则加载后面的
    private static void tryImplementation(Runnable runnable) {
        if(logConstructor == null) {
            try {
                runnable.run();
            } catch (Throwable var2) {
                ;
            }
        }

    }
    //实例化适配器,并记录该适配器的构造器是否为空
    private static void setImplementation(Class<? extends Log> implClass) {
        try {
            //获取指定适配器的构造方法,因为这些适配器的构造器都是一个String类型的单参构造器
            Constructor<? extends Log> candidate = implClass.getConstructor(new Class[]{String.class});
            //使用构造器来实例化指定的适配器
            Log log = (Log)candidate.newInstance(new Object[]{LogFactory.class.getName()});
            if(log.isDebugEnabled()) {
                log.debug("Logging initialized using '" + implClass + "' adapter.");
            }
            //初始化日志工厂的日志框架构造器字段
            logConstructor = candidate;
        } catch (Throwable var3) {
            throw new LogException("Error setting Log implementation.  Cause: " + var3, var3);
        }
    }
    //根据各种日志框架,来尝试加载,依次顺序为Slf4j,Commons,Log4j2,Log4j,jdk,useNo。直到加载成功一个为止,后面的将不再加载。
    static {
        tryImplementation(new Runnable() {
            public void run() {
                LogFactory.useSlf4jLogging();
            }
        });
        tryImplementation(new Runnable() {
            public void run() {
                LogFactory.useCommonsLogging();
            }
        });
        tryImplementation(new Runnable() {
            public void run() {
                LogFactory.useLog4J2Logging();
            }
        });
        tryImplementation(new Runnable() {
            public void run() {
                LogFactory.useLog4JLogging();
            }
        });
        tryImplementation(new Runnable() {
            public void run() {
                LogFactory.useJdkLogging();
            }
        });
        tryImplementation(new Runnable() {
            public void run() {
                LogFactory.useNoLogging();
            }
        });
    }
}
展开阅读全文
加载中

作者的其它热门文章

打赏
0
2 收藏
分享
打赏
0 评论
2 收藏
0
分享
返回顶部
顶部