栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

mybatis源码解析(mybatis源码分析pdf百度云)

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

mybatis源码解析(mybatis源码分析pdf百度云)

目录

前言

一、Mybatis介绍

二、Mybatis配置文件

三、SqlSessionFactory源码解析

四、SqlSession源码解析

五、Mapper源码解析

总结


前言

         mybatis中文官网地址:https://mybatis.org/mybatis-3/zh/index.html


一、Mybatis介绍

        MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。 

        后面所有的代码都是Mybatis的源码!!!!!java代码!!!!!


二、Mybatis配置文件

       截图自中文官网:

         配置文件样例:





    

    
    
    
    
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
    
    
    
        
        
        
        
        
        
    
    
    
        
    
    
    
        
    
    
    
        
            
        
    
    
    
        
            
            
                
            
            
                
                
                
                
            
        
    
    
    
        
        
        
    
    
    
    
        
        
        
    
    


三、SqlSessionFactory源码解析

        根据工具类创建SqlSessionFactory对象

InputStream inputStream = Resources.getResourceAsStream("mybatis.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder()
.build(inputStream);

        进入build方法

// 当前类:SqlSessionFactoryBuilder
public SqlSessionFactory build(InputStream inputStream) {
    return build(inputStream, null, null);
}

// 当前类:SqlSessionFactoryBuilder
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
    try {
      // 把传入的Mybatis的配置文件路径,包装成一个XMLConfigBuilder对象,解析xml 
      XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
      // build方法返回一个DefaultSqlSessionFactory对象
      // parse方法用来解析xml配置文件,返回Configuration对象
      return build(parser.parse());
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error building SqlSession.", e);
    } finally {
      ErrorContext.instance().reset();
      try {
        inputStream.close();
      } catch (IOException e) {
        // Intentionally ignore. Prefer previous error.
      }
    }
}

// 当前类:SqlSessionFactoryBuilder
public SqlSessionFactory build(Configuration config) {
    return new DefaultSqlSessionFactory(config);
}

        进入XPathParser的parse方法

// 当前类:XPathParser
public Configuration parse() {
 // 默认时false
 if (parsed) {
  // 全局配置文件不可以重复解析
  throw new BuilderException("Each XMLConfigBuilder can only be used once.");
 }
 // 改为true
 parsed = true;
 // 解析入口,parser.evalNode("/configuration")返回XNode对象,xml文件解析成的java bean
 parseConfiguration(parser.evalNode("/configuration"));
 // 返回全局配置文件
 return configuration;
}

        进入parseConfiguration方法

// 当前类:XMLConfigBuilder
private void parseConfiguration(XNode root) {
  try {
    //解析全局配置文件的标签下的标签
    //保存在Configuration的variables属性和XPathParser的variables属性里
    propertiesElement(root.evalNode("properties"));
    //解析全局配置文件的标签下的标签到Properties对象里面
    Properties settings = settingsAsProperties(root.evalNode("settings"));
    //解析全局配置文件的标签下的标签,从本地和FTP加载文件 
    loadCustomVfs(settings);
    //解析全局配置文件的标签下的标签,LOG4J,LOG4J2,SLF4J
    loadCustomLogImpl(settings);
    //解析全局配置文件的标签下的标签,类别名
    typeAliasesElement(root.evalNode("typeAliases"));
    //解析全局配置文件的标签下的标签,所有的插件类
    pluginElement(root.evalNode("plugins"));
    //解析全局配置文件的标签下的标签,对象工厂
    objectFactoryElement(root.evalNode("objectFactory"));
    //解析全局配置文件的标签下的标签,对象装饰类
    objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
    //解析全局配置文件的标签下的标签,辅助性工厂类
    reflectorFactoryElement(root.evalNode("reflectorFactory"));
    ///给全局配置类configuration设置属性,没有则用默认值
    settingsElement(settings);
    //解析全局配置文件的标签下的标签,多个数据源
    environmentsElement(root.evalNode("environments"));
    //解析全局配置文件的标签下的标签,数据源标识
    databaseIdProviderElement(root.evalNode("databaseIdProvider"));
    //解析全局配置文件的标签下的标签,jdbcType和javaType映射类
    typeHandlerElement(root.evalNode("typeHandlers"));
    //解析全局配置文件的标签下的标签,sql所在文件,
    //支持xml文件路径(resource,url,文件下的sql标签如),
    //class文件(package,class,这种格式的去解析方法上的Mybatis注解如:@Selcet),
    //最后将他们解析为MappedStatement放在全局配置类configuration的mappedStatements里面
    mapperElement(root.evalNode("mappers"));
  } catch (Exception e) {
    throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
  }
}

         进入mapperElement(root.evalNode("mappers"))方法,讲解所有sql标签生成MappedStatement的过程

