Spring的日志模块-spring-jcl源码解析以及Java的日志框架

1、 spring-jcl模块结构

Spring的日志模块-spring-jcl源码解析以及Java的日志框架_第1张图片
可以看到,这个模块的包名是apache的commons.logging,因为这是spring团队对common.logging 进行了重写,所以包名还保留之前的apache.commons.logging。
这个模块非常简单,对外其实就提供了一个LogFactory类,提供两个方法,得到 Log的接口的实例

public abstract class LogFactory {
	public static Log getLog(Class clazz) {
		return getLog(clazz.getName());
	}
	public static Log getLog(String name) {
		return LogAdapter.createLog(name);
	}
	 后面还有几个方法,被弃用了
}

这里可以看到这其实就一个包装,真正的实现逻辑在LogAdapter里面
下面我们看一下LogAdapter类,
(简化版本)

final class LogAdapter {
  静态代码块 
  作用:根据classpath加载类,来决定采用什么Log日志实现,对枚举类赋值。
///  这里定义了 一个枚举类:LogApi
private enum LogApi {LOG4J, SLF4J_LAL, SLF4J, JUL}
static {
		if (isPresent("org.apache.logging.log4j.spi.ExtendedLogger")) {
		/// 说明有 log4j-api jar包
			if (isPresent("org.apache.logging.slf4j.SLF4JProvider") && isPresent("org.slf4j.spi.LocationAwareLogger")) {
				// 如果有log4j-to-slf4j jar包,也有 slf4j-api 那就采用 slf4j
				// 这里总结就是 如果有log4j 也有 log4j-to-slf4j ,那就直接用slf4j日志
				logApi = LogApi.SLF4J_LAL;
			}
			else {
			///  如果不存在 log4j转 slf4j 的话,那就采用log4j来进行日志记录
			///  所以总得来说   log4j的优先级比较高
				logApi = LogApi.LOG4J;
			}
		}
		else if (isPresent("org.slf4j.spi.LocationAwareLogger")) {
		/// 这里是对应 slf4j 1.3以及以后的版本,因为在1.3以后才有了LocationAwareLogger类
			logApi = LogApi.SLF4J_LAL;
		}
		else if (isPresent("org.slf4j.Logger")) {
		/// 这里是对应 slf4j 1.3之前的版本,之前的版本没有LocationAwareLogger类
			logApi = LogApi.SLF4J;
		}
		else {
		 默认采用 jdk的 日志类 jul
			logApi = LogApi.JUL;
		}
	}
	///  提供给LogFactory类调用
	private static class Log4jAdapter {
		public static Log createLog(String name) {
			return new Log4jLog(name);
		}
	}
	 根据 枚举类型,决定采用哪种类型的Log
	 这里有 由 三个Adapter内部类  来完成创建
	public static Log createLog(String name) {
		switch (logApi) {
			case LOG4J:
				return Log4jAdapter.createLog(name);
			case SLF4J_LAL:
				return Slf4jAdapter.createLocationAwareLog(name);
			case SLF4J:
				return Slf4jAdapter.createLog(name);
			default:
				return JavaUtilAdapter.createLog(name);
		}
	}
	/  下面三个Adapter内部类,用来返回不同Log接口的实现类
	private static class Log4jAdapter{
		///  这个内部类会返回的Log接口的实现对象,底层会调用Log4j 日志实现
	}
	private static class Slf4jAdapter{
		///  这个类 两个方法  返回的Log接口的实现对象,底层会调用  Slf4j的机制,在找到真正的日志实现组建
		public static Log createLocationAwareLog(String name){
		}
		public static Log createLog(String name){
		}
	}
	private static class JavaUtilAdapter{
	  这个内部类,返回的Log接口的实现类,底层调用 jdk自带的 日志实现,即 jul(java.util.log)
	}
	/   四个Log接口的实现内部类 分布对应上面三个Adapter的返回对象 这里就不写了
	private static class Log4jLog implements Log, Serializable{}
	private static class Slf4jLog implements Log, Serializable {}
	private static class Slf4jLocationAwareLog extends Slf4jLog implements Serializable{}
	private static class JavaUtilLog implements Log, Serializable
	
	///  这个类是 为jdk的日志服务的
	private static class LocationResolvingLogRecord extends LogRecord 


}

2、static代码块逻辑(决定了底层采用什么日志类型)

关于static代码块里面的处理逻辑:
这里有一个优先级:LOG4J2 级是最高的,其次是SLF4J (1.3版本及以后)、SLF4J(1.3版本之前)、JUL
这里有一个逻辑比较饶: 如果有LOG4J2 但是还存在SLF4J和log4j-to-slf4j.jar,那么就直接采用SLF4J。 因为SLF4J和log4j-to-slf4j.jar的存在,就会使得LOG4J2最终由SLF4J实现,所以Spring就直接采用SLF4J了。
总结: spring-jcl 是对Apache Commons Logging 的改造版本,采用的设计模式是“适配器模式”,它对外提供统一的接口,然后在适配类中将对日志的操作委托给具体的日志框架。
在Spring-JCL中对外有两个统一的接口,分别是Log和LogFactory。
LogFactory返回的Log的实现是由运行时决定的,可能有SLF4J 、LOG4J2、JUL中的一种实现。

3、关于java的日志系统

简单说一下:
关于java日志系统的使用总的来说有两种:

一种:代码中直接使用固定日志框架,例如JUL、Log4J1、Log4J2、Logback
另一种:代码中使用日志接口,例如SLF4J、commons-logging

日志实现框架有:
1、JDK自带的logging日志框架 java.util.logging,简称 JUL
2、Log4J1:
3、Log4J2 :Log4j1的升级版,发生了很大的变化,不兼容。并且推出了Log4J-api的面向接口编程
4、Logback:SLF4J提的一种日志框架
日志接口框架有:
1、apache的commons-logging
2、SLF4J
日志接口是为各种loging APIs提供一个简单统一的接口,从而使得最终用户能够在部署的时候配置自己希望的loging APIs实现

本来还想写点关于java中日志的一些对比,但是后来看到了下面这篇文章,就放弃了
因为写得太好了,无法超越啊
强烈推荐大家看乒乓狂魔的这篇文章(共4章):jdk-logging、log4j、logback日志介绍及原理

你可能感兴趣的:(Spring)