我们查看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方法的调用