// 当前类:XMLConfigBuilder,parent为mapper标签
private void mapperElement(XNode parent) throws Exception {
  if (parent != null) {
    // 映射器支持四种方式,
    //  
    // 
    // 最常用的就是xml,第一种
    for (XNode child : parent.getChildren()) {
      if ("package".equals(child.getName())) {
        String mapperPackage = child.getStringAttribute("name");
        configuration.addMappers(mapperPackage);
      } else {
        String resource = child.getStringAttribute("resource");
        String url = child.getStringAttribute("url");
        String mapperClass = child.getStringAttribute("class");
        if (resource != null && url == null && mapperClass == null) {
          ErrorContext.instance().resource(resource);
          // 把xml文件包装为流
          InputStream inputStream = Resources.getResourceAsStream(resource);
          // 用解析工具类进行解析,resource="org/mybatis/builder/AuthorMapper.xml"
          XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
          // 解析入口
          mapperParser.parse();
        } else if (resource == null && url != null && mapperClass == null) {
          ErrorContext.instance().resource(url);
          InputStream inputStream = Resources.getUrlAsStream(url);
          XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
          mapperParser.parse();
        } else if (resource == null && url == null && mapperClass != null) {
          Class mapperInterface = Resources.classForName(mapperClass);
          configuration.addMapper(mapperInterface);
        } else {
          throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
        }
      }
    }
  }
}

        进入XMLMapperBuilder的parse方法

// 当前类:XMLMapperBuilder
public void parse() {
  if (!configuration.isResourceLoaded(resource)) {
    // 解析mapper的xml文件的mapper标签
    configurationElement(parser.evalNode("/mapper"));
    // 保存resource到Configuration对象loadedResources属性里面,    
    // resource="org/mybatis/builder/AuthorMapper.xml"
    configuration.addLoadedResource(resource);
    // 把所有的namespace属性值保存到Configuration对象mapperRegistry属性里面
    // 
    bindMapperForNamespace();
  }
  // 这几个用于解析配置文件发生异常之后,再次处理,后面会看到
  parsePendingResultMaps();
  parsePendingCacheRefs();
  parsePendingStatements();
}

        进入bindMapperForNamespace方法,这个方法要注意,很重要

// 当前类:org.apache.ibatis.builder.xml.XMLMapperBuilder
private void bindMapperForNamespace() {
  String namespace = builderAssistant.getCurrentNamespace();
  if (namespace != null) {
    Class boundType = null;
    try {
      boundType = Resources.classForName(namespace);
    } catch (ClassNotFoundException e) {
      //ignore, bound type is not required
    }
    if (boundType != null) {
      if (!configuration.hasMapper(boundType)) {
        // Spring may not know the real resource name so we set a flag
        // to prevent loading again this resource from the mapper interface
        // look at MapperAnnotationBuilder#loadXmlResource
        configuration.addLoadedResource("namespace:" + namespace);
        // 这里要注意,在生成Mapper对象时要用到
        configuration.addMapper(boundType);
      }
    }
  }
}

// 当前类:org.apache.ibatis.session.Configuration
public  void addMapper(Class type) {
    // 进入这里
    mapperRegistry.addMapper(type);
}

// 当前类:org.apache.ibatis.binding.MapperRegistry
public  void addMapper(Class type) {
  if (type.isInterface()) {
    if (hasMapper(type)) {
      throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
    }
    boolean loadCompleted = false;
    try {
      // 在mybaits中,namespace属性值就是接口的全路径,所有在这里保存knownMappers集合中
      // 以接口class对象为key,MapperProxyFactory对象为value保存在knownMappers集合中,
      // 一个class对应一个MapperProxyFactory对象
      knownMappers.put(type, new MapperProxyFactory<>(type));
    
      MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
      parser.parse();
      loadCompleted = true;
    } finally {
      if (!loadCompleted) {
        knownMappers.remove(type);
      }
    }
  }
}

        进入configurationElement方法

