mybatis源码解析 - mapper代理对象的生成
Posted codingjav
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了mybatis源码解析 - mapper代理对象的生成相关的知识,希望对你有一定的参考价值。
1、简单示例
先看一个简单纯粹的mybatis demo(不集成spring等其他框架),代码结构很简单,如下图:
完整代码地址:kingoe/boot-study;mapper层和我们平时说的dao层指的是同一个内容,都是数据库操作的封装,但是在没有集成mybatis时,dao层的接口都是需要我们手动去写其实现类,可在上图中我们却发现:我们并没有手动去实现PersonMapper接口,但工程却能实实在在的查询数据库,获取我们需要的数据,debug代码可以发现 PersonMapper实例是一个代理对象,我们操作的其实是PersonMapper的代理实现;也就是说不用我们手动去实现PersonMapper接口,mybatis会动态生成PersonMapper的代理实例【MapperProxy$xxx类】,然后由代理实例完成数据库的操作。那么问题来了,mybatis是何时、何地、如何生成mapper代理实例的呢?我们接着往下看
2、源码分析
针对上述问题,从mybatis源码入手:
2.1、SqlSessionFactory创建
SqlSessionFactoryBuilder 通过指定 mybatis-config.xml 配置文件构建SqlSessionfactory,内部通过XMLConfigBuilder解析Mybatis配置文件(mybatis-config.xml),将配置文件中各个属性解析到Configuration实例中,然后以Configuration实例构建SqlSessionFactory(实际是DefaultSqlSessionFactory);
class SqlSessionFactoryBuilder
public SqlSessionFactory build(InputStream inputStream)
return build(inputStream, null, null);
........
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties)
try
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
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.
public SqlSessionFactory build(Configuration config)
return new DefaultSqlSessionFactory(config);
XMLConfigBuilder 内部通过 parse() 方法解析配置文件各个参数,其中parseConfiguration方法是解析的具体过程。
/**
* 配置文件只能解析一次,多次解析会抛出异常
*/
public Configuration parse()
if (parsed)
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
parsed = true;
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
/**
* root是以configuration标签开始的文档树
* 解析配置文件中的各个标签,并存放到Configuration实例对应的属性中
* 解析完成之后,配置文件中的内容全部解析到了Configuration实例中
* @param root
*/
private void parseConfiguration(XNode root)
try
//issue #117 read properties first
propertiesElement(root.evalNode("properties")); // 解析配置文件中的properties标签
Properties settings = settingsAsProperties(root.evalNode("settings")); // 解析配置文件中的settings标签
loadCustomVfs(settings);
typeAliasesElement(root.evalNode("typeAliases")); // 解析配置文件中的typeAliases标签
pluginElement(root.evalNode("plugins")); // 解析配置文件中的plugins标签
objectFactoryElement(root.evalNode("objectFactory")); // 解析配置文件中的objectFactory标签
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory")); // 解析配置文件中的objectWrapperFactory标签
reflectorFactoryElement(root.evalNode("reflectorFactory")); // 解析配置文件中的reflectorFactory标签
settingsElement(settings);
// read it after objectFactory and objectWrapperFactory issue #631
environmentsElement(root.evalNode("environments")); // 解析配置文件中的environments标签
databaseIdProviderElement(root.evalNode("databaseIdProvider")); // 解析配置文件中的databaseIdProvider标签
typeHandlerElement(root.evalNode("typeHandlers")); // 解析配置文件中的typeHandlers标签
mapperElement(root.evalNode("mappers")); // 解析配置文件中的mappers标签
catch (Exception e)
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
mapperElement(root.evalNode("mappers")); 中核心方法
public void parse()
if (!configuration.isResourceLoaded(resource))
configurationElement(parser.evalNode("/mapper")); // 解析映射文件Person.xml
configuration.addLoadedResource(resource);
bindMapperForNamespace(); // 将mapper与namespace绑定起来; 将PersonMapper接口与MapperProxyFactory关联起来
parsePendingResultMaps(); // 解析Configuration的incompleteResultMaps到Configuration的resultMaps
parsePendingCacheRefs(); // 解析Configuration的incompleteCacheRefs到Configuration的cacheRefMap
parsePendingStatements(); // 解析Configuration的incompleteStatements到Configuration的mappedStatements
/**
* context是映射文件:Person.xml的文档树,以mapper标签开始
* 解析映射文件中的各个标签,并存放到MapperBuilderAssistant实例对应的属性中
*/
private void configurationElement(XNode context)
try
String namespace = context.getStringAttribute("namespace"); // 解析mapper标签的namespace属性
if (namespace == null || namespace.equals(""))
throw new BuilderException("Mapper's namespace cannot be empty");
builderAssistant.setCurrentNamespace(namespace); // namespace属性值解析到Configuration的mapperRegistry中
cacheRefElement(context.evalNode("cache-ref")); // 解析cache-ref标签到Configuration的cacheRefMap中
cacheElement(context.evalNode("cache")); // 解析cache标签到Configuration的caches中
parameterMapElement(context.evalNodes("/mapper/parameterMap")); // 解析parameterMap标签到Configuration的parameterMaps中
resultMapElements(context.evalNodes("/mapper/resultMap")); // 解析resultMap标签到Configuration的resultMaps中
sqlElement(context.evalNodes("/mapper/sql")); // 解析sql标签到XMLMapperBuilder的sqlFragments中
buildStatementFromContext(context.evalNodes("select|insert|update|delete")); // 解析select|insert|update|delete标签到Configuration的mappedStatements中
catch (Exception e)
throw new BuilderException("Error parsing Mapper XML. The XML location is '" + resource + "'. Cause: " + e, e);
此时SqlSessionFactory已经创建,但PersonMapper的代理实例还没有创建;期间准备了很多东西,包括读取配置文件和映射文件的内容,并将其放置到Configuration实例的对应属性中
2.2、SqlSession创建
上述步骤一创建SqlSessionFactory后,通过 openSession() 方法获取 SqlSession对象,就可以进行数据库操作。
@Override
public SqlSession openSession()
return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
........
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);
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();
该步骤实例化了Transaction(JdbcTransaction)、Executor(SimpleExecutor)和SqlSession(DefaultSqlSession),此时mapper代理实例仍未被创建。
2.3、Mapper代理对象的创建
DefaultSqlSession.class
@Override
public <T> T getMapper(Class<T> type)
return configuration.<T>getMapper(type, this); // 类名全路径
Configuration.class
public <T> T getMapper(Class<T> type, SqlSession sqlSession)
// mapperRegistry
return mapperRegistry.getMapper(type, sqlSession); //类名全路径
MapperRegistry.class
public <T> T getMapper(Class<T> type, SqlSession sqlSession)
// knownMappers map集合,存放的 key 是上述步骤解析配置文件放进去的 mapper路径名,value 是统一的MapperProxyFactory,
//【knownMappers.put(type, new MapperProxyFactory<T>(type));】
final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
if (mapperProxyFactory == null)
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
try
return mapperProxyFactory.newInstance(sqlSession); // MapperProxyFactory 工厂类创建代理对象
catch (Exception e)
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
MapperProxyFactory.class
protected T newInstance(MapperProxy<T> mapperProxy)
// 利用JDK的动态代理生成mapper的代理实例
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] mapperInterface , mapperProxy);
public T newInstance(SqlSession sqlSession)
final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
生成了mapper的代理实例,后续就可以利用此代理实例进行数据库的操作了。后续通过代理类(MapperProxy)执行的方法都会走其 invoke()方法,最终都会进到 MapperMethod 类中执行对应类型的方法。【select|insert|update|delete|flush】
3、总结
1、我们用mytabis操作数据库,有一个固定流程:先创建SqlSessionFactory,然后创建SqlSession,然后再创建获取mapper代理对象,最后利用mapper代理对象完成数据库的操作;一次数据库操作完成后需要关闭SqlSession;
2、创建SqlSessionFactory实例的过程中,解析mybatis配置文件和映射文件,将内容都存放到Configuration实例的对应属性中;创建SqlSession的过程中,有创建事务Transaction、执行器Executor,以及DefaultSqlSession;Mapper代理对象的创建,利用的是JDK的动态代理,InvocationHandler是MapperProxy,后续Mapper代理对象方法的执行都会先经过MapperProxy的invoke方法;
3、很多细节没有讲到,但大体流程就是这样;另外提下,实际应用中,mybatis往往不会单独使用,绝大多数都是集成在spring中;关于在spring的集成下,mapper代理对象的创建过程查看:springboot集成下,mybatis的mapper代理对象究竟是如何生成的。
以上是关于mybatis源码解析 - mapper代理对象的生成的主要内容,如果未能解决你的问题,请参考以下文章