淺談mybatis的日誌適配模式

Java開發中常常用到的日誌框架有不少,Log4j、Log4j二、slf4j等等,Mybatis定義了一套統一的日誌接口供上層使用,併爲上述經常使用的日誌框架提供了相應的適配器。有關適配器模式例子能夠參考 設計模式整理 java

適配器模式中有接口適配和委託適配兩種,固然咱們能夠把他們統一成又有接口又有委託,而不使用抽象類。apache

首先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日誌工廠使用這些適配器,從它的靜態代碼塊來看,是啓用線程任務來嘗試加載各類日誌框架。this

//
// 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();
            }
        });
}
}
相關文章
相關標籤/搜索