// 当前类:XMLMapperBuilder
private void configurationElement(XNode context) {
  try {
    //得到namespace属性值,假如为:test.Mapper
    String namespace = context.getStringAttribute("namespace");
    if (namespace == null || namespace.equals("")) {
      throw new BuilderException("Mapper's namespace cannot be empty");
    }
    //设置namespace属性,以后用namespace+id来保存MappedStatement对象
    builderAssistant.setCurrentNamespace(namespace);
    //解析mapper标签下的标签,引入其他命名空间二级缓存
    cacheRefElement(context.evalNode("cache-ref"));
    //解析mapper标签下的标签,用来设置二级缓存,没有该标签不使用二级缓存
    cacheElement(context.evalNode("cache"));
    //解析mapper标签下的标签,不常用
    parameterMapElement(context.evalNodes("/mapper/parameterMap"));
    //解析mapper标签下的标签
    resultMapElements(context.evalNodes("/mapper/resultMap"));
    //解析mapper标签下的标签,重复sql提取
    sqlElement(context.evalNodes("/mapper/sql"));
    //解析mapper标签下的增删改查标签,讲解这个
    buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
  } catch (Exception e) {
    throw new BuilderException("Error parsing Mapper XML. The XML location is '" + resource + "'. Cause: " + e, e);
  }
}

        进入buildStatementFromContext方法

// 当前类:org.apache.ibatis.builder.xml.XMLMapperBuilder
private void buildStatementFromContext(List list) {
  // 获取设置的数据库标识
  if (configuration.getDatabaseId() != null) {
    buildStatementFromContext(list, configuration.getDatabaseId())
  }
  // 没有设置为null
  buildStatementFromContext(list, null);
}

        进入buildStatementFromContext方法

// 当前类:org.apache.ibatis.builder.xml.XMLMapperBuilder
private void buildStatementFromContext(List list, String requiredDatabaseId) {
  for (XNode context : list) {
    // 解析工具类
    final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId);
    try {
      // 解析
      statementParser.parseStatementNode();
    } catch (IncompleteElementException e) {
      // 解析发生异常,在org.apache.ibatis.builder.xml.XMLMapperBuilder#parse方法继续处理
      // 可以看之前的XMLMapperBuilder的parse方法解析
      configuration.addIncompleteStatement(statementParser);
    }
  }
}

        进入parseStatementNode方法

