1、mybatis架构设计

Mybatis-架构设计之源码解析篇_java


mybatis架构三层作用是什么呢

  • Api接口层:提供API 增加、删除、修改、查询等接口,通过API接口对数据库进行操作。

  • 数据处理层:主要负责SQL的 查询、解析、执行以及结果映射的处理,主要作用解析sql根据调用请求完成一次数据库操作.

  • 架构基础层:负责通用基础服务支撑,包含事务管理、连接池管理、缓存管理等共用组件的封装,为上层提供基础服务支撑.


2、mybatis层次结构图解

mybatis每个组件作用是什么呢?


3、mybatis源码解析

sql一次执行流程如何实现的?

首先从SqlSessionFactory 与 SqlSession执行流程说起

先看看session是如何获取的,上图:



第一步:SqlSessionFactoryBuilder读取配置文件,XMLConfigBuilder.parseConfiguration真正解析xml 源码如下:




/***  构建XMLConfigBuilder 解析mybatis的配置文件*  创建DefaultSqlSessionFactory*/public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {try {    // 构建 XMLConfigBuilder, XMLConfigBuilder 解析mybatis的配置文件    XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);    // 解析XML 构建 DefaultSqlSessionFactory 对象    return build(parser.parse());} catch (Exception e) {    throw ExceptionFactory.wrapException("Error building SqlSession.", e);} finally {    ErrorContext.instance().reset();    try {        inputStream.close();    } catch (IOException e) {    }}}

第二步:build方法构建DefaultSqlSessionFactory



/** * 创建 DefaultSqlSessionFactory 对象 * @param config Configuration 对象 * @return DefaultSqlSessionFactory 对象 */public SqlSessionFactory build(Configuration config) {    return new DefaultSqlSessionFactory(config); //构建者设计模式}

第三步:当取SqlSessionFactory之后,可以通过SqlSessionFactory获取SqlSession对象。源码如下:




/*** 1、进入openSessionFromDataSource* 2、ExecutorType 为Executor的类型,TransactionIsolationLevel为事务隔离级别,autoCommit是否开启事务* 3、openSession的多个重载方法可以指定获得的SeqSession的Executor类型和事务的处理*/private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {Transaction tx = null;try {  // 获取Environment 对象  final Environment environment = configuration.getEnvironment();  // 构建Transaction 对象  final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);  tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);  // 构建 Executor 对象  final Executor executor = configuration.newExecutor(tx, execType);  // 构建 DefaultSqlSession 对象  return new DefaultSqlSession(configuration, executor, autoCommit);} catch (Exception e) {  // 如异常,则关闭 Transaction 对象  closeTransaction(tx); // may have fetched a connection so lets call close()  throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);} finally {  ErrorContext.instance().reset();}}

SqlSession已经拿到,接下来我们做什么呢,不错就是执行sql 增、删、改查了,那如何读取呢接一下来介绍一下MapperProxy

MapperProxy代理接口


代理接口调用方法案例:



// 使用JDK动态代理对mapper接口产生代理对象IUserMapper mapper = sqlSession.getMapper(IUserMapper.class);//代理对象调用接口中的任意方法,执行的都是动态代理中的invoke方法List<Object> allUser = mapper.findAllUser();

如上图可知:通过MapperProxy动态代理接口, 当执行自己写的IUserMapper里面的findAllUser方法的时候,其实是对应的mapperProxy在代理。具体看一下如何获取MapperProxy对象的


第一步:通sqlSession.getMapper方法从Configuration获取 源码如下





@Overridepublic <T> T getMapper(Class<T> type) {return configuration.getMapper(type, this);}

第二步:MapperRegistry代理源码如下:





@SuppressWarnings("unchecked")public <T> T getMapper(Class<T> type, SqlSession sqlSession) {// 获得 MapperProxyFactory 对象final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);// 不存在,则抛出 BindingException 异常if (mapperProxyFactory == null) {    throw new BindingException("Type " + type + " is not known to the MapperRegistry.");}/// 通过动态代理工厂生成实例。try {    return mapperProxyFactory.newInstance(sqlSession);} catch (Exception e) {    throw new BindingException("Error getting mapper instance. Cause: " + e, e);}}

第三步:MapperProxyFactory源码如下:



@SuppressWarnings("unchecked")protected T newInstance(MapperProxy<T> mapperProxy) {return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[]{mapperInterface}, mapperProxy);}//MapperProxyFactory类中的newInstance方法public T newInstance(SqlSession sqlSession) {// 创建了JDK动态代理的invocationHandler接口的实现类mapperProxyfinal MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);// 调用了重载方法return newInstance(mapperProxy);}

通过以上的动态代理,就可以方便地使用IUserMapper 接口啦:


IUserMapper mapper = sqlSession.getMapper(IUserMapper.class);

最终看一下sql具体是怎么执行的吧 上图:

上图已基本说明sql执行流程了,接下来源码演示

第一步:MapperProxy是如何实现的呢?源码展示:



@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {try {// 如果是 Object 定义的方法,直接调用if (Object.class.equals(method.getDeclaringClass())) {    return method.invoke(this, args);
} else if (isDefaultMethod(method)) {    return invokeDefaultMethod(proxy, method, args);}} catch (Throwable t) {throw ExceptionUtil.unwrapThrowable(t);}// 获得 MapperMethod 对象final MapperMethod mapperMethod = cachedMapperMethod(method);// 重点在这:MapperMethod最终调用了执行的方法return mapperMethod.execute(sqlSession, args);}

第二步:MapperMethod.execute是如何实现的呢?以查询为例子源码展示:


public Object execute(SqlSession sqlSession, Object[] args) {Object result;//判断mapper中的方法类型,最终调用的还是SqlSession中的方法    switch (command.getType()) {    //...由于篇幅 省略  case INSERT  case UPDATE case DELETE:代码    //重点看查询为例子展示:case SELECT:  if (method.returnsMany()) {      result = executeForMany(sqlSession, args);  // 执行查询,返回 Map  }              else {      // 转换参数      Object param = method.convertArgsToSqlCommandParam(args);      // 查询单条      result = sqlSession.selectOne(command.getName(), param);      if (method.returnsOptional() &&              (result == null || !method.getReturnType().equals(result.getClass()))) {          result = Optional.ofNullable(result);      }  }            break;default:  throw new BindingException("Unknown execution method for: " + command.getName());    }// 返回结果return result;}

第三步:executeForMany 参数封装,源码展示:



private <E> Object executeForMany(SqlSession sqlSession, Object[] args) {List<E> result;// 转换参数Object param = method.convertArgsToSqlCommandParam(args);// 执行 SELECT 操作if (method.hasRowBounds()) {    RowBounds rowBounds = method.extractRowBounds(args);    result = sqlSession.selectList(command.getName(), param, rowBounds);} else {    result = sqlSession.selectList(command.getName(), param);}// issue #510 Collections & arrays support// 封装 Array 或 Collection 结果if (!method.getReturnType().isAssignableFrom(result.getClass())) {    if (method.getReturnType().isArray()) { // 情况一,Array        return convertToArray(result);    } else {        return convertToDeclaredCollection(sqlSession.getConfiguration(), result); // 情况二,Collection    }}// 直接返回的结果return result; // 情况三,默认}

第四步:执行sql返回结果集:


@Overridepublic <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {//拼装sql  String sql = boundSql.getSql();  // 执行查询  statement.execute(sql);  // 处理返回结果  return resultSetHandler.handleResultSets(statement);}

到此Mybatis源码分析完毕~


1、mybatis框架中应用哪些模式呢

mybatis架构应用到模式汇总如下:

Mybatis-架构设计之源码解析篇_java_02

今天分享就到这,感谢大家能读完此文章,希望对你有所帮助~