Log4j源码阅读之二—LogManager的静态程序块

来源:互联网 发布:网络推广公司招聘 编辑:程序博客网 时间:2024/05/17 02:48
我们带着上回的疑问到源码去找答案:

我们查看LogManager这个类源码的时候发现,这个类里边有个静态程序块,源码如下:


(源码过长,此处不一一展示)


我们都知道静态程序块,在LogManager初始化的时候,就会将static的静态

程序块在JVM机上执行并且只执行了一次(第二次不再执行)。那么我们可以大胆肯定,我们之前的疑问,必定在这个类的LogManager的静态程序块里边找到答案。(上回的时序图static{}的只是根据源码阅读的流程进行标识和绘制,并不是程序运行时的正确顺序,只做阅读时对代码的理解用)既然大胆假设,那么就开始细心去校验下;我们查看下LogManager静态程序块的源码,并绘制时序图如下:



1,代码通过new RootLogger(Level.DEBUG),构建了一个RootLogger实例,为什么要构建这个实例呢?我们看下Logger这个类的继承关系:

   

通过这个继承关系,我们知道RootLogger其实就是Logger的子类,当我们调用获取RootLogger实例的时候,其实可以认为返回了一个父类Logger。同时我们设置了这个Logger的level属性为Level.DEBUG

2,我们构建了logger的实例后,并通过new Hierarchy将这个实例对象构建进入Hierarchy中,我们看看Hierarchy构造器的源码:

  public Hierarchy(final Logger rootLogger) {
    ht = new Hashtable();
    repositoryEventListeners = new ArrayList(1);
    loggerEventListeners = new ArrayList(1);
    this.root = rootLogger;
    this.objectMap = new HashMap();
    // Enable all level levels by default.
    setThreshold(Level.ALL);
    this.root.setHierarchy(this);
    rendererMap = new RendererMap();
    rendererMap.setLoggerRepository(this);
    properties = new Hashtable();
    loggerFactory = new DefaultLoggerFactory();
  }

这个构建器里边,我们多注意下几点:

 a,将root属性指向之前创建的RootLogger实例

 b,将loggerFactor指向了一个DefaultLoggerFactory实例

 c,创建了一个ht的 Hashtable()对象,这个集合有什么用?用来放置什么?什么时候将数据对象放入这个集合的?为什么要用Hashtable这个集合类呢?

 d,同时创建了几个空集合,有List和Map对象,这些集合里边要放入什么数据?什么时候放入?


3,在我们带着疑问准备往下查看LogManager的静态原码:

Hierarchy hierarchy =  new Hierarchy(new RootLogger(Level.DEBUG));

defaultLoggerRepository = hierarchy;
 hierarchy.setName(Constants.DEFAULT_REPOSITORY_NAME);
        
        // temporary repository
        repositorySelector = new DefaultRepositorySelector(defaultLoggerRepository);

这里出现了一些我们熟悉的东西,即RepositorySelector和LoggerRepository字样。我们注意如下几点

a,defaultLoggerRepository = hierarchy; 这里是将我们上述的自己new 的一个Hierarchy的对象赋值给了DefaultLoggerRepository属性,我们看这个属性的源码

    public static final LoggerRepository defaultLoggerRepository;

   还是一个静态的,final属性,而且是LoggerRepository的接口指引(这个接口在我们上一回中已经出现过)

b,repositorySelector = new DefaultRepositorySelector(defaultLoggerRepository) ,这个更加明显了,直接就将defaultLoggerRepository对象作为构造器参数,直接

    构造了一个DefaultRepositorySelector实例,并将这个实例赋值给了repositorySelector属性,我们再看看这个属性的源码:

   private static RepositorySelector repositorySelector;

   我们发现这个属性是静态的RepositorySelector接口指引(这个接口在我们上一回中也已经出现过)

这个时候,我们发现,这里出现的2个属性,在我们上一回讲解中,都已经出现。只是指向的对象有点奇怪。好,要解决心中的奇怪,我们就查看下这2个类的继承关系如下:

LoggerRepository接口继承关系:


RepositorySelector接口继承关系:


从上述继承关系中我们可以知道,Hierarchy是LoggerRepository接口的实现类,DefaultRepositorySelector是RepositorySelector接口的实现类。