// 当前类:org.apache.ibatis.builder.xml.XMLStatementBuilder
public void parseStatementNode() {
  // 得到sql的id
  String id = context.getStringAttribute("id");
  // 数据库标识
  String databaseId = context.getStringAttribute("databaseId");
  if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {
    return;
  }
  // 下面这些就是获取sql标签属性信息
  String nodeName = context.getNode().getNodeName();
  SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENG
  boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
  boolean flushCache = context.getBooleanAttribute("flushCache", !isSelect);
  boolean useCache = context.getBooleanAttribute("useCache", isSelect);
  boolean resultOrdered = context.getBooleanAttribute("resultOrdered", false);
  // Include Fragments before parsing
  XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builder
  includeParser.applyIncludes(context.getNode());
  String parameterType = context.getStringAttribute("parameterType");
  Class parameterTypeClass = resolveClass(parameterType);
  String lang = context.getStringAttribute("lang");
  LanguageDriver langDriver = getLanguageDriver(lang);
  // Parse selectKey after includes and remove them.
  processSelectKeyNodes(id, parameterTypeClass, langDriver);
  // Parse the SQL (pre:  and  were parsed and removed)
  KeyGenerator keyGenerator;
  String keyStatementId = id + SelectKeyGenerator.SELECT_KEY_SUFFIX;
  keyStatementId = builderAssistant.applyCurrentNamespace(keyStatementId, true);
  if (configuration.hasKeyGenerator(keyStatementId)) {
    keyGenerator = configuration.getKeyGenerator(keyStatementId);
  } else {
    keyGenerator = context.getBooleanAttribute("useGeneratedKeys",
        configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandTyp
        ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;
  }
  SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterType
  StatementType statementType = StatementType.valueOf(context.getStringAttribute("statem
  Integer fetchSize = context.getIntAttribute("fetchSize");
  Integer timeout = context.getIntAttribute("timeout");
  String parameterMap = context.getStringAttribute("parameterMap");
  String resultType = context.getStringAttribute("resultType");
  Class resultTypeClass = resolveClass(resultType);
  String resultMap = context.getStringAttribute("resultMap");
  String resultSetType = context.getStringAttribute("resultSetType");
  ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType);
  String keyProperty = context.getStringAttribute("keyProperty");
  String keyColumn = context.getStringAttribute("keyColumn");
  String resultSets = context.getStringAttribute("resultSets");

  // 利用这个方法将xml的sql数据转换为MappedStatement对象,参数是sql的一些基本信息
  builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
      fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
      resultSetTypeEnum, flushCache, useCache, resultOrdered,
      keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
}

        进入addMappedStatement方法

// 当前类:
public MappedStatement addMappedStatement(
    String id,
    SqlSource sqlSource,
    StatementType statementType,
    SqlCommandType sqlCommandType,
    Integer fetchSize,
    Integer timeout,
    String parameterMap,
    Class parameterType,
    String resultMap,
    Class resultType,
    ResultSetType resultSetType,
    boolean flushCache,
    boolean useCache,
    boolean resultOrdered,
    KeyGenerator keyGenerator,
    String keyProperty,
    String keyColumn,
    String databaseId,
    LanguageDriver lang,
    String resultSets) {
  if (unresolvedCacheRef) {
    throw new IncompleteElementException("Cache-ref not yet resolved");
  }
  id = applyCurrentNamespace(id, false);
  boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
  // 利用构建者模式,构建MappedStatement对象
  MappedStatement.Builder statementBuilder = new MappedStatement.Builder(configuration, id, sqlSource, sqlCommandType)
      .resource(resource)
      .fetchSize(fetchSize)
      .timeout(timeout)
      .statementType(statementType)
      .keyGenerator(keyGenerator)
      .keyProperty(keyProperty)
      .keyColumn(keyColumn)
      .databaseId(databaseId)
      .lang(lang)
      .resultOrdered(resultOrdered)
      .resultSets(resultSets)
      .resultMaps(getStatementResultMaps(resultMap, resultType, id))
      .resultSetType(resultSetType)
      .flushCacheRequired(valueOrDefault(flushCache, !isSelect))
      .useCache(valueOrDefault(useCache, isSelect))
      .cache(currentCache);
  ParameterMap statementParameterMap = getStatementParameterMap(parameterMap, parameterType, id);
  if (statementParameterMap != null) {
    statementBuilder.parameterMap(statementParameterMap);
  }
  // 返回一个MappedStatement对象,完成xml的sql转换为MappedStatement的过程
  MappedStatement statement = statementBuilder.build();
  // 保存在Configuration配置类的mappedStatements属性里面
  configuration.addMappedStatement(statement);
  return statement;
}

        到这SqlSessionFactory的源码就解析完了,主要就是把xml的配置信息进行java对象化,生成一个全局的配置对象Configuration,然后保存在DefaultSqlSessionFactory对象里面,然后返回就完了。


四、SqlSession源码解析

        上面介绍了SqlSessionFactory的源码,那么接下来就是根据SqlSessionFactory得到SqlSession对象了。

SqlSession session = sqlSessionFactory.openSession();

        进入openSession方法

// 当前类:org.apache.ibatis.session.defaults.DefaultSqlSessionFactory
public SqlSession openSession() {
    // 参数:默认的执行器,事务管理器,是否自动提交
    // 具体的执行器这些参数,可以查看那个中文文档进行了解
    return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
}

// 当前类:org.apache.ibatis.session.defaults.DefaultSqlSessionFactory
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
  // 事务管理器对象
  Transaction tx = null;
  try {
    // 得到环境变量对象
    final Environment environment = configuration.getEnvironment();
    // 事务管理器工厂对象
    final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
    // 创建一个事务管理器
    tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
    // 创建执行器
    final Executor executor = configuration.newExecutor(tx, execType);
    // 返回一个DefaultSqlSession,包含全局配置文件,执行器,和是否自动提交标志
    return new DefaultSqlSession(configuration, executor, autoCommit);
  } catch (Exception e) {
    closeTransaction(tx); // may have fetched a connection so lets call close()
    throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);
  } finally {
    ErrorContext.instance().reset();
  }
}

        进入newExecutor方法

// 当前类:org.apache.ibatis.session.Configuration
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
  executorType = executorType == null ? defaultExecutorType : executorType;
  executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
  Executor executor;
  // 根据配置的执行器类型,得到一个执行器对象,执行器的知识参考mybaits中文文档
  if (ExecutorType.BATCH == executorType) {
    //批量执行器
    executor = new BatchExecutor(this, transaction);
  } else if (ExecutorType.REUSE == executorType) {
    //复用Statement对象的执行器
    executor = new ReuseExecutor(this, transaction);
  } else {
    //简单执行器
    executor = new SimpleExecutor(this, transaction);
  }
  // 如果cacheEnabled属性为true,对执行器进行包装,cacheEnabled看中文官网的settings属性介绍
  if (cacheEnabled) {
    // 装饰器模式,带有缓存功能的执行器
    executor = new CachingExecutor(executor);
  }
  // 对执行器进行插件处理,就是对它功能进行增强,具体看中文官网的plugins属性介绍
  executor = (Executor) interceptorChain.pluginAll(executor);
  return executor;
}

        进入pluginAll方法,这里对插件的功能介绍之后,在后面对ParameterHandler,ResultSetHandler和StatementHandler 对象进行插件处理时,就不多介绍了,都是一样的逻辑。

