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 supportlogApi = LogApi.LOG4J;}}else if (isPresent(SLF4J_SPI)) {// Full SLF4J SPI including location awareness supportlogApi = LogApi.SLF4J_LAL;}else if (isPresent(SLF4J_API)) {// Minimal SLF4J API without location awareness supportlogApi = LogApi.SLF4J;}else {// java.util.logging as defaultlogApi = 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) {// log4j2case LOG4J:return Log4jAdapter.createLog(name);// slf4j_fullcase SLF4J_LAL:return Slf4jAdapter.createLocationAwareLog(name);// slf4jcase 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) {// log4j2case LOG4J:return Log4jAdapter.createLog(name);// slf4j_fullcase SLF4J_LAL:return Slf4jAdapter.createLocationAwareLog(name);// slf4jcase 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 supportlogApi = LogApi.LOG4J;}}else if (isPresent(SLF4J_SPI)) {// Full SLF4J SPI including location awareness supportlogApi = LogApi.SLF4J_LAL;}else if (isPresent(SLF4J_API)) {// Minimal SLF4J API without location awareness supportlogApi = LogApi.SLF4J;}else {// java.util.logging as defaultlogApi = 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。
