谈谈设计模式的几个原则
单一职责原则:一个类或者一个接口只负责唯一项职责,尽量设计出功能单一的接口;
依赖倒转原则:高层模块不应该依赖低层模块具体实现,解耦高层与低层。既面向接口编程,当实现发生变化时,只需提供新的实现类,不需要修改高层模块代码;
开放-封闭原则:程序对外扩展开放,对修改关闭;换句话说,当需求发生变化时,我们可以通过添加新模块来满足新需求,而不是通过修改原来的实现代码来满足新需求;
1、MyBatis没有提供日志的实现类,需要接入第三方的日志组件,但第三方日志组件都有各自的Log级别,且各不相同,MyBatis统一提供了 trace、debug、warn、error 四个级别;
日志模块采用了适配器设计模式:
这么熟悉的代码,那一定就是适配器设计模式啦。
那就需要看这个类:LogFactory
这个类加载的时候,就会执行静态代码块。看一下这个静态代码块:
那么就点进 tryImplementation这个方法,这个tryImplementation方法和我开篇讲的java8那个例子一样。
那么我们可以开始写测试类,来Debug一下Mybatis源码,看一下是如何进行调用的。
第一个断点打在这里。
第二个断点打在这里
无论如何首先应该是加载静态代码块,就别看getLog方法了。
那么就看静态代码块,到这一步怎么走?是先看外面的方法还是里面的方法?
根据文章开头的代码结果来看,肯定是先走tryImplementation,LogFactory::useSlf4JLogging是runnable.run()的时候才去执行的。
所以看一下tryImplementation这个方法:显然logConstructor是null,然后进入了if
然后继续往下运行runnable.run()的时候,跳到了这里,这个useSlf4jLogging正是tryImplementation(LogFactory::useSlf4jLogging);里面的这个
那就继续进入setImplementation方法:try内运行完毕之后,可以看到logConstructor= candidate = slf4jImpl的全限定名。
然后代码继续运行,最终就跳回到了这里
结论就是 runnable.run()之前 logConstructor是null的,但是run了之后 logConstructor就不在是null了。道理和开篇的那个小案例是一样的。
继续走第二个,一样也是进tryImplementation方法,但是此时logConstructor是有值的,等于
但是此时logConstructor是有值的,等于看下图Variables,等于上一个tryImplementation执行后的logConstructor = Slf4jImpl的类的构造器对象
所以进第二个tryImplementation方法的时候 就不走 if 了。直接跳出
看下图,直接就跳过if判断了
所以,static代码块之后的第三方日志包都不加载。而且静态代码块的性质是只加载一次,也就是说,如果项目里有这6个第三包日志包的时候加载顺序是slf4J -> commonsLoging -> log4J2 -> log4J ->JdkLog ->NoLogging,并且是加载了slf4j的话,就不加在其他的第三方日志包了,如果项目里没有slf4j的时候,那就优先加载commonsLoging,加载了commonsLoging的话,就不会加载剩下的了,如果没有commonsLoging的话,那就加载log4J2,依次类推。
有同学就会问,你的Demo类里还有代码呢,
这些断点往下走,不会进入LogFactory类的static吗,不会的,static只加载一次,技术把断点继续下去放到红框这里的话
然后把断点想放过去放到static,根本就放不过去。
所以整个日志加载顺序大致是这个样子。
日志模块有一个jdbc包
看一下BaseJdbcLogger这个抽象类,父类。
这些注释的属性,在静态代码块里初始化,我的mybatis版本比较新是采用了stream来初始化的属于java8的语法,
本文系作者在时代Java发表,未经许可,不得转载。
如有侵权,请联系nowjava@qq.com删除。