Mybatis框架基础支持层——日志模块(8)

前言

java开发中常用的日志框架有Log4j,Log4j2,Apache Commons Log,java.util.logging,slf4j等,这些工具对外的接口不尽相同。为了统一这些工具的接口,Mybatis定义了一套统一的日志接口供上层调用,并为上述日志框架提供了相应的适配器。

在Mybatis的日志模块中,使用了适配器模式。Mybatis调用其他日志模块时,使用了其内部接口(org.apache.ibatis.logging.Log接口)。但是第三方日志组件对外提供的接口各不相同,Mybatis为了集成和服用这些第三方日志组件,在其模块中提供了多种Adapter,将这些第三方日志组件对外接口适配成了org.apache.ibatis.logging.Log接口,这样Mybatis内部就可以统一通过org.apache.ibatis.logging.Log接口调用第三方日志组件的功能了。

日志适配器

Mybatis的日志模块位于org.apache.ibatis.logging包中,改模块中的Log接口定义了日志模块的功能,当然日志适配器也会实现此接口。LogFactory工厂类负责创建对应的日志组件适配器。

LogFactory类解析:

/**
 * 在LogFactory类加载时会执行其静态代码块,其逻辑是按序加载并实例化对应的日志适配器,
 * 然后使用LogConstructor这个静态字段,记录当前使用的第三方日志组件适配器
 */
public final class LogFactory {

    /**
     * Marker to be used by logging implementations that support markers
     */
    public static final String MARKER = "MYBATIS";

    /**
     * 记录当前使用的第三方日志组件所对应的适配器的构造方法
     */
    private static Constructor<? extends Log> logConstructor;

    /**
     * 对每种日志组件调用tryImplementation()进行尝试加载,具体的调用顺序是:
     * useSlf4jLogging——>useCommonsLogging——>useLog4J2Logging
     * ——>useLog4JLogging——>useJdkLogging——>useNoLogging
     */
    static {
        tryImplementation(new Runnable() {
            @Override
            public void run() {
                useSlf4jLogging();
            }
        });
        tryImplementation(new Runnable() {
            @Override
            public void run() {
                useCommonsLogging();
            }
        });
        tryImplementation(new Runnable() {
            @Override
            public void run() {
                useLog4J2Logging();
            }
        });
        tryImplementation(new Runnable() {
            @Override
            public void run() {
                useLog4JLogging();
            }
        });
        tryImplementation(new Runnable() {
            @Override
            public void run() {
                useJdkLogging();
            }
        });
        tryImplementation(new Runnable() {
            @Override
            public void run() {
                useNoLogging();
            }
        });
    }

    private LogFactory() {
        // disable construction
    }

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

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

    public static synchronized void useCustomLogging(Class<? extends Log> clazz) {
        setImplementation(clazz);
    }
    /**
     * 使用Slf4j日志组件
     */
    public static synchronized void useSlf4jLogging() {
        setImplementation(org.apache.ibatis.logging.slf4j.Slf4jImpl.class);
    }

    public static synchronized void useCommonsLogging() {
        setImplementation(org.apache.ibatis.logging.commons.JakartaCommonsLoggingImpl.class);
    }

    public static synchronized void useLog4JLogging() {
        setImplementation(org.apache.ibatis.logging.log4j.Log4jImpl.class);
    }

    public static synchronized void useLog4J2Logging() {
        setImplementation(org.apache.ibatis.logging.log4j2.Log4j2Impl.class);
    }

    public static synchronized void useJdkLogging() {
        setImplementation(org.apache.ibatis.logging.jdk14.Jdk14LoggingImpl.class);
    }

    public static synchronized void useStdOutLogging() {
        setImplementation(org.apache.ibatis.logging.stdout.StdOutImpl.class);
    }

    public static synchronized void useNoLogging() {
        setImplementation(org.apache.ibatis.logging.nologging.NoLoggingImpl.class);
    }

    /**
     * 此方法会先检测logConstructor,若为空则调用runnable.run()方法
     */
    private static void tryImplementation(Runnable runnable) {
        if (logConstructor == null) {
            try {
                runnable.run();
            } catch (Throwable t) {
                // ignore
            }
        }
    }

    private static void setImplementation(Class<? extends Log> implClass) {
        try {
            //获取指定适配器的构造方法
            Constructor<? extends Log> candidate = implClass.getConstructor(String.class);
            //实例化适配器
            Log log = candidate.newInstance(LogFactory.class.getName());
            //输出日志
            if (log.isDebugEnabled()) {
                log.debug("Logging initialized using '" + implClass + "' adapter.");
            }
            //初始化logConstructor字段
            logConstructor = candidate;
        } catch (Throwable t) {
            throw new LogException("Error setting Log implementation.  Cause: " + t, t);
        }
    }

}

JDBC调试

在Mybatis的日志模块中有一个jdbc包,它并不是将日志信息通过jdbc存入数据库,而是通过JDK动态代理的方式,将JDBC操作通过制定的日志框架打印出来。这个功能通常在开发阶段使用,它可以输出sql语句、用户传入的参数、sql影响的行数等信息。对调试程序非常有用。

BaseJdbcLogger是一个抽象类,它是jdbc包下其他Logger类的父类,具体作用请看源码分析,此处不贴源码:

 

 

posted @ 2019-02-12 14:14  ^^ITBOY^^  阅读(282)  评论(0编辑  收藏  举报