mybatis 架构及基础模块
1、 mybatis整体架构

基础支撑层详解
1、日志模块
mybatis日志模块没有实现类,需要接入第三方的组件,问题是第三方的组件有各自的log级别,为了能接入第三方组件,mybati日志模块定义了trace、debug、warn、error级别,然后采取适配器模式将各个日志组件转化为mybatis定义的四种log级别,以此来实现日志模块的接入。Mybatis 会自动扫描日志实现,并且定义了第三方日志组件的加载顺序,加载优先级如下:slfj4-commonLoging-log4J2-log4J-JdkLog
详解自动扫描日志实现和如何实现优先级加载。
先来看一下日志实例的获取流程:框架启动的时候会调用日志模块的LogFactory.getLog()方法来获取log实例,获取到log实例后会设置到configuration对象中。LogFactory.getLog()方法其实就是拿到一个日志实现类的构造器logConstructor,并通过构造器来实例化一个实例进行返回。重点就在这个日志实现类的构造器上,构造器的类型就来源于这个日志实现类的类型。
// getLog(Class<?> aClass)方法
public static Log getLog(Class<?> aClass) {
return getLog(aClass.getName());
}
//通过类构造器实例化实例
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);
}
}
按顺序查找日志实现类:再来看一下构造器logConstructor,在LogFactory第一次初始化的时候执行static 静态代码块,按顺序查找日志实现类,找到就将实现类的构造器赋值。
public final class LogFactory {
/**
* Marker to be used by logging implementations that support markers.
*/
public static final String MARKER = "MYBATIS";
//具体日志实现类的构造器
private static Constructor<? extends Log> logConstructor;
//按顺序加载
static {
tryImplementation(LogFactory::useSlf4jLogging);
tryImplementation(LogFactory::useCommonsLogging);
tryImplementation(LogFactory::useLog4J2Logging);
tryImplementation(LogFactory::useLog4JLogging);
tryImplementation(LogFactory::useJdkLogging);
tryImplementation(LogFactory::useNoLogging);
}
1、启动一个线程去获取构造器
//启动一个线程获取日志实现类的构造器,并设置构造器的值
private static void tryImplementation(Runnable runnable) {
if (logConstructor == null) {
try {
runnable.run();
} catch (Throwable t) {
//捕捉到异常不进行处理
// ignore
}
}
}
2、查找具体实现类动作
//加载具体的日志实现
public static synchronized void useSlf4jLogging() {
setImplementation(org.apache.ibatis.logging.slf4j.Slf4jImpl.class);
} //拿到类构造器,并给类的logConstructor变量赋值,后期getLog()方法将使用logConstructor实例一个日志类实例。
private static void setImplementation(Class<? extends Log> implClass) {
try {
Constructor<? extends Log> candidate = implClass.getConstructor(String.class);
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);
}
}
最后来看一下这个日志实现类org.apache.ibatis.logging.log4j.Log4jImpl.class,其实这个日志实现类就是采用适配器模式,将log4j的日志级别,封装成mybatis的日志级
/**
* Copyright ${license.git.copyrightYears} the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.ibatis.logging.slf4j; import org.apache.ibatis.logging.Log;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.Marker;
import org.slf4j.spi.LocationAwareLogger; /**
* @author Clinton Begin
* @author Eduardo Macarron
*/
//实现Log 接口,通过slf4j具体实现完成适配
public class Slf4jImpl implements Log { private Log log; public Slf4jImpl(String clazz) {
Logger logger = LoggerFactory.getLogger(clazz); if (logger instanceof LocationAwareLogger) {
try {
// check for slf4j >= 1.6 method signature
logger.getClass().getMethod("log", Marker.class, String.class, int.class, String.class, Object[].class, Throwable.class);
log = new Slf4jLocationAwareLoggerImpl((LocationAwareLogger) logger);
return;
} catch (SecurityException | NoSuchMethodException e) {
// fail-back to Slf4jLoggerImpl
}
} // Logger is not LocationAwareLogger or slf4j version < 1.6
log = new Slf4jLoggerImpl(logger);
} @Override
public boolean isDebugEnabled() {
return log.isDebugEnabled();
} @Override
public boolean isTraceEnabled() {
return log.isTraceEnabled();
} @Override
public void error(String s, Throwable e) {
log.error(s, e);
} @Override
public void error(String s) {
log.error(s);
} @Override
public void debug(String s) {
log.debug(s);
} @Override
public void trace(String s) {
log.trace(s);
} @Override
public void warn(String s) {
log.warn(s);
} }
适配器模式详解
适配器模式(Adapter Pattern)是作为两个不兼容的接口之间的桥梁,将一个类的接口转换成客户希望的 另外一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。

