MyBatis架构
MyBatis依赖的jar不多,而且代码行数也没多少,其中使用了大量的设计模式,值得好好学习。下图是MyBatis的一张架构图,来自Java框架篇—Mybatis 入门。
Mybatis的功能架构分为三层:
- API接口层:提供给外部使用的接口API,开发人员通过这些本地API来操纵数据库。接口层一接收到调用请求就会调用数据处理层来完成具体的数据处理。
- 数据处理层:负责具体的SQL查找、SQL解析、SQL执行和执行结果映射处理等。它主要的目的是根据调用的请求完成一次数据库操作。
- 基础支撑层:负责最基础的功能支撑,包括连接管理、事务管理、配置加载和缓存处理,这些都是共用的东西,将他们抽取出来作为最基础的组件。为上层的数据处理层提供最基础的支撑。
MyBatis整个项目的包结构如下:
.└── org └── apache └── ibatis ├── annotations ├── binding ├── builder ├── cache ├── cursor ├── datasource ├── exceptions ├── executor ├── io ├── jdbc ├── logging ├── mapping ├── parsing ├── plugin ├── reflection ├── scripting ├── session ├── transaction └── type
1.兵马未动,日志先行org.apache.ibatis.loggingorg.apache.ibatis.logging.commonsorg.apache.ibatis.logging.jdbcorg.apache.ibatis.logging.jdk14org.apache.ibatis.logging.log4jorg.apache.ibatis.logging.log4j2org.apache.ibatis.logging.nologgingorg.apache.ibatis.logging.slf4jorg.apache.ibatis.logging.stdout2.异常org.apache.ibatis.exceptions3.缓存org.apache.ibatis.cacheorg.apache.ibatis.cache.decoratorsorg.apache.ibatis.cache.impl4.解析org.apache.ibatis.parsingxml解析,${} 格式的字符串解析源码分析可以参考http://www.cnblogs.com/sunzhenchao/p/3161093.html5.类型处理器org.apache.ibatis.type实现java和jdbc中的类型之间转换源码分析可以参考http://www.cnblogs.com/sunzhenchao/archive/2013/04/09/3009431.html6.IOorg.apache.ibatis.io通过类加载器在jar包中寻找一个package下满足条件(比如某个接口的子类)的所有类7.反射org.apache.ibatis.reflectionorg.apache.ibatis.reflection.factoryorg.apache.ibatis.reflection.invokerorg.apache.ibatis.reflection.propertyorg.apache.ibatis.reflection.wrapper可以参考MetaObjectTest来跟踪调试,基本上用到了reflection包下所有的类8.数据源org.apache.ibatis.datasourceorg.apache.ibatis.datasource.jndiorg.apache.ibatis.datasource.pooledorg.apache.ibatis.datasource.unpooled9.事务org.apache.ibatis.transactionorg.apache.ibatis.transaction.jdbcorg.apache.ibatis.transaction.managed10.会话org.apache.ibatis.sessionorg.apache.ibatis.session.defaults11.jdbc单元测试工具org.apache.ibatis.jdbc12.构建org.apache.ibatis.builderorg.apache.ibatis.builder.annotationorg.apache.ibatis.builder.xml13.映射org.apache.ibatis.mapping14.脚本org.apache.ibatis.scriptingorg.apache.ibatis.scripting.defaultsorg.apache.ibatis.scripting.xmltags15.注解org.apache.ibatis.annotations16.绑定org.apache.ibatis.binding17.执行器org.apache.ibatis.executororg.apache.ibatis.executor.keygenorg.apache.ibatis.executor.loaderorg.apache.ibatis.executor.loader.cgliborg.apache.ibatis.executor.loader.javassistorg.apache.ibatis.executor.parameterorg.apache.ibatis.executor.resultorg.apache.ibatis.executor.resultsetorg.apache.ibatis.executor.statement18.插件org.apache.ibatis.plugin
注释①
mybatis通过SqlSessionFactoryBuilder作为入口,通过传入配置文件,使用了BaseBuilder实现类进行配置文件解析,具体实现类是XMLConfigBuilder,在这里mybatis对配置的项进行了全面解析,只不过不是所有的解析都放在了XMLConfigBuilder,XMLConfigBuilder解析了二级节点,并作为一个总入口,还有另外几个类继承了BaseBuilder,用于解析不同的配置。而解析到的配置项信息,基本都保存在了Configuration这个类,可以看到多处地方依赖到它。
之后通过配置获取到SqlSessionFactory,可以看到SqlSessionFactoryBuilder提供了一个build方法,就是返回SqlSessionFactory的。
注释②
而SqlSessionFactory只是一个接口,默认实现有DeaultSqlSessionFactory。通过SqlSessionFactory.openSession的几个多态方法,可以获取到SqlSession。
注释③
而SqlSession也只是一个接口,怎么理解SqlSession呢,其实Session即是一次会话,假如我们把某次请求与数据库连接Connection的交互,看成是一次会话,那就可以理解SqlSession了。SqlSession是请求与数据库的一次交互,交互完成之后,Session则结束,但Session结束不代表数据库连接也关闭,只是这次会话终止了,但是数据库连接依旧是返回给连接池。
这里的SqlSession即是对一次交互信息的封装,请求可以通过SqlSession的方法(如图:CRUD )进行操作,而数据库连接,依旧由SqlSessionFactory注入。
注释②
如图我们可以看到SqlSessionFactory获取到TransactionFactory,一个事务的工厂类接口,而通过这个工厂具体实现类可以产生多个事务Transaction(具体参考ManagedTransactionFactory),Transaction抽象了一个方法getConnection,通过这个方法我们可以获取到不同事务级别的connection。mybatis的代码是这样的:
ManagedTransaction.java
protected void openConnection() throws SQLException {
if (log.isDebugEnabled()) {
log.debug(“Opening JDBC Connection”);
}
this.connection = this.dataSource.getConnection();
if (this.level != null) {
this.connection.setTransactionIsolation(this.level.getLevel());
}
}
注释④
这个时候可能大家觉得万事俱备了,SqlSession都定义好了CRUD 方法了,那它也会帮忙将语句扔给Connection操作,其实不然。这里mybatis还抽象了接口Execute。
Execute才是真正将sql与Connection打交道的地方。
图中我们可以看到Execute有四个实现类,通过名称我们不难发现它们各自的用途,例如CachingExecutor.java,mybatis默认是有缓存的,就是通过这个类实现的。
还有一个点,每个Execute实现类中,都可以发现有这样一个定义和构造方法
private Executor delegate;
public CachingExecutor(Executor delegate) {
this.delegate = delegate;
delegate.setExecutorWrapper(this);
}
所以这里我们也可以看到,其实Executor有个链结构,一步一步往下执行,所以在真正执行查询时,外面是嵌套了一层CachingExecutor,证据在这里。
- public Executor newExecutor(Transaction transaction, ExecutorType executorType, boolean autoCommit) {
- executorType = executorType == null ? defaultExecutorType : executorType;
- executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
- Executor executor;
- if (ExecutorType.BATCH == executorType) {
- executor = new BatchExecutor(this, transaction);
- } else if (ExecutorType.REUSE == executorType) {
- executor = new ReuseExecutor(this, transaction);
- } else {
- executor = new SimpleExecutor(this, transaction);
- }
- if (cacheEnabled) {
- executor = new CachingExecutor(executor, autoCommit);
- }
- executor = (Executor) interceptorChain.pluginAll(executor);
- return executor;
- }
在Execute中,还有更深层的执行逻辑,这个后面文章继续分析。
转载请注明出处:http://sukerz.scse.cn/index.php/2016/02/01/mybatis-framework/