Spring5 之前是 JCL(外部jar包)
Spring5(包含) 之后是 Spring-JCL(内部module,基于jcl修改)
因为之前源码下载是 5.2.19,所以主要看下 Spring-JCL。
源码
首先看下log对象
/** Logger used by this class. Available to subclasses. */
protected final Log logger = LogFactory.getLog(getClass());
/**
* Convenience method to return a named logger.
* @param name logical name of the <code>Log</code> instance to be returned
*/
public static Log getLog(String name) {
// 创建具体的log对象
return LogAdapter.createLog(name);
}
看到这边主要是LogAdapter类去创建对象,那我们详细看下这个类。
final class LogAdapter {
// log4j2 jar包中的一个类
private static final String LOG4J_SPI = "org.apache.logging.log4j.spi.ExtendedLogger";
private static final String LOG4J_SLF4J_PROVIDER = "org.apache.logging.slf4j.SLF4JProvider";
private static final String SLF4J_SPI = "org.slf4j.spi.LocationAwareLogger";
private static final String SLF4J_API = "org.slf4j.Logger";
private static final LogApi logApi;
static {
if (isPresent(LOG4J_SPI)) {
if (isPresent(LOG4J_SLF4J_PROVIDER) && isPresent(SLF4J_SPI)) {
// log4j-to-slf4j bridge -> we'll rather go with the SLF4J SPI;
// however, we still prefer Log4j over the plain SLF4J API since
// the latter does not have location awareness support.
/*
* spring团队其实更倾向使用 log4j2,但如果你非要用slf4j,那就必须满足两个条件
* 1. 必须要有 log4j2 - slf4j 的桥接器
* 2. slf4j 的依赖
*/
logApi = LogApi.SLF4J_LAL;
}
else {
// Use Log4j 2.x directly, including location awareness support
logApi = LogApi.LOG4J;
}
}
else if (isPresent(SLF4J_SPI)) {
// Full SLF4J SPI including location awareness support
logApi = LogApi.SLF4J_LAL;
}
else if (isPresent(SLF4J_API)) {
// Minimal SLF4J API without location awareness support
logApi = LogApi.SLF4J;
}
else {
// java.util.logging as default
logApi = LogApi.JUL;
}
}
private LogAdapter() {
}
/**
* Create an actual {@link Log} instance for the selected API.
* @param name the logger name
*/
public static Log createLog(String name) {
switch (logApi) {
// log4j2
case LOG4J:
return Log4jAdapter.createLog(name);
// slf4j_full
case SLF4J_LAL:
return Slf4jAdapter.createLocationAwareLog(name);
// slf4j
case SLF4J:
return Slf4jAdapter.createLog(name);
default:
// Defensively use lazy-initializing adapter class here as well since the
// java.logging module is not present by default on JDK 9. We are requiring
// its presence if neither Log4j nor SLF4J is available; however, in the
// case of Log4j or SLF4J, we are trying to prevent early initialization
// of the JavaUtilLog adapter - e.g. by a JVM in debug mode - when eagerly
// trying to parse the bytecode for all the cases of this switch clause.
return JavaUtilAdapter.createLog(name);
}
}
private static boolean isPresent(String className) {
try {
Class.forName(className, false, LogAdapter.class.getClassLoader());
return true;
}
catch (ClassNotFoundException ex) {
return false;
}
}
private enum LogApi {LOG4J, SLF4J_LAL, SLF4J, JUL}
private static class Log4jAdapter {
public static Log createLog(String name) {
return new Log4jLog(name);
}
}
private static class Slf4jAdapter {
public static Log createLocationAwareLog(String name) {
Logger logger = LoggerFactory.getLogger(name);
return (logger instanceof LocationAwareLogger ?
new Slf4jLocationAwareLog((LocationAwareLogger) logger) : new Slf4jLog<>(logger));
}
public static Log createLog(String name) {
return new Slf4jLog<>(LoggerFactory.getLogger(name));
}
}
private static class JavaUtilAdapter {
public static Log createLog(String name) {
return new JavaUtilLog(name);
}
}
@SuppressWarnings("serial")
private static class Log4jLog implements Log, Serializable {
// 先不关注
...
}
@SuppressWarnings("serial")
private static class Slf4jLog<T extends Logger> implements Log, Serializable {
// 先不关注
...
}
@SuppressWarnings("serial")
private static class Slf4jLocationAwareLog extends Slf4jLog<LocationAwareLogger> implements Serializable {
// 先不关注
...
}
@SuppressWarnings("serial")
private static class JavaUtilLog implements Log, Serializable {
// 先不关注
...
}
@SuppressWarnings("serial")
private static class LocationResolvingLogRecord extends LogRecord {
// 先不关注
...
}
}
去掉一些不是很重要的代码,先看下 createLog
方法。
public static Log createLog(String name) {
switch (logApi) {
// log4j2
case LOG4J:
return Log4jAdapter.createLog(name);
// slf4j_full
case SLF4J_LAL:
return Slf4jAdapter.createLocationAwareLog(name);
// slf4j
case SLF4J:
return Slf4jAdapter.createLog(name);
default:
// Defensively use lazy-initializing adapter class here as well since the
// java.logging module is not present by default on JDK 9. We are requiring
// its presence if neither Log4j nor SLF4J is available; however, in the
// case of Log4j or SLF4J, we are trying to prevent early initialization
// of the JavaUtilLog adapter - e.g. by a JVM in debug mode - when eagerly
// trying to parse the bytecode for all the cases of this switch clause.
return JavaUtilAdapter.createLog(name);
}
}
这里主要是根据 logApi
这个变量去判断的,那我们看下这个变量。
可以看到这个变量是在类初始化的时候去赋值的。
// log4j2 jar包中的一个类
private static final String LOG4J_SPI = "org.apache.logging.log4j.spi.ExtendedLogger";
private static final String LOG4J_SLF4J_PROVIDER = "org.apache.logging.slf4j.SLF4JProvider";
private static final String SLF4J_SPI = "org.slf4j.spi.LocationAwareLogger";
private static final String SLF4J_API = "org.slf4j.Logger";
private static final LogApi logApi;
static {
if (isPresent(LOG4J_SPI)) {
if (isPresent(LOG4J_SLF4J_PROVIDER) && isPresent(SLF4J_SPI)) {
// log4j-to-slf4j bridge -> we'll rather go with the SLF4J SPI;
// however, we still prefer Log4j over the plain SLF4J API since
// the latter does not have location awareness support.
/*
* spring团队其实更倾向使用 log4j2,但如果你非要用slf4j,那就必须满足两个条件
* 1. 必须要有 log4j2 - slf4j 的桥接器
* 2. slf4j 的依赖
*/
logApi = LogApi.SLF4J_LAL;
}
else {
// Use Log4j 2.x directly, including location awareness support
logApi = LogApi.LOG4J;
}
}
else if (isPresent(SLF4J_SPI)) {
// Full SLF4J SPI including location awareness support
logApi = LogApi.SLF4J_LAL;
}
else if (isPresent(SLF4J_API)) {
// Minimal SLF4J API without location awareness support
logApi = LogApi.SLF4J;
}
else {
// java.util.logging as default
logApi = LogApi.JUL;
}
}
可以看到主要是判断一些相关的类是否存在去实例化 logApi 对象。isPresent 方法就是通过 class.forName 去判断类是否存在。
流程图大概如下:
可以看出 spring 的开发团队还是偏向 log4j2 的,原因他也在注释里写了
the latter does not have location awareness support. slf4j 没有位置感应支持。
位置感应支持这个暂时先不深究,因为偏向于 log4j2,所以只有当你项目只要存在log4j2的依赖,就会优先考虑log4j2,除非你的项目同时存在 slf4j 和 log4j-slf4j 桥接器的依赖后,这就表明你就是想用 slf4j,这时候才会使用 slf4j。或者我本身就没有 log4j2 的依赖,这种就是 slf4j 优先,没有的话使用 JUL。