源碼閱讀-logback的StaticLoggerBinder如何提供ILoggerFactory的實現類


上一篇博客介紹了slf4j-api的核心類和接口,以及如何和日志實現框架對接的。簡而言之就是通過下面這行代碼:

return StaticLoggerBinder.getSingleton().getLoggerFactory();

每個實現門面slf4j-api的日志實現框架都提供了org.slf4j.impl.StaticLoggerBinder類,通過委托該類返回一個ILoggerFactory的實現類,從而和具體的日志實現框架進行綁定。

這篇博客就來講述一下,logback的StaticLoggerBinder如何提供ILoggerFactory的實現類。

 

從圖中可以看到,StaticLoggerBinder實現了LoggerFactory接口,提供了getLoggerFactory()方法。LoggerContext實現了ILoggerFactory接口,提供了getLogger(String name)方法。StaticLoggerBinder擁有成員變量LoggerContext和ContextSelectorStaticBinder。StaticLoggerBinder委托ContextInitializer來初始化LoggerContext和委托ContextSelectorStaticBinder來選擇一個上下文。

 

下面就來具體看看StaticLoggerBinder的代碼 :

/**
     * The unique instance of this class.
     * 餓漢單例
     *
     */
    private static StaticLoggerBinder SINGLETON = new StaticLoggerBinder();

    /**
     * 私有靜態對象
     * 只有該類能訪問的對象
     * 用來做權限控制  后續只能該類來調用
     */
    private static Object KEY = new Object();

    /**
     * 初始化該對象
     */
    static {
        SINGLETON.init();
    }

    /**
     * 該對象是否初始化
     */
    private boolean initialized = false;
    /**
     * ILoggerFactory的實現類
     */
    private LoggerContext defaultLoggerContext = new LoggerContext();
    /**
     * 上下文選擇器綁定者
     * 通過 把defaultLoggerContext傳遞給ContextSelectorStaticBinder
     * ContextSelectorStaticBinder 選擇創建不同的ContextSelector 來獲取不同環境的LoggerContext
     * 選擇策略  ContextSelector接口的實現有 ContextJNDISelector 和 DefaultContextSelector 策略模式的應用
     * ContextSelectorStaticBinder 作用主要是來選擇策略
     * 這種實現方式可以用來借鑒
     */
    private final ContextSelectorStaticBinder contextSelectorBinder = ContextSelectorStaticBinder.getSingleton();

 

 private StaticLoggerBinder() {
        //為上下文設置名稱
        defaultLoggerContext.setName(CoreConstants.DEFAULT_CONTEXT_NAME);
    }
 /**
*提供getSingleton()方法是對接slf4j的強制要求
*/
public
static StaticLoggerBinder getSingleton() { return SINGLETON; } /** * Package access for testing purposes. * 重置 */ static void reset() { SINGLETON = new StaticLoggerBinder(); SINGLETON.init(); } /** * Package access for testing purposes. * * 1.委托ContextInitializer對象初始化LoggerContext * 2.判斷上下文是否有狀態監聽器 如果沒有就用StatusPrinter打印上下文中的警告和錯誤狀態 * 3.把defaultLoggerContext傳遞給ContextSelectorStaticBinder,選擇一個contextSelector 初始化成員變量 * 在getLoggerFactory()方法中通過contextSelectorBinder.getContextSelector().getLoggerContext()來獲取loggerContext */ void init() { try { try { //委托ContextInitializer類對defaultLoggerContext進行初始化 //這里如果找到了任一配置文件,就會根據配置文件去初始化LoggerContext,如果沒找到,會使用默認配置。 new ContextInitializer(defaultLoggerContext).autoConfig(); } catch (JoranException je) { Util.report("Failed to auto configure default logger context", je); } // logback-292 if (!StatusUtil.contextHasStatusListener(defaultLoggerContext)) { StatusPrinter.printInCaseOfErrorsOrWarnings(defaultLoggerContext); } //對ContextSelectorStaticBinder類進行初始化 contextSelectorBinder.init(defaultLoggerContext, KEY); initialized = true; } catch (Exception t) { // see LOGBACK-1159 Util.report("Failed to instantiate [" + LoggerContext.class.getName() + "]", t); } } /** * LoggerFactoryBinder接口的實現方法 獲取ILoggerFactory的實現類 * 1.判斷是否已經初始化 若沒有則返回defaultLoggerContext * 2.若已經初始化 則通過contextSelectorBinder 返回loggerContext * @return */ public ILoggerFactory getLoggerFactory() { //如果initialized是false,那么會直接返回defaultLoggerContext if (!initialized) { return defaultLoggerContext; } //否則就委托剛才提到的ContextSelectorStaticBinder返回一個ContextSelector if (contextSelectorBinder.getContextSelector() == null) { throw new IllegalStateException("contextSelector cannot be null. See also " + NULL_CS_URL); } return contextSelectorBinder.getContextSelector().getLoggerContext(); } public String getLoggerFactoryClassStr() { return contextSelectorBinder.getClass().getName(); }

 

 

其中init()和getLoggerFactory()為核心方法。

這個初始化方法init()里做了2件事
第一件事是委托ContextInitializer類對defaultLoggerContext進行初始化。這里如果找到了任一配置文件,就會根據配置文件去初始化LoggerContext,如果沒找到,會使用默認配置。關於如何根據配置文件進行配置上下文的,在后面的博客中介紹,這里先略過。
第二件事是對ContextSelectorStaticBinder類進行初始化。

 

我們再來大致看一下ContextSelectorStaticBinder的代碼:

/**
     * 餓漢單例
     */
    static ContextSelectorStaticBinder singleton = new ContextSelectorStaticBinder();

    /**
     * 上下文選擇器
     */
    ContextSelector contextSelector;
    /**
     * 用來做權限控制
     */
    Object key;
/**
     * FOR INTERNAL USE. This method is intended for use by  StaticLoggerBinder.
     *
     *  內部使用,這個方法是給StaticLoggerBinder類來調用的
     *  這用權限控制的方式可以用來借鑒
     * @param defaultLoggerContext
     * @throws ClassNotFoundException
     * @throws NoSuchMethodException
     * @throws InstantiationException
     * @throws IllegalAccessException
     * @throws InvocationTargetException
     */
    public void init(LoggerContext defaultLoggerContext, Object key) throws ClassNotFoundException, NoSuchMethodException, InstantiationException,
                    IllegalAccessException, InvocationTargetException {
        /**
         * key 用來做權限控制 當key對象初始化后不能變更
         * 在StaticLoggerBinder中private static Object KEY = new Object();初始化一個靜態對象key
         * 則static 只有StaticLoggerBinder類能夠調用該init()
         */
        if (this.key == null) {
            this.key = key;
        } else if (this.key != key) {
            throw new IllegalAccessException("Only certain classes can access this method.");
        }

        String contextSelectorStr = OptionHelper.getSystemProperty(ClassicConstants.LOGBACK_CONTEXT_SELECTOR);
        if (contextSelectorStr == null) {
            contextSelector = new DefaultContextSelector(defaultLoggerContext);
        } else if (contextSelectorStr.equals("JNDI")) {
            // if jndi is specified, let's use the appropriate class
            contextSelector = new ContextJNDISelector(defaultLoggerContext);
        } else {
            contextSelector = dynamicalContextSelector(defaultLoggerContext, contextSelectorStr);
        }
    }

    /**
     * Instantiate the context selector class designated by the user. The selector
     * must have a constructor taking a LoggerContext instance as an argument.
     * 
     * @param defaultLoggerContext
     * @param contextSelectorStr
     * @return an instance of the designated context selector class
     * @throws ClassNotFoundException
     * @throws SecurityException
     * @throws NoSuchMethodException
     * @throws IllegalArgumentException
     * @throws InstantiationException
     * @throws IllegalAccessException
     * @throws InvocationTargetException
     */
    static ContextSelector dynamicalContextSelector(LoggerContext defaultLoggerContext, String contextSelectorStr) throws ClassNotFoundException,
                    SecurityException, NoSuchMethodException, IllegalArgumentException, InstantiationException, IllegalAccessException,
                    InvocationTargetException {
        Class<?> contextSelectorClass = Loader.loadClass(contextSelectorStr);
        Constructor cons = contextSelectorClass.getConstructor(new Class[] { LoggerContext.class });
        return (ContextSelector) cons.newInstance(defaultLoggerContext);
    }

    public ContextSelector getContextSelector() {
        return contextSelector;
    }

如果系統參數中配置了JNDI,這里會得到一個ContextJNDISelector,實際應用中,一般會得到一個DefaultContextSelector,並且把已經初始化完成的defaultLoggerContext傳給新創建的這個DefaultContextSelector。

 

總結一下這個大致流程:
1、StaticLoggerBinder委托ContxetInitializer去初始化上下文,這個時候會去讀取配置文件,並根據配置文件對LoggerContext進行初始化
2、然后初始化ContextSelectorStaticBinder,選擇一個合適的ContextSelector並把defaultLoggerContext傳遞給它。
3、調用getLoggerFactory()方法,若未初始化返回defaultLoggerContext,否則委托ContextSelectorStaticBinder獲取一個ContextSelector返回一個上下文。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM