项目用的是springmvc+spring+mybatis框架,

配置日志的时候非常简单,仅仅是把commons-logging、log4j,还有slf4j-log4j三个日志相关的jar包导入项目,然后在classpath中加个log4j.properties的配置文件即可。

但是你有没有想过Log4j是什么时候被加载进虚拟机的?为什么我们没有手动加载,spring和mybatis就能自动的用起来log4j,毫不见外?

spring使用的是commons-logging

mybatis用的是自己写的

看一下源码,非常简单,一个个的去试,没有这个就进入下一个,直到找到了log4j

这两个工具其实都只是日志工具的规范,可以理解成接口,而log4j才是一个实实在在的日志实现。

但是slf4j在什么地方?——虽然这两个工具没用到slf4j,我还是把它放进来,因为我引入的其他工具会用到它。。比如:

下面开始看代码代码来说明一下log4j是怎么被加载到虚拟机的:

首先我们来看spring,我们从web.xml看起,加载spring:

到这里我们看到了LogFactory,这是commons-logging的一个log工厂,这里用的是这个虚类的一个静态方法,我们继续看这个方法:

再看getFactory(代码较长,可以直接跳过先看后边的结论):

 public static LogFactory getFactory() throws LogConfigurationException {
// Identify the class loader we will be using
ClassLoader contextClassLoader = getContextClassLoaderInternal(); if (contextClassLoader == null) {
// This is an odd enough situation to report about. This
// output will be a nuisance on JDK1.1, as the system
// classloader is null in that environment.
if (isDiagnosticsEnabled()) {
logDiagnostic("Context classloader is null.");
}
} // Return any previously registered factory for this class loader
LogFactory factory = getCachedFactory(contextClassLoader);
if (factory != null) {
return factory;
} if (isDiagnosticsEnabled()) {
logDiagnostic(
"[LOOKUP] LogFactory implementation requested for the first time for context classloader " +
objectId(contextClassLoader));
logHierarchy("[LOOKUP] ", contextClassLoader);
} // Load properties file.
//
// If the properties file exists, then its contents are used as
// "attributes" on the LogFactory implementation class. One particular
// property may also control which LogFactory concrete subclass is
// used, but only if other discovery mechanisms fail..
//
// As the properties file (if it exists) will be used one way or
// another in the end we may as well look for it first. Properties props = getConfigurationFile(contextClassLoader, FACTORY_PROPERTIES); // Determine whether we will be using the thread context class loader to
// load logging classes or not by checking the loaded properties file (if any).
ClassLoader baseClassLoader = contextClassLoader;
if (props != null) {
String useTCCLStr = props.getProperty(TCCL_KEY);
if (useTCCLStr != null) {
// The Boolean.valueOf(useTCCLStr).booleanValue() formulation
// is required for Java 1.2 compatibility.
if (Boolean.valueOf(useTCCLStr).booleanValue() == false) {
// Don't use current context classloader when locating any
// LogFactory or Log classes, just use the class that loaded
// this abstract class. When this class is deployed in a shared
// classpath of a container, it means webapps cannot deploy their
// own logging implementations. It also means that it is up to the
// implementation whether to load library-specific config files
// from the TCCL or not.
baseClassLoader = thisClassLoader;
}
}
} // Determine which concrete LogFactory subclass to use.
// First, try a global system property
if (isDiagnosticsEnabled()) {
logDiagnostic("[LOOKUP] Looking for system property [" + FACTORY_PROPERTY +
"] to define the LogFactory subclass to use...");
} try {
String factoryClass = getSystemProperty(FACTORY_PROPERTY, null);
if (factoryClass != null) {
if (isDiagnosticsEnabled()) {
logDiagnostic("[LOOKUP] Creating an instance of LogFactory class '" + factoryClass +
"' as specified by system property " + FACTORY_PROPERTY);
}
factory = newFactory(factoryClass, baseClassLoader, contextClassLoader);
} else {
if (isDiagnosticsEnabled()) {
logDiagnostic("[LOOKUP] No system property [" + FACTORY_PROPERTY + "] defined.");
}
}
} catch (SecurityException e) {
if (isDiagnosticsEnabled()) {
logDiagnostic("[LOOKUP] A security exception occurred while trying to create an" +
" instance of the custom factory class" + ": [" + trim(e.getMessage()) +
"]. Trying alternative implementations...");
}
// ignore
} catch (RuntimeException e) {
// This is not consistent with the behaviour when a bad LogFactory class is
// specified in a services file.
//
// One possible exception that can occur here is a ClassCastException when
// the specified class wasn't castable to this LogFactory type.
if (isDiagnosticsEnabled()) {
logDiagnostic("[LOOKUP] An exception occurred while trying to create an" +
" instance of the custom factory class" + ": [" +
trim(e.getMessage()) +
"] as specified by a system property.");
}
throw e;
} // Second, try to find a service by using the JDK1.3 class
// discovery mechanism, which involves putting a file with the name
// of an interface class in the META-INF/services directory, where the
// contents of the file is a single line specifying a concrete class
// that implements the desired interface. if (factory == null) {
if (isDiagnosticsEnabled()) {
logDiagnostic("[LOOKUP] Looking for a resource file of name [" + SERVICE_ID +
"] to define the LogFactory subclass to use...");
}
try {
final InputStream is = getResourceAsStream(contextClassLoader, SERVICE_ID); if( is != null ) {
// This code is needed by EBCDIC and other strange systems.
// It's a fix for bugs reported in xerces
BufferedReader rd;
try {
rd = new BufferedReader(new InputStreamReader(is, "UTF-8"));
} catch (java.io.UnsupportedEncodingException e) {
rd = new BufferedReader(new InputStreamReader(is));
} String factoryClassName = rd.readLine();
rd.close(); if (factoryClassName != null && ! "".equals(factoryClassName)) {
if (isDiagnosticsEnabled()) {
logDiagnostic("[LOOKUP] Creating an instance of LogFactory class " +
factoryClassName +
" as specified by file '" + SERVICE_ID +
"' which was present in the path of the context classloader.");
}
factory = newFactory(factoryClassName, baseClassLoader, contextClassLoader );
}
} else {
// is == null
if (isDiagnosticsEnabled()) {
logDiagnostic("[LOOKUP] No resource file with name '" + SERVICE_ID + "' found.");
}
}
} catch (Exception ex) {
// note: if the specified LogFactory class wasn't compatible with LogFactory
// for some reason, a ClassCastException will be caught here, and attempts will
// continue to find a compatible class.
if (isDiagnosticsEnabled()) {
logDiagnostic(
"[LOOKUP] A security exception occurred while trying to create an" +
" instance of the custom factory class" +
": [" + trim(ex.getMessage()) +
"]. Trying alternative implementations...");
}
// ignore
}
} // Third try looking into the properties file read earlier (if found) if (factory == null) {
if (props != null) {
if (isDiagnosticsEnabled()) {
logDiagnostic(
"[LOOKUP] Looking in properties file for entry with key '" + FACTORY_PROPERTY +
"' to define the LogFactory subclass to use...");
}
String factoryClass = props.getProperty(FACTORY_PROPERTY);
if (factoryClass != null) {
if (isDiagnosticsEnabled()) {
logDiagnostic(
"[LOOKUP] Properties file specifies LogFactory subclass '" + factoryClass + "'");
}
factory = newFactory(factoryClass, baseClassLoader, contextClassLoader); // TODO: think about whether we need to handle exceptions from newFactory
} else {
if (isDiagnosticsEnabled()) {
logDiagnostic("[LOOKUP] Properties file has no entry specifying LogFactory subclass.");
}
}
} else {
if (isDiagnosticsEnabled()) {
logDiagnostic("[LOOKUP] No properties file available to determine" + " LogFactory subclass from..");
}
}
} // Fourth, try the fallback implementation class if (factory == null) {
if (isDiagnosticsEnabled()) {
logDiagnostic(
"[LOOKUP] Loading the default LogFactory implementation '" + FACTORY_DEFAULT +
"' via the same classloader that loaded this LogFactory" +
" class (ie not looking in the context classloader).");
} // Note: unlike the above code which can try to load custom LogFactory
// implementations via the TCCL, we don't try to load the default LogFactory
// implementation via the context classloader because:
// * that can cause problems (see comments in newFactory method)
// * no-one should be customising the code of the default class
// Yes, we do give up the ability for the child to ship a newer
// version of the LogFactoryImpl class and have it used dynamically
// by an old LogFactory class in the parent, but that isn't
// necessarily a good idea anyway.
factory = newFactory(FACTORY_DEFAULT, thisClassLoader, contextClassLoader);
} if (factory != null) {
/**
* Always cache using context class loader.
*/
cacheFactory(contextClassLoader, factory); if (props != null) {
Enumeration names = props.propertyNames();
while (names.hasMoreElements()) {
String name = (String) names.nextElement();
String value = props.getProperty(name);
factory.setAttribute(name, value);
}
}
} return factory;
}

以上代码会先试图从classpath中加载commons-logging.properties等几个commons-logging才有的东西,直到208行代码,

我们看到最后创建了一个LogFactoryImpl的实例,然后返回了。

回头看看我们上边的代码:

看完了getFactory我们知道最后我们得到的是LogFactoryImpl的实例,

那么接下来我们该去这个类中看看它的getInstance方法了:

非常简单,在这里创建了一个Log的实例,这也是最关键的地方,我们去看看这个方法。

经过我个人进一步的代码分析logConstructor这个东西是空的,我就不再展开了,这时我们看541行这个方法,这里才是真正的创建Log实例:

这个方法名我们能看懂,就是去发现系统中的Log实现,也就是一个找的过程,后边的782行是找找系统变量中有没有commons-logging自己的指明实现类类名的变量,结果是没有的,因为我没有配。

这个方法还没完,中间是一大段的注释,我们直接看后边的重点:

这个时候开始遍历一个字符串数组,依此去系统中找有没有这个数组中的实现类,我们看看这个数组先。

大家看到了什么?log4j,现在大家基本已经知道为什么了,后边的代码如果还有兴趣可以自己去看,其实就是一个个的试着去系统中加载这些类,加载不到处理一下ClassNotFoundException然后继续找下一个。

完毕。