// 当前类:org.apache.ibatis.plugin.InterceptorChain
public Object pluginAll(Object target) {
  // interceptors就是实现插件增强的全部实现类的集合,具体在中文官网看plugins插件配置的介绍
  for (Interceptor interceptor : interceptors) {
    // 采用责任链模式进行,层层代理,使得执行器的功能更多
    target = interceptor.plugin(target);
  }
  // 返回代理的执行器对象,赋值给DefaultSqlSession对象
  return target;
}

             插件实现的样例,对官网提供的代码进行改造

package test;

import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;

import java.util.Properties;
import java.util.concurrent.Executor;

@Intercepts({@Signature(
        type= Executor.class,
        method = "update",
        args = {MappedStatement.class,Object.class})})
public class ExamplePlugin implements Interceptor {

    private Properties properties = new Properties();
    public Object intercept(Invocation invocation) throws Throwable {
        // implement pre processing if need
        System.out.println("我是执行器,我要开始工作了。。。。。。");
        Object returnObject = invocation.proceed();
        // implement post processing if need
        System.out.println("我是执行器,我工作结束了,下班。。。。。。");
        return returnObject;
    }

    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    public void setProperties(Properties properties) {
        this.properties = properties;
    }
}

          进入plugin方法,假设目前只有上面这一个执行器的插件

// 当前类:test.ExamplePlugin,就是上面的那个样例类
public Object plugin(Object target) {
    // 进入wrap方法,target执行器对象:CachingExecutor,this插件对象:ExamplePlugin
    return Plugin.wrap(target, this);
}

        进入wrap方法

// 当前类:org.apache.ibatis.plugin.Plugin
// 这个里面的getSignatureMap和getAllInterfaces方法可以多看看很有意思
public static Object wrap(Object target, Interceptor interceptor) {
  // 得到当前插件拦截器的注解信息,取到它拦截的什么类和拦截的什么方法,注解格式    
  // @Intercepts({@Signature(type = Executor.class,method = "query",
  // args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})
})
  Map, Set> signatureMap = getSignatureMap(interceptor);
  Class type = target.getClass();
  // 得到当前对象和其父类,满足插件拦截的所有类
  Class[] interfaces = getAllInterfaces(type, signatureMap);
  // 如果当前对象满足当前插件的拦截
  if (interfaces.length > 0) {
    // 返回一个JDK代理对象
    return Proxy.newProxyInstance(
        type.getClassLoader(),
        interfaces,
        new Plugin(target, interceptor, signatureMap));
  }
  // 如果当前对象不满足,当前插件的拦截条件就返回,让下一个插件继续处理
  return target;
}

        JDK代理的对象,在对象执行方法时,会进入实现了InvocationHandler接口的invoke方法,这是JDK的知识,可以自己去学习。进入Plugin的invoke方法。

// 当前类:org.apache.ibatis.plugin.Plugin
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  try {
    // 得到当前执行方法的类对象,查询该对象满足的所有拦截的方法
    Set methods = signatureMap.get(method.getDeclaringClass());
    // 包括当前执行的方法
    if (methods != null && methods.contains(method)) {
      // 就执行插件的intercept方法,把当前对象,方法和参数传入,intercept假设为ExamplePlugin
      return interceptor.intercept(new Invocation(target, method, args));
    }
    // 没有或者不包括当前执行的方法,就直接执行该方法
    return method.invoke(target, args);
  } catch (Exception e) {
    throw ExceptionUtil.unwrapThrowable(e);
  }

        进入ExamplePlugin的intercept方法

package test;

import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;

import java.util.Properties;
import java.util.concurrent.Executor;

@Intercepts({@Signature(
        type= Executor.class,
        method = "update",
        args = {MappedStatement.class,Object.class})})
public class ExamplePlugin implements Interceptor {
    