这里引用一个百度百科的图片
角色
Target(目标抽象类):目标抽象类定义客户所需接口,可以是一个抽象类或接口,也可以是具体类。比如:Mybatis 中的Log接口
/**
* Copyright ${license.git.copyrightYears} the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.ibatis.logging; /**
* @author Clinton Begin
*/
public interface Log { boolean isDebugEnabled(); boolean isTraceEnabled(); void error(String s, Throwable e); void error(String s); void debug(String s); void trace(String s); void warn(String s); }
Adapter(适配器类):适配器可以调用另一个接口,作为一个转换器,对Adaptee和Target进行适配,适配器类是适配器模式的核心,在对象适配器中,它通过继承Target并关联一个Adaptee对象使二者产生联系。比如Mybatis中的日志模块的实现类org.apache.ibatis.logging.log4j.Log4jImpl.class
/**
* Copyright ${license.git.copyrightYears} the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.ibatis.logging.log4j; import org.apache.ibatis.logging.Log;
import org.apache.log4j.Level;
import org.apache.log4j.Logger; /**
* @author Eduardo Macarron
*/
public class Log4jImpl implements Log { private static final String FQCN = Log4jImpl.class.getName(); private final Logger log; public Log4jImpl(String clazz) {
log = Logger.getLogger(clazz);
} @Override
public boolean isDebugEnabled() {
return log.isDebugEnabled();
} @Override
public boolean isTraceEnabled() {
return log.isTraceEnabled();
} @Override
public void error(String s, Throwable e) {
log.log(FQCN, Level.ERROR, s, e);
} @Override
public void error(String s) {
log.log(FQCN, Level.ERROR, s, null);
} @Override
public void debug(String s) {
log.log(FQCN, Level.DEBUG, s, null);
} @Override
public void trace(String s) {
log.log(FQCN, Level.TRACE, s, null);
} @Override
public void warn(String s) {
log.log(FQCN, Level.WARN, s, null);
} }
Adaptee(适配者类):适配者即被适配的角色,它定义了一个已经存在的接口,这个接口需要适配,适配者类一般是一个具体类,包含了客户希望使用的业务方法,在某些情况下可能没有适配者类的源代码。比如需要被使用的具体日志模块(log4j、log4j2等)
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/ package org.apache.log4j; import org.apache.log4j.spi.LoggerFactory; /**
This is the central class in the log4j package. Most logging
operations, except configuration, are done through this class. @since log4j 1.2 @author Ceki Gülcü */
public class Logger extends Category { /**
The fully qualified name of the Logger class. See also the
getFQCN method. */
private static final String FQCN = Logger.class.getName(); protected
Logger(String name) {
super(name);
} /**
Log a message object with the {@link Level#FINE FINE} level which
is just an alias for the {@link Level#DEBUG DEBUG} level. <p>This method first checks if this category is <code>DEBUG</code>
enabled by comparing the level of this category with the {@link
Level#DEBUG DEBUG} level. If this category is
<code>DEBUG</code> enabled, then it converts the message object
(passed as parameter) to a string by invoking the appropriate
{@link org.apache.log4j.or.ObjectRenderer}. It then proceeds to call all the
registered appenders in this category and also higher in the
hierarchy depending on the value of the additivity flag. <p><b>WARNING</b> Note that passing a {@link Throwable} to this
method will print the name of the <code>Throwable</code> but no
stack trace. To print a stack trace use the {@link #debug(Object,
Throwable)} form instead. @param message the message object to log. */
//public
//void fine(Object message) {
// if(repository.isDisabled(Level.DEBUG_INT))
// return;
// if(Level.DEBUG.isGreaterOrEqual(this.getChainedLevel())) {
// forcedLog(FQCN, Level.DEBUG, message, null);
// }
//} /**
Log a message object with the <code>FINE</code> level including
the stack trace of the {@link Throwable} <code>t</code> passed as
parameter. <p>See {@link #fine(Object)} form for more detailed information. @param message the message object to log.
@param t the exception to log, including its stack trace. */
//public
//void fine(Object message, Throwable t) {
// if(repository.isDisabled(Level.DEBUG_INT))
// return;
// if(Level.DEBUG.isGreaterOrEqual(this.getChainedLevel()))
// forcedLog(FQCN, Level.FINE, message, t);
//} /**
* Retrieve a logger named according to the value of the
* <code>name</code> parameter. If the named logger already exists,
* then the existing instance will be returned. Otherwise, a new
* instance is created.
*
* <p>By default, loggers do not have a set level but inherit it
* from their neareast ancestor with a set level. This is one of the
* central features of log4j.
*
* @param name The name of the logger to retrieve.
*/
static
public
Logger getLogger(String name) {
return LogManager.getLogger(name);
} /**
* Shorthand for <code>getLogger(clazz.getName())</code>.
*
* @param clazz The name of <code>clazz</code> will be used as the
* name of the logger to retrieve. See {@link #getLogger(String)}
* for more detailed information.
*/
static
public
Logger getLogger(Class clazz) {
return LogManager.getLogger(clazz.getName());
} /**
* Return the root logger for the current logger repository.
* <p>
* The {@link #getName Logger.getName()} method for the root logger always returns
* string value: "root". However, calling
* <code>Logger.getLogger("root")</code> does not retrieve the root
* logger but a logger just under root named "root".
* <p>
* In other words, calling this method is the only way to retrieve the
* root logger.
*/
public
static
Logger getRootLogger() {
return LogManager.getRootLogger();
} /**
Like {@link #getLogger(String)} except that the type of logger
instantiated depends on the type returned by the {@link
LoggerFactory#makeNewLoggerInstance} method of the
<code>factory</code> parameter. <p>This method is intended to be used by sub-classes. @param name The name of the logger to retrieve. @param factory A {@link LoggerFactory} implementation that will
actually create a new Instance. @since 0.8.5 */
public
static
Logger getLogger(String name, LoggerFactory factory) {
return LogManager.getLogger(name, factory);
} /**
* Log a message object with the {@link org.apache.log4j.Level#TRACE TRACE} level.
*
* @param message the message object to log.
* @see #debug(Object) for an explanation of the logic applied.
* @since 1.2.12
*/
public void trace(Object message) {
if (repository.isDisabled(Level.TRACE_INT)) {
return;
} if (Level.TRACE.isGreaterOrEqual(this.getEffectiveLevel())) {
forcedLog(FQCN, Level.TRACE, message, null);
}
} /**
* Log a message object with the <code>TRACE</code> level including the
* stack trace of the {@link Throwable}<code>t</code> passed as parameter.
*
* <p>
* See {@link #debug(Object)} form for more detailed information.
* </p>
*
* @param message the message object to log.
* @param t the exception to log, including its stack trace.
* @since 1.2.12
*/
public void trace(Object message, Throwable t) {
if (repository.isDisabled(Level.TRACE_INT)) {
return;
} if (Level.TRACE.isGreaterOrEqual(this.getEffectiveLevel())) {
forcedLog(FQCN, Level.TRACE, message, t);
}
} /**
* Check whether this category is enabled for the TRACE Level.
* @since 1.2.12
*
* @return boolean - <code>true</code> if this category is enabled for level
* TRACE, <code>false</code> otherwise.
*/
public boolean isTraceEnabled() {
if (repository.isDisabled(Level.TRACE_INT)) {
return false;
} return Level.TRACE.isGreaterOrEqual(this.getEffectiveLevel());
} }
适用场景:当调用双方都不太容易修改的时候,为了复用现有组件可以使用适配器模式;在系统中接入第三方组 件的时候经常被使用到。
类图

总结:
mybatis自身没有提供实现日志实现类,而是利用了适配器模式,适配第三方日志组件,可以通过LogFactory.getLog(Class<?> aClass))获得mybaits 日志实例,并设置到configuration对象中,方便在各个需要的地方进行使用。
mybatis 架构及基础模块的更多相关文章
- SpringMVC,Spring,Hibernate,Mybatis架构开发搭建之SpringMVC部分
SpringMVC,Spring,Hibernate,Mybatis架构开发搭建之SpringMVC部分 辞职待业青年就是有很多时间来写博客,以前在传统行业技术强度相对不大,不处理大数据,也不弄高并发 ...
- Mybatis架构学习
Mybatis架构学习 MyBatis 是支持定制化 SQL.存储过程以及高级映射的持久层框架.MyBatis 封装了几乎所有的 JDBC 代码和手动设置参数以及获取结果集.可以对配置和原生Map使用 ...
- ASP.NET MVC +EasyUI 权限设计(三)基础模块
请注明转载地址:http://www.cnblogs.com/arhat 在上一章中呢,我们基本上搭建好了环境,那么本章我们就从基础模块开始写起.由于用户,角色,动作三个当中,都是依赖与动作的,所以本 ...
- Spring+SpringMVC+MyBatis+easyUI整合基础篇(六)maven整合SSM
写在前面的话 承接前文<Spring+SpringMVC+MyBatis+easyUI整合基础篇(五)讲一下maven>,本篇所讲述的是如何使用maven与原ssm项目整合,使得一个普 ...
- Mybatis架构简介
一.Mybatis与ORM 对象关系映射(即Object Relational Mapping,简称ORM),主要用于关系型数据库和实体之间的映射,主要为了解决对象与关系数据库存在的互不匹配的现象,O ...
- MyBatis(十一):MyBatis架构流程浅析
架构分层 我们将MyBatis架构分为三层,分别为接口层.数据处理层和框架支撑层 接口层:提供外部接口调用的API,使用端通过这些API来操作数据库,接口层收到请求后会调用数据处理层完成具体的数据处理 ...
- python基础——模块
python基础——模块 在计算机程序的开发过程中,随着程序代码越写越多,在一个文件里代码就会越来越长,越来越不容易维护. 为了编写可维护的代码,我们把很多函数分组,分别放到不同的文件里,这样,每个文 ...
- ansible中文手册-基础模块使用
此篇文章主要是翻译ansible官网文档而来,在里面讲述了如何使用ansible的基础模块,总体感觉比较晦涩,但是后面会写出自己相关实践的文档,从而更加通俗易懂,官网的东西拿来当手册偶尔翻翻也是很不错 ...
- node.js基础模块http、网页分析工具cherrio实现爬虫
node.js基础模块http.网页分析工具cherrio实现爬虫 一.前言 说是爬虫初探,其实并没有用到爬虫相关第三方类库,主要用了node.js基础模块http.网页分析工具cherri ...
随机推荐
- 面试题:JVM在Java堆中对对象的创建、内存结构、访问方式
一.对象创建过程 1.检查类是否已被加载 JVM遇到new指令时,首先会去检查这个指令参数能否在常量池中定位到这个类的符号引用,检查这个符号引用代表的类是否已被加载.解析.初始化,若没有,则进行类加载 ...
- SpringMVC初始化阶段流程源码分析
1.都知道SpringMVC项目启动的时候都会初始化一个类:DispatcherServlet,看这个类的源码我们可以发现他其实就是一个servlet, 为什么这么说呢?请看: DispatcherS ...
- php+html实现用户登录退出
随着渗透学习,逐渐意识到了学会开发也是非常重要的,仅仅是看懂感觉还是差了一些,所以写一写php的开发,这套程序目前并未有较完整的功能,之后会不断进行完善 登录页面.html <!DOCTYPE ...
- noSql 的应用场景简述
选型一定要结合实际情况而不是照本宣科,比如: 企业发展之初,明明一个关系型数据库就能搞定且支撑一年的架构,搞一套大而全的技术方案出来 有一些数据条件查询多,更适合使用ElasticSearch做存储降 ...
- golang开发:select多路选择
select 是 Golang 中的一个控制结构,语法上类似于switch 语句,只不过select是用于 goroutine 间通信的 ,每个 case 必须是一个通信操作,要么是发送要么是接收,s ...
- NMAP类型题目 (escapeshellarg,escapeshellcmd使用不当)
[BUUCTF 2018]Online Tool 给出了源码 审计 <?php if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) { $_SERVER[' ...
- RestTemplate get请求多参数 简单封装
使用RestTemplate发送get请求时,如果有多个参数拼接起来会比较麻烦,在此做个简单的封装 public static void main(String[] args) { Map<St ...
- 手对手的教你用canvas画一个简单的海报
啦啦啦,首先说下需求,产品想让用户在我们app内,分享一张图片到微信.qq等平台.图片中包含用户的姓名.头像.和带着自己信息的二维码.然后,如何生成这张海报呢~~~首先我们老大告诉我有一个插件叫htm ...
- spring给容器注册组件 的几种方式
环境搭建: 新建一个maven项目,引入依赖 <dependency> <groupId>org.springframework</groupId> <art ...
- Python-IndexError: list index out of range
Error:IndexError: list index out of range Where? 对Python中有序序列进行按索引取值的时候,出现这个异常 Why? 对于有序序列: 字符串 str ...