最后如果大家想看看commons-logging自己的日志,可以写一个Listener放在web xml第一个位置,把下边一句代码弄上:

你们会从日志中看到多了很多内容,其中有一句是:

[LogFactoryImpl@1302539661 from org.apache.catalina.loader.WebappClassLoader@1536551745] Class 'org.apache.commons.logging.impl.Log4JLogger' was found at 'jar:file:/E:/eaipweb/wtpwebapps/EAIP/WEB-INF/lib/commons-logging-1.1.1.jar!/org/apache/commons/logging/impl/Log4JLogger.class'

这回真完毕了。

spring日志加载代码解析的更多相关文章

  1. spring资源加载结构解析

    1.spring中资源加载使用resources的原因? 在java将不同资源抽象成url,然后通过注册不同的hander来处理不同读取逻辑,一般hander使用协议的前缀来命名,如http,jar, ...

  2. Spring如何加载log4j配置文件

    今天有朋友在群里问了这个问题,于是写了这篇文章进行整理. 问题如下: 在项目中添加了log4j.properties配置文件,并没有在Spring配置文件中配置,也没有在web.xml中配置,但是代码 ...

  3. spring boot 加载web容器tomcat流程源码分析

    spring boot 加载web容器tomcat流程源码分析 我本地的springboot版本是2.5.1,后面的分析都是基于这个版本 <parent> <groupId>o ...

  4. HTML文档、javascript脚本的加载与解析

    1.onload事件 1.1 onload事件分类 a.文档加载完成事件(包括脚本.图片等资源都加载完),绑定方法:<body onload="doSomething()"& ...

  5. Tomcat源码分析——SERVER.XML文件的加载与解析

    前言 作为Java程序员,对于Tomcat的server.xml想必都不陌生.本文基于Tomcat7.0的Java源码,对server.xml文件是如何加载和解析的进行分析. 加载 server.xm ...

  6. HTML页面加载和解析流程详细介绍

    浏览器加载和渲染html的顺序 1. IE下载的顺序是从上到下,渲染的顺序也是从上到下,下载和渲染是同时进行的. 2. 在渲染到页面的某一部分时,其上面的所有部分都已经下载完成(并不是说所有相关联的元 ...

  7. html页面加载和解析流程

    HTML页面加载和解析流程 用户输入网址(假设是个html页面,并且是第一次访问),浏览器向服务器发出请求,服务器返回html文件: 浏览器开始载入html代码,发现<head>标签内有一 ...

  8. Bean Definition从加载、解析、处理、注册到BeanFactory的过程。

    为了弄清楚Bean是怎么来的,花费了大把功夫,现在要把Bean Definition的加载.解析.处理.注册到bean工厂的过程记下来.这只是bean definition 的加载.解析.处理.注册过 ...

  9. spring容器加载完毕做一件事情(利用ContextRefreshedEvent事件)

    关键字:spring容器加载完毕做一件事情(利用ContextRefreshedEvent事件) 应用场景:很多时候我们想要在某个类加载完毕时干某件事情,但是使用了spring管理对象,我们这个类引用 ...

随机推荐

  1. Beaglebone Black教程Beaglebone Black的引脚分配

    Beaglebone Black教程Beaglebone Black的引脚分配 Beaglebone Black的引脚分配 绝大多数的微型开发平台都提供了一些称为GPIO的输入输出端口.这些端口可以让 ...

  2. CSU - 1337 (搞笑版费马大定理 )

    费马大定理:当n>2时,不定方程an+bn=cn没有正整数解.比如a3+b3=c3没有正整数解.为了活跃气氛,我们不妨来个搞笑版:把方程改成a3+b3=c3,这样就有解了,比如a=4, b=9, ...

  3. 苹果Itools

    韩梦飞沙  韩亚飞  313134555@qq.com  yue31313  han_meng_fei_sha

  4. 【Floyd】文化之旅

    [NOIP2012]文化之旅 题目描述 有一位使者要游历各国,他每到一个国家,都能学到一种文化,但他不愿意学习任何一 种文化超过一次(即如果他学习了某种文化,则他就不能到达其他有这种文化的国家).不 ...

  5. 【动态规划】【滚动数组】Educational Codeforces Round 26 D. Round Subset

    给你n个数,让你任选K个,使得它们乘起来以后结尾的0最多. 将每个数的因子2和因子5的数量求出来,记作a[i]和b[i]. 答案就是max{ min{Σa[i],Σb[i]} }(a[i],b[i]是 ...

  6. python基础之单例模式

    单例模式: 什么是单例模式? 基于某种方法实例化多次得到实例是同一个 实现方法: ip = '1.1.1.1' port = 3306 # 假装来自配置文件 #方法一:定义类方法进行判断 class ...

  7. Hibernate的QBC检索方式

    Hibernate的QBC检索方式 一直习惯了Hibernate的HQL查询,一直也觉得挺方便,对于最近项目里出现的QBC(org.hibernate.Criteria接口)也是报着一种看看的心理,因 ...

  8. codevs 1959 拔河比赛--判断背包内刚好装满n/2个物品

    1959 拔河比赛  时间限制: 1 s  空间限制: 128000 KB  题目等级 : 黄金 Gold 题解  查看运行结果     题目描述 Description 一个学校举行拔河比赛,所有的 ...

  9. Educational Codeforces Round 8 C. Bear and String Distance 贪心

    C. Bear and String Distance 题目连接: http://www.codeforces.com/contest/628/problem/C Description Limak ...

  10. PHP温故知新(一)

    前言 开发PHP也有几年的时间了,记得第一次接触PHP那时候还是PHP4,现在PHP版本已经是7了,虽然本人也算是一个PHP老手了,但是总觉得有些基础知识掌握的不是很好.学PHP之初只是为了混口饭吃, ...