    private Properties properties = new Properties();
    // 这个就是我们自定义的插件实现类
    public Object intercept(Invocation invocation) throws Throwable {
        // implement pre processing if need
        System.out.println("我是执行器,我要开始工作了。。。。。。");
        // 等插件增强后,执行目标方法,Invocation的代码在下面
        Object returnObject = invocation.proceed();
        // implement post processing if need
        System.out.println("我是执行器,我工作结束了,下班。。。。。。");
        return returnObject;
    }

    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    public void setProperties(Properties properties) {
        this.properties = properties;
    }
}

package org.apache.ibatis.plugin;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;


public class Invocation {

  private final Object target;
  private final Method method;
  private final Object[] args;

  public Invocation(Object target, Method method, Object[] args) {
    this.target = target;
    this.method = method;
    this.args = args;
  }

  public Object getTarget() {
    return target;
  }

  public Method getMethod() {
    return method;
  }

  public Object[] getArgs() {
    return args;
  }

  public Object proceed() throws InvocationTargetException, IllegalAccessException {
    // 就是执行原来的方法本身
    return method.invoke(target, args);
  }

}

        插件的实现就是JDK动态代理,我认为Mybatis是最开始学习源码的最佳之选,因为它通俗易懂。

五、Mapper源码解析

        得到SqlSession对象之后,我们就可以得到Mapper对象了,因为Mapper是一个接口,所有肯定是一个代理对象,不知道JDK代理的原理是看不懂的。

Mapper mapper = session.getMapper(Mapper.class);
int i = mapper.selectCount("1");

         进入getMapper方法

// 当前类:org.apache.ibatis.session.defaults.DefaultSqlSession
public  T getMapper(Class type) {
    // configuration全局配置对象,就是在生成DefaultSqlSession对象,通过构造方法传入的
    return configuration.getMapper(type, this);
}

// 当前类:org.apache.ibatis.session.Configuration
public  T getMapper(Class type, SqlSession sqlSession) {
    // 根据mapperRegistry得到代理对象
    return mapperRegistry.getMapper(type, sqlSession);
}

        进入getMapper方法

// 当前类:org.apache.ibatis.binding.MapperRegistry
public  T getMapper(Class type, SqlSession sqlSession) {
  // 看之前SqlSessionFactory源码解析的bindMapperForNamespace方法
  final MapperProxyFactory mapperProxyFactory = (MapperProxyFactory) knownMappers.get(type);
  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);
  }
}

        进入newInstance方法

// 当前类:org.apache.ibatis.binding.MapperProxyFactory
public class MapperProxyFactory {

  private final Class mapperInterface;
  private final Map methodCache = new ConcurrentHashMap<>();

  public MapperProxyFactory(Class mapperInterface) {
    this.mapperInterface = mapperInterface;
  }

  public Class getMapperInterface() {
    return mapperInterface;
  }

  public Map getMethodCache() {
    return methodCache;
  }

  @SuppressWarnings("unchecked")
  protected T newInstance(MapperProxy mapperProxy) {
    // 生成的代理对象
    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
  }

  public T newInstance(SqlSession sqlSession) {
     // 生成一个MapperProxy对象,包含SqlSession,Mapper接口对象,ConcurrentHashMap
    final MapperProxy mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
    // this的newInstance方法
    return newInstance(mapperProxy);
  }

}

        既然用JDK代理,我门就要进入MapperProxy的invoke方法了

// 当前类:org.apache.ibatis.binding.MapperProxy
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  try {
    // 如果是Object的方法直接执行
    if (Object.class.equals(method.getDeclaringClass())) {
      return method.invoke(this, args);
    // JDK1.8的新特性 接口中可以定义默认方法
    } else if (isDefaultMethod(method)) {
      // 这里没有深入研究过
      return invokeDefaultMethod(proxy, method, args);
    }
  } catch (Throwable t) {
    throw ExceptionUtil.unwrapThrowable(t);
  }
  // 这个很有意思
  final MapperMethod mapperMethod = cachedMapperMethod(method);
  return mapperMethod.execute(sqlSession, args);
}

        进入cachedMapperMethod

// 当前类:org.apache.ibatis.binding.MapperProxy
private MapperMethod cachedMapperMethod(Method method) {
    // methodCache是一个Map集合
    // 把当前method对象,和包装method的MapperMethod对象保存起来
    return methodCache.computeIfAbsent(method, 
        k -> new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()));
}

// 当前类:org.apache.ibatis.binding.MapperMethod
public MapperMethod(Class mapperInterface, Method method, Configuration config) {
  // 这个方法是根据传入的参数,从MappedStatements中获取一个MappedStatement对象,
  // 包含了sql语句的主要信息,包装为SqlCommand对象
  this.command = new SqlCommand(config, mapperInterface, method);
  // 这个就是将传入的信息,封装在一个内部类里面,Mapper接口和全局配置文件信息
  this.method = new MethodSignature(config, mapperInterface, method);
}

