前言本文主要介绍的是spring5、spring4、mybatis日志源码中是具体如何加载具体的日志类，看完后你可以根据源码的加载顺序使用自己需要使用的日志。如有错误，欢迎指出spring当中的日志log4jlogbacklog4j2julslf4jjcl各种日志技术的关系和作用通过源码来分析spring的日志技术分别分析spring5和spring4的使用日志的区别通过源码分析mybaits的日志技术spring和mybatis整合通过注解设置自定义的日志主流的log技术名词log4j log4j log4j 1.2.17 可以不需要依赖第三方的技术，直接记录日志jcljakartaCommonsLoggingImpl commons-logging commons-logging 1.1.1 jcl他不直接记录日志,他是通过第三方记录日志(jul)，它是jdk自带的如果使用jcl来记录日志,在没有log4j的依赖情况下,是用jul如果有了log4j则使用log4jjcl=Jakarta commons-logging ,是apache公司开发的一个抽象日志通用框架,本身不实现日志记录,但是提供了记录日志的抽象方法即接口(info,debug,error.......),底层通过一个数组存放具体的日志框架的类名,然后循环数组依次去匹配这些类名是否在app中被依赖了,如果找到被依赖的则直接使用,所以他有先后顺序。上图为jcl中存放日志技术类名的数组，默认有四个，后面两个可以忽略，因为都是jdk1.3以前的。上图81行就是通过一个类名去load一个class，如果load成功则直接new出来并且返回使用。如果没有load到class这循环第二个，直到找到为止。可以看到这里的循环条件必须满足result不为空，也就是如果没有找到具体的日志依赖则继续循环，如果找到则条件不成立，不进行循环了。总结：顺序log4j>juljuljava自带的一个日志记录的技术,直接使用log4j2slf4jslf4j他也不记录日志,通过绑定器绑定一个具体的日志记录来完成日志记录绑定器、桥接器问题slf4j -->通过绑定器-->jcl -->通过桥接器--> jcl ====> 会导致内存溢出logbacksimple-log各种日志技术的关系和作用spring日志技术分析spring5.*日志技术实现spring5使用的spring的jcl(spring改了jcl的代码)来记录日志的,但是jcl不能直接记录日志,默认是使用JUL记录，但是只要引入了log4j2、slf4j等，就会被重新赋值改变（在static静态块中）源代码展示org.apache.commons.logging.LogFactory类里面被调用的最多的方法：// 这个方法，调用本类的getLog()方法 public static Log getLog(Class<?> clazz) { return getLog(clazz.getName()); } public static Log getLog(String name) { return LogAdapter.createLog(name); }org.apache.commons.logging.LogAdapter类静态变量和静态代码块会在类的实例化之前被加载，具体是在类加载过程的初始化阶段，会在编译器生成的clinit方法中被调用，执行的顺序有源文件中出现的顺序一致/** * Create an actual {@link Log} instance for the selected API. * 这个方法就是返回具体的实例 */ public static Log createLog(String name) { // 通过静态变量logApi具体的值来加载日志 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); } } // 核心代码 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; // 枚举类 private enum LogApi {LOG4J, SLF4J_LAL, SLF4J, JUL} static { // isPresent()方法，是判断这个类是否能够被加载到 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. logApi = LogApi.SLF4J_LAL; } else { // Use Log4j 2.x directly, including location awareness support // 使用Log4j 2.x的版本 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 // 默认使用JUL logApi = LogApi.JUL; } }spring4.*日志技术实现spring4当中依赖的是原生的jcl，采用循环优先的原则。mybatis的日志技术实现你提供哪个，就使用你提供的。如果没有提供，就会使用JCL，然后JCL会去找到实现类初始化静态代码块会在类的加载过程中的初始化阶段执行org.apache.ibatis.logging.LogFactory常被调用的方法public static Log getLog(Class<?> aClass) { return getLog(aClass.getName()); } public static Log getLog(String logger) { try { // 通过静态变量logConstructor来获得具体的 return logConstructor.newInstance(logger); } catch (Throwable t) { throw new LogException("Error creating logger for logger " + logger + ". Cause: " + t, t); } }// 静态变量 private static Constructor<? extends Log> logConstructor; static { // 在初始化阶段执行，一旦logConstructor不为空，后面的就不会被执行 tryImplementation(LogFactory::useSlf4jLogging); tryImplementation(LogFactory::useCommonsLogging); tryImplementation(LogFactory::useLog4J2Logging); tryImplementation(LogFactory::useLog4JLogging); tryImplementation(LogFactory::useJdkLogging); tryImplementation(LogFactory::useNoLogging); }关键代码 if (logConstructor == null),没有找到实现则继续找private static void tryImplementation(Runnable runnable) { // 当为空时，才执行方法 if (logConstructor == null) { try { runnable.run(); } catch (Throwable t) { // ignore } } } public static synchronized void useSlf4jLogging() { setImplementation(org.apache.ibatis.logging.slf4j.Slf4jImpl.class); } private static void setImplementation(Class<? extends Log> implClass) { try { Constructor<? extends Log> candidate = implClass.getConstructor(String.class); // 为什么不直接返回这里的Log对象？ // 这里主要的目的是看能不能得到，用户还需要自定义一些东西，所以不能返回这个，应该使用getLog()方法返回的对象 Log log = candidate.newInstance(LogFactory.class.getName()); if (log.isDebugEnabled()) { log.debug("Logging initialized using '" + implClass + "' adapter."); } // 赋值，只要前面没有抛异常 logConstructor = candidate; } catch (Throwable t) { throw new LogException("Error setting Log implementation. Cause: " + t, t); } } // getLog方法 public static Log getLog(String logger) { try { return logConstructor.newInstance(logger); } catch (Throwable t) { throw new LogException("Error creating logger for logger " + logger + ". Cause: " + t, t); } }具体实现类mybatis提供很多日志的实现类,用来记录日志,取决于初始化的时候load到的class上图红色箭头可以看到JakartaCommonsLoggingImpl中引用了jcl 的类,如果在初始化的时候load到类为JakartaCommonsLoggingImpl那么则使用jcl 去实现日志记录,但是也是顺序的,顺序参考源码Spring和Mybatis整合设置自定义的的日志// spring的Appconfig中 @Bean public SqlSessionFactory sqlSessionFactory() throws Exception { SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean(); // 所以可以通过这样设置自己想要使用的日志 org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration(); // 这个方法里面调用的是org.apache.ibatis.logging.LogFactory的useCustomLogging() configuration.setLogImpl(Log4jImpl.class); factoryBean.setConfiguration(configuration); factoryBean.setDataSource(dataSource()); return factoryBean.getObject(); } // useCustomLogging方法 public static synchronized void useCustomLogging(Class<? extends Log> clazz) { setImplementation(clazz); }