【Mybatis源码分析 3.5.1版本】
目录
前言
一、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() {// 默认时falseif (parsed) {// 全局配置文件不可以重复解析throw new BuilderException("Each XMLConfigBuilder can only be used once.");}// 改为trueparsed = true;// 解析入口,parser.evalNode("/configuration")返回XNode对象,xml文件解析成的java beanparseConfiguration(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,SLF4JloadCustomLogImpl(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标签如
进入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#loadXmlResourceconfiguration.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.MapperString 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())}// 没有设置为nullbuildStatementFromContext(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的idString 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.ENGboolean 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 parsingXMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderincludeParser.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, parameterTypeStatementType statementType = StatementType.valueOf(context.getStringAttribute("statemInteger 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 needSystem.out.println("我是执行器,我要开始工作了。。。。。。");Object returnObject = invocation.proceed();// implement post processing if needSystem.out.println("我是执行器,我工作结束了,下班。。。。。。");return returnObject;}@Overridepublic 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插件对象:ExamplePluginreturn 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假设为ExamplePluginreturn 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 needSystem.out.println("我是执行器,我要开始工作了。。。。。。");// 等插件增强后,执行目标方法,Invocation的代码在下面Object returnObject = invocation.proceed();// implement post processing if needSystem.out.println("我是执行器,我工作结束了,下班。。。。。。");return returnObject;}@Overridepublic 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;/*** @author Clinton Begin*/
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接口对象,ConcurrentHashMapfinal 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 statementgenerateKeys(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来说,它的源码很通俗易懂,特别适合准备开始研究源码的人阅读。祝愿大家能在源码中学到更多的知识和思想。
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!