// 当前类:org.apache.ibatis.binding.MapperMethod
public SqlCommand(Configuration configuration, Class mapperInterface, Method method) {
  final String methodName = method.getName();
  final Class declaringClass = method.getDeclaringClass();
  // ms对象,可以看DefaultSqlSessionFactory源码解析
  MappedStatement ms = resolveMappedStatement(mapperInterface, methodName, declaringClass,
      configuration);
  if (ms == null) {
    if (method.getAnnotation(Flush.class) != null) {
      name = null;
      type = SqlCommandType.FLUSH;
    } else {
      throw new BindingException("Invalid bound statement (not found): "
          + mapperInterface.getName() + "." + methodName);
    }
  } else {
    name = ms.getId();
    type = ms.getSqlCommandType();
    if (type == SqlCommandType.UNKNOWN) {
      throw new BindingException("Unknown execution method for: " + name);
    }
  }
}

        进入mapperMethod.execute(sqlSession, args)方法

// 当前类:org.apache.ibatis.binding.MapperMethod
public Object execute(SqlSession sqlSession, Object[] args) {
  Object result;
  // 根据不同的sql类型进行处理
  switch (command.getType()) {
    case INSERT: {
      // 把参数进行处理
      Object param = method.convertArgsToSqlCommandParam(args);
      // result为sql执行的结果,进入insert方法,command.getName()为id,
      // id=namespace+id(sql标签的id),用来从mappedStatements取ms对象
      result = rowCountResult(sqlSession.insert(command.getName(), param));
      break;
    }
    case UPDATE: {
      Object param = method.convertArgsToSqlCommandParam(args);
      result = rowCountResult(sqlSession.update(command.getName(), param));
      break;
    }
    case DELETE: {
      Object param = method.convertArgsToSqlCommandParam(args);
      result = rowCountResult(sqlSession.delete(command.getName(), param));
      break;
    }
    case SELECT:
      if (method.returnsVoid() && method.hasResultHandler()) {
        executeWithResultHandler(sqlSession, args);
        result = null;
      } else if (method.returnsMany()) {
        result = executeForMany(sqlSession, args);
      } else if (method.returnsMap()) {
        result = executeForMap(sqlSession, args);
      } else if (method.returnsCursor()) {
        result = executeForCursor(sqlSession, args);
      } 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;
    case FLUSH:
      result = sqlSession.flushStatements();
      break;
    default:
      throw new BindingException("Unknown execution method for: " + command.getName());
  }
  if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
    throw new BindingException("Mapper method '" + command.getName()
        + " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
  }
  return result;
}
private Object rowCountResult(int rowCount) {
  final Object result;
  if (method.returnsVoid()) {
    result = null;
  } else if (Integer.class.equals(method.getReturnType()) || Integer.TYPE.equals(method.getReturnType())) {
    result = rowCount;
  } else if (Long.class.equals(method.getReturnType()) || Long.TYPE.equals(method.getReturnType())) {
    result = (long)rowCount;
  } else if (Boolean.class.equals(method.getReturnType()) || Boolean.TYPE.equals(method.getReturnType())) {
    result = rowCount > 0;
  } else {
    throw new BindingException("Mapper method '" + command.getName() + "' has an unsupported return type: " + method.getReturnType());
  }
  return result;
}

        进入insert方法

// 当前类:org.apache.ibatis.session.defaults.DefaultSqlSession
public int insert(String statement, Object parameter) {
  // statement为id,parameter为参数
  return update(statement, parameter);
}

        进入update方法

// 当前类:org.apache.ibatis.session.defaults.DefaultSqlSession
public int update(String statement, Object parameter) {
  try {
    dirty = true;
    // 取ms对象,很简单的代码,不看了
    MappedStatement ms = configuration.getMappedStatement(statement);
    // 执行器executor,一般为CachingExecutor而且被插件增强的执行器代理对象
    return executor.update(ms, wrapCollection(parameter));
  } catch (Exception e) {
    throw ExceptionFactory.wrapException("Error updating database.  Cause: " + e, e);
  } finally {
    ErrorContext.instance().reset();
  }
}

        进入执行器的update方法

