源码阅读-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返回一个上下文。

 

posted @ 2020-03-31 15:00  张天赐的博客  阅读(1930)  评论(0编辑  收藏  举报