源码读到这里的时候,我们可以整理一下Logger,LoggerRepository和RepositorySelector三者的关系,即Logger的子类RootLogger实例,通过LoggerRepository实现类Hierarchy构造器的参数构建到Hierarchy对象中,同时,这个对象又作为RepositorySelector实现类DefaultRepositorySelector的构造参数,构建到DefaultRepositorySelector对象中去,并通过对应的接口进行指向。这个时候,代码如果在这个地方结束,我们回到上一回的LogManager.getLogger()的源码中查看下:

    public static Logger getLogger(String name) {
        // Delegate the actual manufacturing of the logger to the logger repository.
        return repositorySelector.getLoggerRepository().getLogger(name);

    }

我们会发现这个方法中repositorySelector指向的就是我们刚构造的DefaultRepositorySelector对象,DefaultRepositorySelector对象的getLoggerRepository()返回的可能也是我们刚刚构建的Hierarchy对象,而Hierarchy对象通过getLogger(name)返回的对象,必定是Logger的对象或其子类对象。是否如此呢?

 进入Hierarchy类下,查看下getLogger(name)的源码:

    public Logger getLogger(final String loggerName,
                          final LoggerFactory factory) {
    //System.out.println("getInstance("+name+") called.");
    CategoryKey key = new CategoryKey(loggerName);




    // Synchronize to prevent write conflicts. Read conflicts (in
    // getChainedLevel method) are possible only if variable
    // assignments are non-atomic.
    Logger logger;


    synchronized (ht) {
      Object o = ht.get(key);


      if (o == null) {
        LogLog.debug(
            "Creating new logger [" + loggerName
                    + "] in repository [" + getName() + "].");
        logger = factory.makeNewLoggerInstance(loggerName);
        logger.setHierarchy(this);
        ht.put(key, logger);
        updateParents(logger);


        return logger;
      } else if (o instanceof Logger) {
          LogLog.debug(
            "Returning existing logger [" + loggerName
                    + "] in repository [" + getName() + "].");
        return (Logger) o;
      } else if (o instanceof ProvisionNode) {
        //System.out.println("("+name+") ht.get(this) returned ProvisionNode");
        logger = factory.makeNewLoggerInstance(loggerName);
        logger.setHierarchy(this);
        ht.put(key, logger);
        updateChildren((ProvisionNode) o, logger);
        updateParents(logger);


        return logger;
      } else {
        // It should be impossible to arrive here
        return null; // but let's keep the compiler happy.
      }
    }
  }

我们会发现这里解决了我上述提出的ht这个HashTable对象装载的的是什么,为什么要用HashTable,什么时候将数据装入这个集合:

1,通过new CategoryKey构建了一个key

2,对ht进行同步锁定,然后对ht调用get(key)方法,返回一个object对象

3,对返回的object对象进行判断,如果为空(通过我们之前查看源码可知道,这个时候必定是空,程序必定会进入这里,所以不为空的条件下,就不再进入深究了)

   程序会通过factory.makeNewLoggerInstance(loggerName)进行logger对象的创建。

4,我们这个时候发现factory是个接口LoggerFactory的指引,而且是由前面的Hierarchy对象通过getLogger()方法的loggerFactory属性参数传入。而且我们通过

   上述的loggerFactory属性可以知道,在new Hierarchy的时候,以及将这个属性指向了new DefaultLoggerFactory()这个实体,我们可以看看LoggerFactory接口的继承

  关系如下:

   

从上述条件可以知道,DefaultLoggerFactory是LoggerFactory接口的实现类。这个时候,我们查看下DefaultLoggerFactory的源码中查看下makeNewLoggerInstance()

方法:

  public Logger makeNewLoggerInstance(String name) {
    return new Logger(name);
  }

哈,我们从这里就可以知道这里返回的就是Logger这个类的实例。

5,我们回到Hierarchy这个类的getLogger()源码中,可以知道,第4步返回的对象,我们用我们的logger变量进行了指向。

6,同时ht.put(key, logger);将这个对象和Key放入HashTable对象中

7,最后进行updateParents()方法调用,这个方法主要就是对含有w.x.y这种格式的loggerName进行循环创建ProvisionNode对象和CategoryKey对象,并将它们放入

      ht的HashTable集合对象中。这个时候我们可以知道,ht这个集合放置的key就是CategoryKey对象,value是Object对象,这个Object对象可以是ProvisionNode也可以是

    Logger对象。

代码阅读到这里,我们感觉到已经能获取到Logger对象了  logger = Logger.getLogger("test"); 这句入口代码已经完结了。但是LogManage的静态代码还未执行完成,接下来

进入下一回 Log4j源码阅读之三—initialConfiguration方法的调用

0 0
原创粉丝点击