// 当前类:org.apache.ibatis.executor.CachingExecutor
public int update(MappedStatement ms, Object parameterObject) throws SQLException {
  // 修改操作,要删除二级缓存
  flushCacheIfRequired(ms);
  return delegate.update(ms, parameterObject);
}

        进入delegate.update(ms, parameterObject)方法

// 当前类:org.apache.ibatis.executor.baseExecutor
public int update(MappedStatement ms, Object parameter) throws SQLException {
  ErrorContext.instance().resource(ms.getResource()).activity("executing an update").object(ms.getId());
  if (closed) {
    throw new ExecutorException("Executor was closed.");
  }
  // 清除一级缓存,为什么是一级,因为我们现在讲解的insert语句,可以看select语句的执行就知道了
  clearLocalCache();
  return doUpdate(ms, parameter);
}

        进入doUpdate方法

// 当前类:org.apache.ibatis.executor.SimpleExecutor
public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
  Statement stmt = null;
  try {
    Configuration configuration = ms.getConfiguration();
    //返回一个StatementHandler对象,工具类
    StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);
    //返回Statement对象,很简单
    stmt = prepareStatement(handler, ms.getStatementLog());
    // 插入数据
    return handler.update(stmt);
  } finally {
    closeStatement(stmt);
  }
}

// 当前类:org.apache.ibatis.executor.statement.PreparedStatementHandler
public int update(Statement statement) throws SQLException {
  PreparedStatement ps = (PreparedStatement) statement;
  // 接下来就是jdbc的代码了
  ps.execute();
  int rows = ps.getUpdateCount();
  Object parameterObject = boundSql.getParameterObject();
  KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();
  keyGenerator.processAfter(executor, mappedStatement, ps, parameterObject);
  return rows;
}

        进入newStatementHandler方法

// 当前类:org.apache.ibatis.session.Configuration
public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql)
  // 返回StatementHandler,构造方法如下,根据不同策略生成StatementHandler对象
  StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
  // 这里就是对statementHandler进行插件的增强,和Executor哪里一模一样
  statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
  return statementHandler;
}

// 当前类:org.apache.ibatis.executor.statement.RoutingStatementHandler
public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {

  switch (ms.getStatementType()) {
    case STATEMENT:
      delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
      break;
    case PREPARED:
      // 假如是这个对象
      delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
      break;
    case CALLABLE:
      delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
      break;
    default:
      throw new ExecutorException("Unknown statement type: " + ms.getStatementType());
  }
}

        进入PreparedStatementHandler的构造方法

// 当前类:org.apache.ibatis.executor.statement.PreparedStatementHandler
public PreparedStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
    super(executor, mappedStatement, parameter, rowBounds, resultHandler, boundSql);
}

        进入父类的构造方法

// 当前类:org.apache.ibatis.executor.statement.baseStatementHandler
protected baseStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
  this.configuration = mappedStatement.getConfiguration();
  this.executor = executor;
  this.mappedStatement = mappedStatement;
  this.rowBounds = rowBounds;

  this.typeHandlerRegistry = configuration.getTypeHandlerRegistry();
  this.objectFactory = configuration.getObjectFactory();
  if (boundSql == null) { // issue #435, get the key before calculating the statement
    generateKeys(parameterObject);
    boundSql = mappedStatement.getBoundSql(parameterObject);
  }
  this.boundSql = boundSql;
  // 这里生成ParameterHandler的代理对象,插件进行增强的
  this.parameterHandler = configuration.newParameterHandler(mappedStatement, parameterObject, boundSql);
  // 这里生成ResultSetHandler的代理对象,插件进行增强的
  this.resultSetHandler = configuration.newResultSetHandler(executor, mappedStatement, rowBounds, parameterHandler, resultHandler, boundSql);
}

        进入newParameterHandler方法

// 当前类:org.apache.ibatis.session.Configuration
public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
  ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement, parameterObject, boundSql);
  // 和其他插件的处理一模一样
  parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler);
  return parameterHandler;
}

        到这里我们就看到了一个sql执行的全过程,也看到了插件的处理原理,下面是官网原话。

MyBatis 允许你在映射语句执行过程中的某一点进行拦截调用。默认情况下,MyBatis 允许使用插件来拦截的方法调用包括:

Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)ParameterHandler (getParameterObject, setParameters)ResultSetHandler (handleResultSets, handleOutputParameters)StatementHandler (prepare, parameterize, batch, update, query)

总结

        Mybaits的源码就分析完了,相对于Spring来说,它的源码很通俗易懂,特别适合准备开始研究源码的人阅读。祝愿大家能在源码中学到更多的知识和思想。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/773405.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号